Using a code for an exception. Useful? - php

I am not sure if Exceptions work the same way in each language, but I am using PHP and I was wondering when I'm doing something like this:
if (!$this->connection[0]->query($this->query))
throw new QueryFailedException($this->connection[0]->error);
Is there a need to supply a code in the second parameter? For example:
if (!$this->connection[0]->query($this->query))
throw new QueryFailedException($this->connection[0]->error,123);
Now the code is 123... I can't think of a need for this. Is there one? In this case the message contains the query, exception name is QueryFailedException which explains the exception type, the exception itself contains file, line and stack trace, so, I can't think of anything where you could use the code for something useful.

The error code was a feature used when there was no object oriented language. The only thing that could aid you to understand what went wrong was the error code. In an object oriented language, the object IS your error code.
Unless, in specific cases, more than one thing can throw the exact same error AND they are treated in different ways, drop it.
Also, you would provide much better explanation to whomever is debugging your code if you left a message instead of a meaningless error code, so if you feel like the exception needs more information, fill the Error Message field instead.

The error code is a field that can be used to provide more detailed information. If for example you have two things that can generate the same exception, the code could be used to give more detail.

If you have an "error source" that works on error codes and you "promote" it to exceptions you can include the actual error code in the exception. a) it does no harm and b) maybe you do not want to have an exception class for each single error code that may or may not occur (and virtually no one cares for in a running system).
Let's take the MySQL server errors as an example. You could create one class for each of those codes
class MySQLException_ER_HASHCHK extends MySQLException
class MySQLException_ER_NISAMCHK extends MySQLException
class MySQLException_ER_NO extends MySQLException
class MySQLException_ER_YES extends MySQLException
class MySQLException_ER_CANT_CREATE_FILE extends MySQLException
class MySQLException_ER_CANT_CREATE_TABLE extends MySQLException
class MySQLException_ER_CANT_CREATE_DB extends MySQLException
class MySQLException_ER_DB_CREATE_EXISTS extends MySQLException
class MySQLException_ER_DB_DROP_EXISTS extends MySQLException
....
but in reality ...who cares? Who's really gonna catch them individually? In almost all cases there will only be a catch(MySQLException $mex) in the app's code and maybe, just maybe it's looking for one specific code where it makes little to no difference for the coder whether there are two catch-blocks or an if/switch block. Now you have a lot of "dead" classes and no one -except the parser- gives a damn about them. (on the other hand "everything worth doing is worth overdoing it")
And even if you do provide some granularity I think it makes little sense to go beyond e.g. having one exception class for each SQLState (does that make sense? sqlstate? don't know, just an example)
class MySQLException_HY000 extends MySQLException
class MySQLException_HY001 extends MySQLException
class MySQLException_XA100 extends MySQLException
class MySQLException_XA102 extends MySQLException
And then again you probably want to include the error code - why lose this information even though/even if your code usually doesn't evaluate it?

If you can, it is very good to set in an exception code.
That is if you don't change your code to throw different exceptions based on the data you get from your database.
The error code, in OOP is the Exception Class Name itself, so that you can interpret each of them in just one try but with multiple catch clauses.
try {
// code here
} catch (AccessDeniedException $e) {
// do something
} catch (Exception $e) {
// do something else
}

Related

Is it safe to use PhpStorm hint ArrayShape? [duplicate]

A basic use case would be calling MyEventListener::class without having imported use MyNamespace\MyEventListener. The result would be a broken piece of code that's relatively hard to debug.
Does PHP 7 provide a directive to crash instead of returning the class name if no class exists? For example:
After calling use Foo\Bar;, Bar::class would return 'Foo\Bar'.
But if no import statement, PHP returns 'Bar', even though the class doesn't exist, not even in the global namespace.
Can I make it crash somehow?
The thing you need to keep in mind is that use Foo\Bar; is not "importing" anything. It is telling the compiler: when I say "Bar" I mean Bar from the namespace Foo.
Bar::class is substituted blindly with the string "Foo\Bar". It isn't checking anything.
Until you attempt to instantiate or interact with a class it will not check to see if it exists. That said, it does not throw an Exception, it throws an Error:
// this doesn't exist!
use Foo/Bar;
try {
$instanceOfBar = new Bar();
}
catch (Error $e) {
// catching an Exception will not work
// Throwable or Error will work
}
You can trap and check for non-existent classes at run time, but until you do it will happily toss around strings referring to classes that don't exist.
This is a blessing in the case of Laravel's IoC container and autoloader that abuses this to alias classes as convenient top-level objects. A curse, if you were expecting PHP to throw a fuss on ::class not existing.
Update:
My suggestion for anyone worried about this problem is to use PHPStan in your testing pipeline. It prevents a lot of mistakes, and unlike php -l it will catch if you were to try and interact with a non-existent class.
As far as I know you're going to get a nice error message when you try to instantiate a class that cannot be found through autoloading or explicitly added.
If you want to check if the class exists, first, try this:
$classOutsideNamespaceExists = class_exists('Bar');
$classInsideNameSpaceExists = class_exists('\\Foo\\Bar'));
Or you could try this syntax available since PHP 5.5:
class_exists(MyClass::class)
Finally, you can always use the tried and true method of a try-catch block.
try {
$instanceOfMyClass = new MyClass();
}
catch (Exception $e) {
// conclude the class does not exist and handle accordingly
}
PhpStorm proposes and generates hints like ArrayShape, Pure, etc.
But automatically it is adding
php use JetBrains\PhpStorm\ArrayShape;
or another.
Is not that dangerous that on some production server I will get error
'Class JetBrains\PhpStorm\ArrayShape not found'?
(c)LazyOne:
Well, just use composer require --dev jetbrains/phpstorm-attributes to add such classes to your project. See github.com/JetBrains/phpstorm-attributes
As long as instance of such a class is not actually gets instantiated (created) you should have no error because use statement is just a declaration.

use Special class if available, base class if not

I am useing a class in my code from the base framework. But it might not be available yet:
use BaseFramework\Libs\SpecialException;
So this use-Statement will result in an error. I.e. for frameworks, where this SpecialException is not available I would like to do:
use Exception as SpecialException;
so that I do not need to change my code.
I learned that the use is only creating an alias to the full named class.
I would like to use the originial SpecialException, if this is not possible I would like to use Exception.
I am wondering, what is the best practice or recommended way in PHP to solve this?
You can decide which one to throw using class_exists, it's going to be pretty nasty to actually use though.
Example:
try {
// do something
} catch (\Exception $e) {
// you'd still need to catch a common exception to all your custom types
if (class_exists('SomeCustomException')) {
throw new SomeCustomException; // or whatever
}
}
But you'd need to do that or something equally awful everywhere.
Your question suggests the actual answer here is to implement your own custom exception and throw that instead, as you have full control over it then.
Sometimes frameworks get around this kind of issue by having shared interoperability packages, so they can conform to common interfaces, throw the same exceptions and so on.
Since SpecialException might contains methods, variables and stuff that Exception doesn't contain, there is no rock-solid way to achieve what you need. Just replacing a class with a more generic one, might lead to trouble once you use some of the more dedicated methods.
You can see this post for working with class-aliases to achieve your desired behaviour, but for the reason meantioned above I wouldn't recommend it:
Why use class alisases?
You rather should use the factory-Pattern, just import the super-type of your eventually-custom-class and work with that super-type.
As soon, as you need to call a method on an instance, where you are not sure if that method is present (due to up-casting) - your class definition (or at least the method required) is placed into the wrong level inside the inheritance tree.
OK, thanks to some clues by #dognose and #bcmcfc this works for me:
use BaseFramework\Libs\SpecialException;
if (!class_exists("SpecialException")) {
class_alias("Exception", "SpecialException");
}
Why not just extend Exception? Something like this ...
namespace ProjectName\Exceptions\SpecialException;
class SpecialException extends Exception
{
// Implement custom properties and methods if required. Optional.
}
Here we have a custom class that uses SpecialException:
use \ProjectName\Exceptions\SpecialException;
class DocumentRepository
{
public static function fetchByID($docID)
{
throw new SpecialException("Document does not exist");
}
}
And now you don't need to worry about whether or not SpecialException exists or not. If calling code throws a regular Exception it will get caught, but if it throws a SpecialException it will still get caught as the new exceptions base class is Exception.
try
{
$doc = DocumentRepository::fetchByID(12);
}
catch(Exception $e)
{
die($e->getMessage());
}
Or, if you want to catch the SpecialException you can do (and I highly recommend this):
try
{
$doc = DocumentRepository::fetchByID(12);
}
catch(SpecialException $e)
{
die($e->getMessage());
}
Update to answer the problem in your comment
As a developer using a framework you have a location where you store your custom classes, files etc. right? Let me assume that this location is ProjectName/lib. And lets assume the framework you're using lives in the directory ProjectName/BaseFramework.
Your custom SpecialException will live in ProjectName/lib/Exceptions/SpecialException.php.
Currently, the framework doesn't include this exception. So in the files you wish to use SpecialException you use the following use line:
use \ProjectName\Exceptions\SpecialException
When the framework finally does implement this SpecialException you simply replace that use line with this one:
use \BaseProject\Exceptions\SpecialException
It's as simple as that.
If you try to do this in the way other users have suggested you will have dead code in your system. When SpecialException is finally implemented the checks on which type of Exception to use will be redundant.
This assumes you're using something like composer or something else that handles autoloading.

How to detect if a class does not exist without triggering an error

I have run into an interesting dilema. In a DataMapper class, I am generating a class name to be used for returned rows from a database.
The thing is, all of my classes are autoloaded, and can come from many places (library, application/models, etc.) and I wanted to check if the class name generated actually exists. Now, one would think that:
try
{
$test = new $className();
}
catch(Exception $ex)
{
// Class could not be loaded
}
But of course, php errors (instead of throwing an exception) saying the class could not be found... Not very helpful. Short of rewriting the autoloader in Zend_Loader to search all directories to see if the class could be loaded, is there anyway to accomplish this?
For anyone wondering why I would need to do this instead of just letting the Class Not Found error show up, if the class isn't found, I want to generate a class in a pre-determined location to make my life easy as this project goes along.
Thanks in advance!
Amy
P.S. Let me know if you guys need any more info.
PHP's function class_exists() has a flag to trigger the autoloader if the class should not be loaded yet:
http://www.php.net/class_exists
So you simply write
if (!class_exists($className)) {
// generate the class here
}

"Cannot override final method Exception::__clone()"

I'm getting the following strange error message whenever I attempt to run my script.
There is nothing I could see that would be causing the problem - in fact, the only thing in my script right now that deals with exceptions at all (they are the building blocks of a future addition) are the following lines:
class NoMatchingRouteException extends \RuntimeException { }
class HandlerException extends \RuntimeException { }
class HandlerMissingException extends HandlerException { }
class HandlerInaccessibleException extends HandlerException { }
These are various exceptions that form a tree of various exceptions I can throw.
Nowhere in here am I ever overriding the Exception class's __clone magic method, so I can't see where the problem is occurring.
I understand that, as it stands, my question may be hard to answer - thus, if you have any ideas where I should look for the problem and or what additional code I should look for to post, please post them in the comments and I will try to reply ASAP.
Thanks.
I've had this error when I mistakenly used
include
for an overridden exception class twice.
When I changed back to include_once, the error went away.
This error appears when you define class more than once. So avoid defining classes inside functions and use require_once for .inc files.
I managed to rid myself of the strange error (albeit accidentally) when I changed some architecture. I agree that the error is very weird, and would love to post the class in question - unfortunately, I can't revert back to it (I know, I know. ;). Thanks!

suggestions for unit testing exceptions

Consider a method which might throw an exception with some descriptive text:
if ($someCondition) {
throw new \Whatever\Exception('dilithium exhausted');
}
And elsewhere in the method is another block that might throw the same exception, but with different text:
if ($anotherCondition) {
throw new \Whatever\Exception('differentialator exploded');
}
While writing unit tests for this class, you create failure cases so that you can verify that these two exceptions get thrown properly. In these failure cases, do you prefer to:
A) Use #exceptionExpected in the test method's docblock to trap the generic \Whatever\Exception class and subsequently ignore the getMessage() text, assuming you got the right one? (Seems like a bad idea.)
or:
B) Use try/catch and then assert that the caught exception's getMessage() text equals the exact descriptive string you're expecting? (More resilient but it means changing your tests whenever you change your error wording.)
or:
C) Create a separate exception for each error case (e.g., \Whatever\DilithiumException and \Whatever\DifferentialatorException) and then use #exceptionExpected for each one.
I'm currently using B but tending toward C. I'm curious what others are doing in this same scenario. Do you have any guidelines that help you determine, "At what point does an error deserve its own exception class versus a more generic shared one?"
All of the above.
A is great, and I use as much as possible because it is simplest. There is another case when A does not work:
/**
* #exceptionExpected FooException
*/
test() {
// code that could throw FooException
...
// purpose of the test that throws of FooException
}
In this case, the test could pass when it should have failed because it didn't even get to what I was testing. A good way to deal with this is to use $this->setExpectedException()
B is great when you might actually use information from the exception. Rather than using the text of the exception message I would prefer to use the code. I have a form validation exception that packages up all the problems encountered in the data into one exception. By extending the exception class it becomes easy to transmit a good deal of information from the internal error state to the external handling code.
C accomplishes the same thing as B, but allows for simplifying the code by relying on more classes. The difference between these two is subtle and I tend to rely on design aesthetic to make the decision.
TL; DR: Use exception codes rather than messages, and design to the use case rather than the unit tests.
PHPUnit also provides #expectedExceptionCode and #expectedExceptionMessage when you need this level of detail. Warning: The latter requires the former.
BTW, I also tend toward A. If I need to express more meaning in the exception, I prefer to create a new exception class. I find the message to be too volatile to be worth testing in most applications.

Categories