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.
Related
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.
Now what I generally do when writing code is something like this
function changeBookAuthor(int $id, string $newName){
if(!$newName){
throw new MyAppException('No author name was provided');
}
$book = Books::find($id);
if(!$book){
throw new MyAppException('The provided book id could not be found');
}
}
in the laravel doc we see:
https://laravel.com/docs/5.4/errors
public function report(Exception $exception)
{
if ($exception instanceof CustomException) {
//
}
return parent::report($exception);
}
Now how to I properly handle the exception? they are all the same exception and they have no code neither. Should I provide an error code?
the problem with php exception is that they use integers. Is quite annoying imho. Better would be 'changeauthor_bookid_notfound' as code instead of a random number. Should I create an exception class for each single exception? e.g. not reuse MyAppException that seems a bit tedious. I would have a trillion classes.
Now if for a special exception I want special handling, with my code, I cannot easily do it. I have no code to check for (e.g. $exception->code == 3331 then do special) and I don't have custom exception classes neither
what is a proven good solid way to handle this case?
code, new class on each error, something else all together?
and if provide a code, what is a nice way to do it?
The "proper" way to do it would be to define either a custom Exception class for each exception, or to define custom exceptions based on the type of error being thrown, however realize that Laravel already has many built in exceptions and mechanics for handling the use cases you outlined.
For instance, in the case of the "Book Not Found" exception, rather than manually triggering an exception yourself, you could use Books::findOrFail($id); which throws an instance of ModelNotFoundException when appropriate.
Also, in PHP there is no need to handle exceptions for unprovided arguments. Unless expressly denoted as optional, all method arguments are required, and Laravel will throw a PHP exception if an argument is missing.
Additionally, Laravel provides the abort() magic method which throws a HTTP error along with a custom error message and can be used like so:
abort(418, "I'm a teapot...")
So, if you must reinvent the wheel, the proper way is to define custom exception classes and define the custom handlers for those classes, but realize that Laravel already has many built in tools for managing exceptions without needing to do so.
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
}
I've used exceptions in Java and like the way it won't let you call a method unless you catch or throw the exceptions that it might throw.
I'm looking for something similar in PHP. I realise PHP is more dynamic than Java, and doesn't even let you define the exceptions that it throws, but what is the closest I can get?
We document our methods using PHP Doc, so something that triggered an E_WARNING if you called a method without the correct try/catch block, or threw an exception without the correct #thows comment, would be perfect.
There's no way to do it in PHP itself. You will have to parse PHP and figure it out yourself. Try writing phc plugin for this.
I don't think you can reasonably get very close at all, because the language core provides just nothing whatsoever for you to work with. At best, you'd wind up creating some kind of entirely user-space funcall/exception validation mechanism that would have an absolutely horrific impact on performance.
I'm not sure that you can accomplish your stated goal. The PHP environment doesn't analyze what a function might or might not do, which would generally be a compile-time operation for other languages (I would think). I don't think you can even find that sort of thing via Reflection.
You are wrong however when you say that you can't define the exceptions that get thrown, as the Exception base class is fully extendable. PHP doesn't throw any exceptions by default though, it triggers errors. There is a fundamental difference between triggered errors and Exceptions, the latter being a user-land construct for the most part.
This isn't your question, but I'd put out a suggestion that if you wanted to move to a fully Exception-oriented environment, you could write your own error handler using set_error_handler() and manage the PHP triggered errors, and have it throw out an Exception.
I think you can simply reproduce this behavior in PHP using exception handlers and reflection.
class YourException extends Exception {
public function __toString() {
return __CLASS__;
}
}
class MyObject {
public function __construct(){}
/**
* #throws MyException
*/
public function myMethod() {
return 'foo';
}
}
try {
$o = new MyObject();
$o->myMethod();
}
catch(YourException $e) {
$method = new ReflectionMethod('MyObject', 'myMethod');
$method->getDocComment();
$throws = // Get the #throws comment (foreach, regexp, whatever);
if($e === $throws) {
// do something
}
}
Setting your own exception handler.
Grab and analyse the comments with Reflection mechanism (see getDocComment)
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
}