Rethrowing exceptions under different names? What's the standard practice? - php

currently I have this client code in my PHP MVC web app:
try {
BookMapper::insert($book);
} catch (DbUniqueConstraintViolationException $e) {
$errorList->addMessage($book . " already exists!");
}
I'm wondering is it bad practice to refer to the low-level framework Db* exceptions to my client code? If so, should I adjust my model code like so:
class BookAlreadyExistsException extends Exception { }
class BookMapper {
public static function insert($book) {
try {
// call to DB-layer to insert $book
// (not relevant to the question)
} catch (DbUniqueConstraintViolationException $e) {
throw new BookAlreadyExistsException();
}
}
}
and then use this new client-code...
try {
BookMapper::insert($book);
} catch (BookAlreadyExistsException $e) {
$errorList->addMessage($book . " already exists!");
}
Or something else? Or is the original method fine?
Thanks!
EDIT: Just want to add, the latter method reads the best IMO, but it comes with the object creation / rethrowing overhead and more significantly, it requires duplicating the rethrowing code in every mapper's insert() method. The former method is easy to implement and catch and works for any model but I remember reading somewhere that you shouldn't do it this way?

I think you should definitly throw your own exception.
But I would also consider a third option and that is letting the insert method return true for success and false for failure. Exceptions should be used for exceptions and the fact that a book already exists might actually be an expected/predictable case.
And if duplicate books are truly excetions that should not be possible (unless for programming errors), then you could as well stick with the database exception but in that case don't catch it. Let it bubble all the way up.

I highly recommend this article. Although it is written for Java, the principles are quite applicable to PHP as well. It has good guidelines for what types of exceptions you should be throwing and catching.

Related

PHP project structure

I have a project I created using structural programming that I want to refactor as a object oriented project in the most "bestpractices" way. I will probably be the only one using this project, it's not actually meant for others. But I might show it to others as example of excellence ;)
My questions are pretty general, but I hope this is OK.
I'm thinking about having it split in three ways; backend (the main class), frontend (get and posts check, call class functionality), visual (using Twig templating).
My project will be using an external intgration for IPS forum software (the user sessions will be kept there).
See below for my code idea how to structure this.
My questions:
Is my general structure ok with the class separated from "frontend" like this?
Is my idea of having the member lookup/handling from IPS outside of class ok, as I later can switch to some other member functionality in frontend without messing with backend? Just put the member object into class from wherever, making sure what class use is always set at least.
Should I send the member data as parameter to class (construct), or keep it like now and set a public class var from frontend?
Should my class throw exceptions on errors or return true/false and setting an error message?
Should the frontend also be a class? Extend the main class?
Setting error messages in __construct like this is ok, or should that be done somewhere else?
Should the MyProject class be split into multiple classes? The current project in structural code is 10000 lines, the new class may be about half since I'm taking out a lot of visual rendering stuff. Maybe classes for MyProjectDisplayData and MyProjectCreateData and such?
If answer to 7 is yes, should I have one core class for messages, db and general functionality, which the other specific classes "extends"?
Is there something else one might want to do different?
myproject_class.php:
namespace MySpace;
use \PDO;
use \PDOException;
use \Exception;
class MyProject {
public $projectdata;
public $errormessages;
public $ips_member;
function __construct () {
//set up vars for error messages
$this->errormessages["database_queryfailed"] = "Query failed";
$this->errormessages["general_missingdata"] = "Missing data";
$this->errormessages["handling_something"] = "Some error";
}
public function displaySomeData ( $id ) {
if ($id == ""){
throw new Exception($this->$errormessages["general_missingdata"]);
}
try{
$sql = "GET SOME DATA FROM DB";
//PDO execute
}catch (PDOException $e) {
throw new Exception($this->$errormessages["database_queryfailed"] . " SQL: " . $sql);
}
$this->projectdata = array();
$this->projectdata["one"] = "cool";
$this->projectdata["two"] = "verycool";
if ($someerror){
throw new Exception($this->$errormessages["handling_something"]);
}
}
public function createSomeData(){
try{
$sql = "INSERT SOME DATA IN DB";
//PDO execute
}catch (PDOException $e) {
throw new Exception($this->$errormessages["database_queryfailed"] . " SQL: " . $sql);
}
}
}
Frontend index.php:
require_once 'vendor/autoload.php';
require_once 'myproject_class.php';
require 'forum/init.php';
//forum initialize
\IPS\Session\Front::i();
$ips_member = \IPS\Member::loggedIn();
//load class
try {
$myproj = new MySpace\MyProject();
$myproj->ips_member = $ips_member;
} catch (Exception $e) {
die($e->getMessage()); //not die, but handle in some way
}
//check get or post var to decide what to do
if ($_GET["dowhat"] == "display"){
try {
$myproj->displaySomeData($_GET["id"]);
} catch (Exception $e) {
die($e->getMessage()); //not die, but handle in some way
}
}
//twig rendering
$loader = new Twig_Loader_Filesystem('template');
$twig = new Twig_Environment($loader);
$template = $twig->load('myproject.html');
echo $template->render(array('projectdata' => $myproj->projectdata, 'member' => $ips_member));
Thank you for your help!
If your codebase is about 10k lines, there is no way you can stuff that in two or three classes (well, apparently you can, but it's a terrible idea).
First of all, you should extract your HTML in templates. Twig is a nice choice and should serve you well. But next step would probably be introduction of routing logic, that would let you automate the choosing of which template to render.
Regarding your general understanding of OOP, I would recommend you watch this and this lecture. Because I am getting a feeling, that you do not really understand OOP paradigm as a whole.
And don't abuse extends keywords. There is this old quote in OOP: "You should favour composition over inheritance". And that sums it up quite well.
Regarding error handling, I wrote about it just few days ago, so I will just be lazy and direct you to an older post, that covered briefly the common approaches and touched upon some of the drawback in each.
And finally, for dealing with DB: each class, that requires access to DB, should have an instance of PDO (or MySQLi) be passed in it's constructor. If you have more than one such class, reading this post might help with sharing that connection instance.

Easy way to list all of exceptions "try" block may produce

Is there any easy way to find out, which types of exception I can expect in try block? Let's say I have:
<?php
try {
foo();
} catch (\A\B\FooException $e) {
} catch (\A\B\BarException $e) {
}
Is there any tool which can inspect the foo() code for me and list all of exceptions types I can expect there? So if there is \A\B\BazException I forgot, I can easily add another catch thanks to that list. For now I use search for "Exception", but sometimes there is to many the same results.
I don´t know any tools to do that. But you can create at least the code block below:
catch (Exception $e) {
mysql_query("INSERT INTO exceptions_tb ('exception_name','expection_message') VALUES ('"+$e->class+"', '"+addslashes($e->message)+"') excetption ", $conn);
}
And after consult the variations of exceptions happened in all your unit tests. Or simply you can do all possible ways and input data types in your unit tests and see what exceptions will arise.
I don´t believe there is a tool that can do this for you also because the tool will need to now all possible inputs and php variables are not predefined types.
...
catch (Exception $oError) {
Logger::logError($oError);
}
class Logger
{
public static function($oError){
// do here detection you like\want
// you can use loops and pre-def. instances of errors
if ($oError instanceof ExceptionCustom)
// some actions
}
...
}
}
class ExceptionCustom extends Exception {
// http://www.php.net/manual/en/language.exceptions.extending.php
//...
}
It's only sample, base thing. You can\should do it more complicated the way you want.
ADDED:
Main thing that you will decide, what error and what you should do, only after you receive exception, and of course you should create default action for it (no matter what exception is thrown).

Cleaning up code for a custom exception class in PHP

I'm playing around with custom PHP exceptions for the first time, and would like some help with cleaning up some code. In particular, I'm catching PDO errors, and have written a class to mail myself a stack trace of the errors. The way that I'm currently doing things is as follows:
try {
//db stuff
} catch (PDOException $e) {
throw new My_Own_Exception($e);
exit;
}
where my My_Own_Exception does the job with:
class My_Own_Exception extends Exception
{
/*code to mail myself error info: this part works!
}
While the above works, I feel like I should be able to just write something cleaner such as:
try {
} catch (My_Own_Exception $e) {
exit;
}
where My_Own_Exception is an extension of the PDOException class. A few questions about this: First, is the second way the better approach? Second, if so, is it possible? Third, if it is possible, how do I let PHP know that My_Own_Exception "exists" if My_Own_Exception is never instantiated anywhere? Hopefully the third question makes some sense: my gut tells me that if I can make that happen, then my approach should be possible.
I don't think that an exception is the correct place for logic, it should contain information about the error. A PDOException is useful because you know it originates from your PDO code, if you throw a MyException instead, you need to at least give more (useful) information.
This being set, you should read BiVOC's comment on your original question.
If you have a custom exception handler, you can then differentiate via instanceof.
function exception_handler($exception) {
if ($exception instanceof PDOException) {
//mail
}
//always log/etc.
}
What you are trying to do wont work, because nothing in the PDO code will throw your exception, unfortunately. So you are going to have to throw it yourself, like you were in the first example.
Also, the exit in the first example will never be hit.

Is there a way to catch an Exception without having to create a variable?

In PHP, I sometimes catch some exceptions with try/catch :
try {
...
} catch (Exception $e) {
// Nothing, this is a test that an exception is thrown.
}
With that kind of code, I end up with the variable $e that is created for nothing (lots of resources), and PHP_MD (PHP Mess Detector) creates a warning because of an unused variable.
Starting with PHP 8, it is possible to use a non-capturing catch.
This is the relevant RFC, which was voted favourably 48-1.
Now it will be possible to do something like this:
try {
readFile($file);
} catch (FileDoesNotExist) {
echo "File does not exist";
} catch (UnauthorizedAccess) {
echo "User does not have the appropriate permissions to access the file";
log("User attempted to access $file");
}
With this, for some edge cases where the exception details are not relevant and exception type already provides all the necessary context, it will be possible to catch the exception without creating a new variable.
You can with PHP 8 #see
PHP 5,7
No, but you can unset it.
try {
...
} catch (Exception $e) {
// Nothing, this is normal
unset($e);
}
If it is PHPMD causing this issue then you can suppress the warning.
PHPMD suppress-warnings
class Bar {
/**
* This will suppress UnusedLocalVariable
* warnings in this method
*
* #SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function foo() {
try {
...
} catch (Exception $e) {
// Nothing, this is normal
unset($e);
}
}
}
I'm assuming you are only catching the exception because you need to not because you want to.
With PHP 5,7 you have to use a catch if you want to use try and if you use a catch you have to declare a variable.
That's the whole point of exceptions - you can have multiple different catch blocks to catch any exceptions you'd want to handle. The exception's data has to be assigned somewhere, hence the variable. You could just do something like unset($e) inside the catch block if you really don't want to see those warnings... or disable the warnings (generally a bad idea).
I disagree fundamentally with Marc B's and Artefacto's answers. There are cases where ommitting the catch is better or even the only option. Especially when using external libraries (where you have no control over what exceptions are thrown) and/or async operations.
For example:
I want to create a file only if it doesn't exist yet. I'm using an external I/O library. Imagine it has File::exists($fileName) and File::create($fileName) methods.
Option 1 (if ommitting the catch was possible):
try {
File::create($fileName);
}
// Go on with the rest of the code.
Option 2 (without try/catch):
if (!File::exists($fileName))
File::create($fileName);
Here, option 1 is perfectly valid, since option 2 has two important issues:
If multiple threads are running and going through this code section at the same time, it could be that thread A first checks if the file exists. Next, thread B checks if the file exists. They both find that it doesn't exist. Thread A creates the file. Thread B then attempts to create it again and throws an exception even though you're using the if check.
It's very likely that the library itself already performs the !File::exists($fileName) check. Therefore you're wasting a call that is already made.
Note that if File::create throws other exceptions that might be unexpected it would be good to catch those.
Conclusion
Stating that something is never a good idea, is almost never a good idea. There are always exceptions (hehe) to the rule. Like any convention or design pattern, it's just a rule of thumb meant to help less experienced developers make the right decision.
No.
In any case, it's generally a bad idea to catch an exception and do nothing; exceptions exist precisely to force you to handle the exceptional circumstance (otherwise execution is aborted), so it's comprehensible the language doesn't facilitate such a use case.
As of PHP 8.0 it may be typed without variables, but the general case for each Exception is now Throwable. Class Exception implements Throwable.
try {
...
} catch (CustomException) {
// CustomException
} catch (Throwable) {
//All other classes implementing Throwable interface
}

php symfony exception handling/error handling

Working on a symfony application that uses nusoap (is this the best method for integrating soap work with php/symfony?) for taking credit card payments.
I've simplified an example of my code below.
What I'm struggling with is the best way to handle exceptions. The example below only has 1 custom exception (where should my custom exceptions reside within the directory structure of symfony? (lib/exception?)) But what happens when there are several different types of exceptions that handle a specific error? It's not very elegant to have a try/catch block with 20 odd exceptions.
I'm also not sure of where I should be throwing and catching. I need to set some user flashes to alert the user of any problems, so I figure the catching should be done in the actions controller rather than within the class that handles the soap call.
Could anyone please advise where I might be going wrong?
I hate messy code/solutions and want to stick to the DRY principle as much as possible. I think I might also be missing some built in symfony functionality that might help with this but whenever I search I usually find examples that are for symfony 1.2, I'm using 1.4.
Some examples would be great, thanks.
lib/soap_payment.class.php
class SoapPayment
{
public function charge()
{
/*assume options are setup correctly for sake of example*/
try
{
$this->call();
}
catch (SoapPaymentClientFaultException $e)
{
/* should this be caught here? */
}
}
private function call()
{
$this->client->call($this->options);
if ($this->client->hasFault())
{
throw new SoapPaymentClientFaultException();
}
}
}
apps/frontend/payment/actions/actions.class.php
class paymentActions extends sfActions
{
public function executeCreate(sfWebRequest $request)
{
/* check form is valid etc */
$soap_payment = new SoapPayment();
try
{
$soap_payment->charge();
}
catch (SoapPaymentClientFaultException $e)
{
/* or throw/catch here? */
$this->getUser()->setFlash('error', ...);
$this->getLogger()->err(...);
}
/* save form regardless, will set a flag to check if successful or not in try/catch block */
}
}
One not very well known feature of Symfony is that exceptions can manage the content sent in a response. So you could do something like this:
class SoapException extends sfException
{
public function printStackTrace() //called by sfFrontWebController when an sfException is thrown
{
$response = sfContext::getInstance()->getResponse();
if (null === $response)
{
$response = new sfWebResponse(sfContext::getInstance()->getEventDispatcher());
sfContext::getInstance()->setResponse($response);
}
$response->setStatusCode(5xx);
$response->setContent('oh noes'); //probably you want a whole template here that prints the message that was a part of the SoapException
}
}
If you need a cleaner handling of SOAP exceptions, like setting flashes, etc. you'll probably have to catch each exception. One idea here might be to create a generic SoapException class that is extended by more specific SoapExceptions so you don't have to catch a bunch of different types. The above code may be a useful fallback mechanism as well.
Finally, yes, you should place custom exceptions in lib/exception.

Categories