I have been using PHP 5.4 until now in one of my old projects.
Recently I have decided to upgrade to PHP 7.2
I have around 800 files in my project and old developer team used count() hundreds of time in the project.
In PHP 5.4 even if the variable was not countable it worked as expected but in PHP 7.2 it throws an error.
I could go into each file and change the function but that could take a lot of time and the risk of not changing a couple of function here and there.
Can I extend count() function and write code to make it work as it used to in PHP 5.4?
Is there any other solution to this problem?
I am replacing all if conditions with count($var) functions to is_array($var) && count($var) for now.
Thank you
The difference between php 5.4 and php 7+ on that aspect is not that count changed it's behavior, is that php now turned errors into catchable exceptions.
Your code was failing silently on production with error_repoting turned off. That is extremely dangerous for the business, as the consistency of your app is unreliable, you should change that asap.
Having that said. Since all errors cannot be turned off anymore by setting your error_reporting to 0 (thats because now they are exceptions rather than errors, and that's really nice, php is no more a super unreliable language). You have to catch those exeptions, which are ErrorException.
Go to the index file of your application and add a try-catch block and swipe the dirty down the carpet again.
try {
//super buggy app bootstraping
} catch (ErrorException $ex) {
error_log($ex);
}
Well, i'd rather fix the counts anyway, they are toxic.
Note: is_array($var) && count($var) is not a goo idea, since arrays is not the only countable thing on php, ((is_array($var) || $var instanceof countable) && count($var)) would be a better, but not perfect solution. Also note a bug in php on any version - count(false) - returns 1
In other languages like Python you can use decorators to intercept an existing method without modifying the original method.
However PHP does not support decorators so you will need to manually change every location of that call.
You can use runkit to overload the count() function with your own implementation:
// to override internal functions, this system INI value needs truthy
assert((bool)ini_get('runkit.internal_override'));
// now you can define your own implementation
runkit_function_redefine('count', function ($var) {
return ((is_array($var) || $var instanceof \Countable) ? count($var) : 0);
});
However, please, consider using this only as a temporary stop gap to ease the pain of 5.4 to 7.2. Because this method is opaque, it'll become a maintenance liability at some point, a liability you probably don't want to linger.
Additionally, note that your conversion of is_array($v) && count($v) does not handle all countable things: i.e., \Generator and \Traversable. The runkit override example above does handle them, though.
Fix your code rather, and learn to type. Make sure your count method returns something countable
Related
I am investigating a still undiscovered zero day exploit in Revive Adserver. An attack happen on one location and the attacker was able to invoke an eval which was already in the development and production version of Revive Adserver code base.
I have investigated the access_logs and they indicate the user was doing a POST attack on delivery script fc.php but the payload of POST still remains unclear.
The code base of Revive Adserver is very mixed, old and weird at times. There are lots of points where an eval is called in the code, and one might find something like:
$values = eval(substr(file_get_contents(self::$file), 6));
Which is actually a Smarty template thing, but it looks really scary.
As mentioned, lots and lots of eval appearances are throughout the code and it would take a whole lot time to go through each one at this time.
Is there a possibility to override eval function in PHP to display some trace information, i.e. from which file it was called, on which line did it occur?
If not, is it possible to do this by modifying PHP's C/C++ source code and recompiling it altogether?
Or is there a PHP extension or some tool which can trace all eval callbacks throughout a script?
And if there's no such thing, it would be great if someone would develop it since it would speed up investigating malicious code containing eval's.
is there a possibility to override eval function in PHP to display some trace information, i.e. from which file it was called, on which line did it occur?
Sort of.
You can add eval to disable_functions in php.ini. Then when you call eval you'll get the fatal error function eval not found or such.
Then with a custom error handler.
set_error_handler(function($errno, $errstr, $errfile, $errline){
if(false !== strpos($errstr,'eval')){
throw new Exception();
}else{
return false; //see below
}
//debug_print_backtrace() - I prefer exceptions as they are easier to work with, but you can use this arcane thing too.
});
Or something like that (untested).
Unfortunately you cannot redefine eval as your own function. Eval is not really a function, its a language construct like isset, empty, include etc... For example function_exists('empty') is always false. Some are just more "function" like then others.
In any case you'll probably have to disable eval, I cant really think of a way around that.
Tip
Don't forget you can do this:
try{
throw new \Exception;
}catch(\Exception $e){
echo $e->getTraceAsString();
}
Which both suppresses the exception (so execution continues), and gives you a nice stacktrace.
Tip
http://php.net/manual/en/function.set-error-handler.php
It is important to remember that the standard PHP error handler is completely bypassed for the error types specified by error_types unless the callback function returns FALSE
So given the above, you can/should return false for all other errors. Then PHP will report them. I am not sure it really matters much in this case, as this isn't really meant to be in production code, but I felt it worth mentioning.
Hope it helps.
I'm analyzing some PHP code which is running on a server I don't have full access to. I can read the phpinfo though. The code seems to run fine on the server. In my local environment I just can't get the code to run as I get a "Catchable Fatal Error" at some call of a method using type hinting.
someMethod(string $str) {
// Do something...
}
The error says the following: "Argument 1 passed to ... must be an instance of path\of\namespace\string, string given ...".
There is no use keyword with a string class nor can I find anything trough a grep command in the folders of the development environment.
Are there any PHP modules, extensions that can make such a type hinting work? The server and my development environment are using PHP 5.4.25.
What could the live system possibly provide to make such code run? Might it use some other programming language based on PHP like Hack? The rest of the code is pretty straight PHP!
You mention that there is no use statement or namespace declaration that alludes to a 'string' class in the code. Does the code use an Autoloader?
Two possible issues at hand here:
Paths - It is possible that the live environment has another path set up, and/or that a file/class is being loaded/searched for via this 'other path' (outside the code you may be looking at).
Error Handling - Another possible cause is if there is an error handler in the production environment that always returns true.
I had this exact issue where a type-hint wasn't resolving in development, but I didn't realize until we pushed it live and the error handler was no longer registered.
Obviously a fatal parse error can not be stopped, but it turns out a catch-able error can be ignored if the error handler returns true. And this is what I am putting my money on in your case.
Additionally, it is very important to note that there is no way to type-hint a scalar, as one user pointed out.
A simpler way to say it is "it is not possible to type-hint anything that can be represented by a string." This is due to the way in which PHP handles it's more primitive variables, they can all be type-cast to one another, and therefore (because 1, "1" and true can all be == 1, == '1' and == true) it is not actually possible for the interpreter as it is written to actually catch and enforce scalar type-hints.
Answer this question: Is that variable supposed to be $str = "something"; or $str = new string(); (ie, a string or an object)?
If it is supposed to be a string, remove the type-hint, as nothing in PHP allows this support (save for HHVM, but you would know this if you were using it).
Since you say you do not have access to the code, I suggest notifying someone who does.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
What are the pros/cons of doing either way. Is there One Right Way(tm) ?
If you want to use exceptions instead of errors for your entire application, you can do it with ErrorException and a custom error handler (see the ErrorException page for a sample error handler). The only downside to this method is that non-fatal errors will still throw exceptions, which are always fatal unless caught. Basically, even an E_NOTICE will halt your entire application if your error_reporting settings do not suppress them.
In my opinion, there are several benefits to using ErrorException:
A custom exception handler will let you display nice messages, even for errors, using set_exception_handler.
It does not disrupt existing code in any way... trigger_error and other error functions will still work normally.
It makes it really hard to ignore stupid coding mistakes that trigger E_NOTICEs and E_WARNINGs.
You can use try/catch to wrap code that may generate a PHP error (not just exceptions), which is a nice way to avoid using the # error suppression hack:
try {
$foo = $_GET['foo'];
} catch (ErrorException $e) {
$foo = NULL;
}
You can wrap your entire script in a single try/catch block if you want to display a friendly message to your users when any uncaught error happens. (Do this carefully, because only uncaught errors and exceptions are logged.)
You should use exceptions in "Exceptional circumstances", that is when you call a method doFoo() you should expect it to perform, if for some reason doFoo is unable to do it's job then it should raise an exception.
A lot of old php code would take the approach of returning false or null when a failure has occured, but this makes things hard to debug, exceptions make this debugging much easier.
For example say you had a method called getDogFood() which returned an array of DogFood objects, if you called this method and it returns null when something goes wrong how will your calling code be able to tell whether null was returned because there was an error or there is just no dog food available?
Regarding dealing with legacy code libraries that use php's inbuilt error logging, you can override the error logging with the set_error_handler() function, which you could use to then rethrow a generic Exception.
Now that you have all of your code throwing detailed exceptions, you are free to decide what to do with them, in some parts of your code you may wish to catch them and try alternative methods or you can log them using your own logging functions which might log to a database, file, email - whichever you prefer. In short - Exceptions are more flexible .
I love the idea of using exceptions, but I often have third party libraries involved, and then if they don't use exceptions you end up with 3-4 different approaches to the problem! Zend uses exceptions. CakePHP uses a custom error handler, and most PEAR libraries use the PEAR::Error object.
I which there WAS one true way in this regard. The custom error handlers route is probably the most flexible in this situation. Exceptions are a great idea though if you're either only using your own code, or using libraries that use them.
Unfortunately in the PHP world we're still suffering from the refusal to die of PHP4, so things like exceptions, while they may represent best practise have been incredibly slow to catch on while everyone is still writing things to be able to work in both 4 and 5. Hopefully this debacle is now ending, though by the time it does, we'll have tensions between 6 and 5 instead...
/me holds head in hands...
It depends on the situation. I tend to use Exceptions when I am writing business logic/application internals, and trigger_error for Validator's and things of that sort.
The pro's of using Exceptions at the logic level is to allow your application to do in case of such an error. You allow the application to chose instead of having the business logic know how to present the error.
The pro's of using trigger_error for Validator's and things of that nature are, say,
try {
$user->login();
} catch (AuthenticationFailureException $e) {
set_error_handler("my_login_form_handler");
trigger_error("User could not be logged in. Please check username and password and try again!");
} catch (PersistenceException $pe) { // database unavailable
set_error_handler("my_login_form_handler");
trigger_error("Internal system error. Please contact the administrator.");
}
where my_login_form_handler pretties up the string and places the element in a visible area above the login form.
The idea of exception is elegant and makes the error handling process so smooth. but this only applies when you have appropriate exception classes and in team development, one more important thing is "standard" exceptions. so if you plan to use exceptions, you'd better first standardize your exception types, or the better choice is to use exceptions from some popular framework. one other thing that applies to PHP (where you can write your code object orienter combined with structural code), is that if you are writing your whole application using classes. If you are writing object oriented, then exceptions are better for sure. after all I think your error handling process will be much smoother with exception than trigger_error and stuff.
Obviously, there's no "One Right Way", but there's a multitude of opinions on this one. ;)
Personally i use trigger_error for things the exceptions cannot do, namely notices and warnings (i.e. stuff you want to get logged, but not stop the flow of the application in the same way that errors/exceptions do (even if you catch them at some level)).
I also mostly use exceptions for conditions that are assumed to be non-recoverable (to the caller of the method in which the exception occurs), i.e. serious errors. I don't use exceptions as an alternative to returning a value with the same meaning, if that's possible in a non-convoluted way. For example, if I create a lookup method, I usually return a null value if it didn't find whatever it was looking for instead of throwing an EntityNotFoundException (or equivalent).
So my rule of thumb is this:
As long as not finding something is a reasonable result, I find it much easier returning and checking for null-values (or some other default value) than handling it using a try-catch-clause.
If, on the other hand, not finding it is a serious error that's not within the scope of the caller to recover from, I'd still throw an exception.
The reason for throwing exceptions in the latter case (as opposed to triggering errors), is that exceptions are much more expressive, given that you use properly named Exception subclasses. I find that using PHP's Standard Library's exceptions is a good starting point when deciding what exceptions to use: http://www.php.net/~helly/php/ext/spl/classException.html
You might want to extend them to get more semantically correct exceptions for your particular case, however.
Intro
In my personal experience, as a general rule, I prefer to use Exceptions in my code instead of trigger_error. This is mainly because using Exceptions is more flexible than triggering errors. And, IMHO, this is also beneficial not only for myself as for the 3rd party developer.
I can extend the Exception class (or use exception codes) to explicitly differentiate the states of my library. This helps me and 3rd party developers in handling and debugging the code. This also exposes where and why it can fail without the need for source code browsing.
I can effectively halt the execution of my Library without halting the execution of the script.
The 3rd party developer can chain my Exceptions (in PHP > 5.3.*) Very useful for debugging and might be handy in handling situations where my library can fail due to disparate reasons.
And I can do all this without imposing how he should handle my library failures. (ie: creating complex error handling functions). He can use a try catch block or just use an generic exception handler
Note:
Some of these points, in essence, are also valid for trigger_error, just a bit more complex to implement. Try catch blocks are really easy to use and very code friendly.
Example
I think this is an example might illustrate my point of view:
class HTMLParser {
protected $doc;
protected $source = null;
public $parsedHtml;
protected $parseErrors = array();
public function __construct($doc) {
if (!$doc instanceof DOMDocument) {
// My Object is unusable without a valid DOMDOcument object
// so I throw a CriticalException
throw new CriticalException("Could not create Object Foo. You must pass a valid DOMDOcument object as parameter in the constructor");
}
$this->doc = $doc;
}
public function setSource($source) {
if (!is_string($source)) {
// I expect $source to be a string but was passed something else so I throw an exception
throw new InvalidArgumentException("I expected a string but got " . gettype($source) . " instead");
}
$this->source = trim($source);
return $this;
}
public function parse() {
if (is_null($this->source) || $this->source == '') {
throw new EmptyStringException("Source is empty");
}
libxml_use_internal_errors(true);
$this->doc->loadHTML($this->source);
$this->parsedHtml = $this->doc->saveHTML();
$errors = libxml_get_errors();
if (count($errors) > 0) {
$this->parseErrors = $errors;
throw new HtmlParsingException($errors[0]->message,$errors[0]->code,null,
$errors[0]->level,$errors[0]->column,$errors[0]->file,$errors[0]->line);
}
return $this;
}
public function getParseErrors() {
return $this->parseErrors;
}
public function getDOMObj() {
return clone $this->doc;
}
}
Explanation
In the constructor I throw a CriticalException if the param passed is not of type DOMDocument because without it my library will not work at all.
(Note: I could simply write __construct(DOMDocument $doc) but this is just an example).
In setsource() method I throw a InvalidArgumentException if the param passed is something other than a string. I prefer to halt the library execution here because source property is an essential property of my class and an invalid value will propagate the error throughout my library.
The parse() method is usually the last method invoked in the cycle. Even though I throw a XmlParsingException if libXML finds a malformed document, the parsing is completed first and the results usable (to an extent).
Handling the example library
Here's an example how to handle this made up library:
$source = file_get_contents('http://www.somehost.com/some_page.html');
try {
$parser = new HTMLParser(new DOMDocument());
$parser->setSource($source)
->parse();
} catch (CriticalException $e) {
// Library failed miserably, no recover is possible for it.
// In this case, it's prorably my fault because I didn't pass
// a DOMDocument object.
print 'Sorry. I made a mistake. Please send me feedback!';
} catch (InvalidArgumentException $e) {
// the source passed is not a string, again probably my fault.
// But I have a working parser object.
// Maybe I can try again by typecasting the argument to string
var_dump($parser);
} catch (EmptyStringException $e) {
// The source string was empty. Maybe there was an error
// retrieving the HTML? Maybe the remote server is down?
// Maybe the website does not exist anymore? In this case,
// it isn't my fault it failed. Maybe I can use a cached
// version?
var_dump($parser);
} catch (HtmlParsingException $e) {
// The html suplied is malformed. I got it from the interwebs
// so it's not my fault. I can use $e or getParseErrors()
// to see if the html (and DOM Object) is usable
// I also have a full functioning HTMLParser Object and can
// retrieve a "loaded" functioning DOMDocument Object
var_dump($parser->getParseErrors());
var_dump($parser->getDOMObj());
}
$var = 'this will print wether an exception was previously thrown or not';
print $var;
You can take this further and nest try catch blocks, chain exceptions, run selective code following a determined exception chain path, selective logging, etc...
As a side note, using Exceptions does not mean that the PROGRAM execution will halt, it just means that the code depending of my object will be bypassed. It's up to me or the 3rd party developer to do with it as he pleases.
The Exceptions are the modern and robust way of signaling an error condition / an exceptional situation. Use them :)
Using exceptions are not a good idea in the era of 3rd party application integration.
Because, the moment you try to integrate your app with something else, or someone else's app with yours, your entire application will come to a halt the moment a class in some 3rd party plugin throws an exception. Even if you have full fledged error handling, logging implemented in your own app, someone's random object in a 3rd party plugin will throw an exception, and your entire application will stop right there.
EVEN if you have the means in your application to make up for the error of that library you are using....
A case in example may be a 3rd party social login library which throws an exception because the social login provider returned an error, and kills your entire app unnecessarily - hybridauth, by the way. So, There you have an entire app, and there you have a library bringing in added functionality for you - in this case, social login - and even though you have a lot of fallback stuff in the case a provider does not authenticate (your own login system, plus like 20 or so other social login providers), your ENTIRE application will come to a grinding halt. And you will end up having to change the 3rd party library to work around these issues, and the point of using a 3rd party library to speed up development will be lost.
This is a serious design flaw in regard to philosophy of handling errors in PHP. Lets face it - under the other end of most of applications developed today, there is a user. Be it an intranet user, be it a user over internet, be it a sysadmin, it does not matter - there is generally a user.
And, having an application die on your face without there being anything you can do at that point other than to go back to a previous page and have a shot in the dark regarding what you are trying to do, as a user, is bad, bad practice from development side. Not to mention, an internal error which only the developers should know due to many reasons (from usability to security) being thrown on the face of a user.
As a result, im going to have to just let go of a particular 3rd party library (hybridauth in this case) and not use it in my application, solely for that reason. Despite the fact that hybridauth is a very good library, and apparently a lot of good effort have been spent on it, with a phletora of capabilities.
Therefore, you should refrain from using exceptions in your code. EVEN if the code you are doing right now, is the top level code that will run your application, and not a library, it is possible that you may want to include all or part of your code in other projects, or have to integrate parts or entirety of it with other code of yours or 3rd party code. And if you used exceptions, you will end up with the same situation - entire applications/integrations dying in your face even if you have proper means to handle whatever issue a piece of code provides.
I'm migrating a legacy app from PHP4 to PHP5.2, and one of the libraries it uses is now throwing exceptions which cause the scripts to break. Am I correct in thinking that under PHP4 this exception wouldn't be handled in the same way (i.e. not at all) and so the script would just continue on from the point the exception is now thrown? Is there any way to restore this behaviour under PHP5 (ideally just for the pages where the library is included)? The exception in question is a com_exception from a library dealing with Excel.
I have tried all binary PHP distributions for Windows that I found from 5.1.2 to 5.1.3 (cvs also) and i had to handle the exception.
My PHP setup is the typical one described in 'install.txt' of the PHP
package. The only change that I have made in php.ini (except of the
doc_root) is 'com.allow_dcom = true'.
so u will have to use
catch( com_exception $e )
{
print( $e->__toString( ) );
}
PHP 4 didn't support exceptions at all. The syntax to throw an exception would have been invalid syntax in PHP 4. See http://uk3.php.net/manual/en/language.exceptions.php
It would be handy to have been told a bit more about the class that is throwing the exeption.
My guess is that there's a bugm either in the class itself or in the way you're calling it which was being triggered all along and was being ignored, but is now causing this exception to be thrown.
This page is about the changes in PHP5, and has some insights into how the com_exception works: http://devzone.zend.com/article/762
To quote:
PHP 5 introduces structured exception handling (try, catch() and throw()) and this allows us to expose the underlying COM exceptions to PHP using the built-in com_exception class. If you want to catch errors in your scripts, you might write code like this:
<?php
$com = new COM("...");
try {
$com->call_a_method();
}
catch (com_exception $e) {
print $e . "\n";
}
?>
I hope the above code snippet helps you. Printing the error message may (should) reveal more about what exactly is going wrong, which should help you work out where to go from there.
It is possible, of course, that the Excel class you're using may also need to be upgraded; if it was part of your old PHP4 app, then presumably it's quite an old class; there may be a newer version of it available, and if you have found a bug in the class, then a new version may deal with that.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
What are the pros/cons of doing either way. Is there One Right Way(tm) ?
If you want to use exceptions instead of errors for your entire application, you can do it with ErrorException and a custom error handler (see the ErrorException page for a sample error handler). The only downside to this method is that non-fatal errors will still throw exceptions, which are always fatal unless caught. Basically, even an E_NOTICE will halt your entire application if your error_reporting settings do not suppress them.
In my opinion, there are several benefits to using ErrorException:
A custom exception handler will let you display nice messages, even for errors, using set_exception_handler.
It does not disrupt existing code in any way... trigger_error and other error functions will still work normally.
It makes it really hard to ignore stupid coding mistakes that trigger E_NOTICEs and E_WARNINGs.
You can use try/catch to wrap code that may generate a PHP error (not just exceptions), which is a nice way to avoid using the # error suppression hack:
try {
$foo = $_GET['foo'];
} catch (ErrorException $e) {
$foo = NULL;
}
You can wrap your entire script in a single try/catch block if you want to display a friendly message to your users when any uncaught error happens. (Do this carefully, because only uncaught errors and exceptions are logged.)
You should use exceptions in "Exceptional circumstances", that is when you call a method doFoo() you should expect it to perform, if for some reason doFoo is unable to do it's job then it should raise an exception.
A lot of old php code would take the approach of returning false or null when a failure has occured, but this makes things hard to debug, exceptions make this debugging much easier.
For example say you had a method called getDogFood() which returned an array of DogFood objects, if you called this method and it returns null when something goes wrong how will your calling code be able to tell whether null was returned because there was an error or there is just no dog food available?
Regarding dealing with legacy code libraries that use php's inbuilt error logging, you can override the error logging with the set_error_handler() function, which you could use to then rethrow a generic Exception.
Now that you have all of your code throwing detailed exceptions, you are free to decide what to do with them, in some parts of your code you may wish to catch them and try alternative methods or you can log them using your own logging functions which might log to a database, file, email - whichever you prefer. In short - Exceptions are more flexible .
I love the idea of using exceptions, but I often have third party libraries involved, and then if they don't use exceptions you end up with 3-4 different approaches to the problem! Zend uses exceptions. CakePHP uses a custom error handler, and most PEAR libraries use the PEAR::Error object.
I which there WAS one true way in this regard. The custom error handlers route is probably the most flexible in this situation. Exceptions are a great idea though if you're either only using your own code, or using libraries that use them.
Unfortunately in the PHP world we're still suffering from the refusal to die of PHP4, so things like exceptions, while they may represent best practise have been incredibly slow to catch on while everyone is still writing things to be able to work in both 4 and 5. Hopefully this debacle is now ending, though by the time it does, we'll have tensions between 6 and 5 instead...
/me holds head in hands...
It depends on the situation. I tend to use Exceptions when I am writing business logic/application internals, and trigger_error for Validator's and things of that sort.
The pro's of using Exceptions at the logic level is to allow your application to do in case of such an error. You allow the application to chose instead of having the business logic know how to present the error.
The pro's of using trigger_error for Validator's and things of that nature are, say,
try {
$user->login();
} catch (AuthenticationFailureException $e) {
set_error_handler("my_login_form_handler");
trigger_error("User could not be logged in. Please check username and password and try again!");
} catch (PersistenceException $pe) { // database unavailable
set_error_handler("my_login_form_handler");
trigger_error("Internal system error. Please contact the administrator.");
}
where my_login_form_handler pretties up the string and places the element in a visible area above the login form.
The idea of exception is elegant and makes the error handling process so smooth. but this only applies when you have appropriate exception classes and in team development, one more important thing is "standard" exceptions. so if you plan to use exceptions, you'd better first standardize your exception types, or the better choice is to use exceptions from some popular framework. one other thing that applies to PHP (where you can write your code object orienter combined with structural code), is that if you are writing your whole application using classes. If you are writing object oriented, then exceptions are better for sure. after all I think your error handling process will be much smoother with exception than trigger_error and stuff.
Obviously, there's no "One Right Way", but there's a multitude of opinions on this one. ;)
Personally i use trigger_error for things the exceptions cannot do, namely notices and warnings (i.e. stuff you want to get logged, but not stop the flow of the application in the same way that errors/exceptions do (even if you catch them at some level)).
I also mostly use exceptions for conditions that are assumed to be non-recoverable (to the caller of the method in which the exception occurs), i.e. serious errors. I don't use exceptions as an alternative to returning a value with the same meaning, if that's possible in a non-convoluted way. For example, if I create a lookup method, I usually return a null value if it didn't find whatever it was looking for instead of throwing an EntityNotFoundException (or equivalent).
So my rule of thumb is this:
As long as not finding something is a reasonable result, I find it much easier returning and checking for null-values (or some other default value) than handling it using a try-catch-clause.
If, on the other hand, not finding it is a serious error that's not within the scope of the caller to recover from, I'd still throw an exception.
The reason for throwing exceptions in the latter case (as opposed to triggering errors), is that exceptions are much more expressive, given that you use properly named Exception subclasses. I find that using PHP's Standard Library's exceptions is a good starting point when deciding what exceptions to use: http://www.php.net/~helly/php/ext/spl/classException.html
You might want to extend them to get more semantically correct exceptions for your particular case, however.
Intro
In my personal experience, as a general rule, I prefer to use Exceptions in my code instead of trigger_error. This is mainly because using Exceptions is more flexible than triggering errors. And, IMHO, this is also beneficial not only for myself as for the 3rd party developer.
I can extend the Exception class (or use exception codes) to explicitly differentiate the states of my library. This helps me and 3rd party developers in handling and debugging the code. This also exposes where and why it can fail without the need for source code browsing.
I can effectively halt the execution of my Library without halting the execution of the script.
The 3rd party developer can chain my Exceptions (in PHP > 5.3.*) Very useful for debugging and might be handy in handling situations where my library can fail due to disparate reasons.
And I can do all this without imposing how he should handle my library failures. (ie: creating complex error handling functions). He can use a try catch block or just use an generic exception handler
Note:
Some of these points, in essence, are also valid for trigger_error, just a bit more complex to implement. Try catch blocks are really easy to use and very code friendly.
Example
I think this is an example might illustrate my point of view:
class HTMLParser {
protected $doc;
protected $source = null;
public $parsedHtml;
protected $parseErrors = array();
public function __construct($doc) {
if (!$doc instanceof DOMDocument) {
// My Object is unusable without a valid DOMDOcument object
// so I throw a CriticalException
throw new CriticalException("Could not create Object Foo. You must pass a valid DOMDOcument object as parameter in the constructor");
}
$this->doc = $doc;
}
public function setSource($source) {
if (!is_string($source)) {
// I expect $source to be a string but was passed something else so I throw an exception
throw new InvalidArgumentException("I expected a string but got " . gettype($source) . " instead");
}
$this->source = trim($source);
return $this;
}
public function parse() {
if (is_null($this->source) || $this->source == '') {
throw new EmptyStringException("Source is empty");
}
libxml_use_internal_errors(true);
$this->doc->loadHTML($this->source);
$this->parsedHtml = $this->doc->saveHTML();
$errors = libxml_get_errors();
if (count($errors) > 0) {
$this->parseErrors = $errors;
throw new HtmlParsingException($errors[0]->message,$errors[0]->code,null,
$errors[0]->level,$errors[0]->column,$errors[0]->file,$errors[0]->line);
}
return $this;
}
public function getParseErrors() {
return $this->parseErrors;
}
public function getDOMObj() {
return clone $this->doc;
}
}
Explanation
In the constructor I throw a CriticalException if the param passed is not of type DOMDocument because without it my library will not work at all.
(Note: I could simply write __construct(DOMDocument $doc) but this is just an example).
In setsource() method I throw a InvalidArgumentException if the param passed is something other than a string. I prefer to halt the library execution here because source property is an essential property of my class and an invalid value will propagate the error throughout my library.
The parse() method is usually the last method invoked in the cycle. Even though I throw a XmlParsingException if libXML finds a malformed document, the parsing is completed first and the results usable (to an extent).
Handling the example library
Here's an example how to handle this made up library:
$source = file_get_contents('http://www.somehost.com/some_page.html');
try {
$parser = new HTMLParser(new DOMDocument());
$parser->setSource($source)
->parse();
} catch (CriticalException $e) {
// Library failed miserably, no recover is possible for it.
// In this case, it's prorably my fault because I didn't pass
// a DOMDocument object.
print 'Sorry. I made a mistake. Please send me feedback!';
} catch (InvalidArgumentException $e) {
// the source passed is not a string, again probably my fault.
// But I have a working parser object.
// Maybe I can try again by typecasting the argument to string
var_dump($parser);
} catch (EmptyStringException $e) {
// The source string was empty. Maybe there was an error
// retrieving the HTML? Maybe the remote server is down?
// Maybe the website does not exist anymore? In this case,
// it isn't my fault it failed. Maybe I can use a cached
// version?
var_dump($parser);
} catch (HtmlParsingException $e) {
// The html suplied is malformed. I got it from the interwebs
// so it's not my fault. I can use $e or getParseErrors()
// to see if the html (and DOM Object) is usable
// I also have a full functioning HTMLParser Object and can
// retrieve a "loaded" functioning DOMDocument Object
var_dump($parser->getParseErrors());
var_dump($parser->getDOMObj());
}
$var = 'this will print wether an exception was previously thrown or not';
print $var;
You can take this further and nest try catch blocks, chain exceptions, run selective code following a determined exception chain path, selective logging, etc...
As a side note, using Exceptions does not mean that the PROGRAM execution will halt, it just means that the code depending of my object will be bypassed. It's up to me or the 3rd party developer to do with it as he pleases.
The Exceptions are the modern and robust way of signaling an error condition / an exceptional situation. Use them :)
Using exceptions are not a good idea in the era of 3rd party application integration.
Because, the moment you try to integrate your app with something else, or someone else's app with yours, your entire application will come to a halt the moment a class in some 3rd party plugin throws an exception. Even if you have full fledged error handling, logging implemented in your own app, someone's random object in a 3rd party plugin will throw an exception, and your entire application will stop right there.
EVEN if you have the means in your application to make up for the error of that library you are using....
A case in example may be a 3rd party social login library which throws an exception because the social login provider returned an error, and kills your entire app unnecessarily - hybridauth, by the way. So, There you have an entire app, and there you have a library bringing in added functionality for you - in this case, social login - and even though you have a lot of fallback stuff in the case a provider does not authenticate (your own login system, plus like 20 or so other social login providers), your ENTIRE application will come to a grinding halt. And you will end up having to change the 3rd party library to work around these issues, and the point of using a 3rd party library to speed up development will be lost.
This is a serious design flaw in regard to philosophy of handling errors in PHP. Lets face it - under the other end of most of applications developed today, there is a user. Be it an intranet user, be it a user over internet, be it a sysadmin, it does not matter - there is generally a user.
And, having an application die on your face without there being anything you can do at that point other than to go back to a previous page and have a shot in the dark regarding what you are trying to do, as a user, is bad, bad practice from development side. Not to mention, an internal error which only the developers should know due to many reasons (from usability to security) being thrown on the face of a user.
As a result, im going to have to just let go of a particular 3rd party library (hybridauth in this case) and not use it in my application, solely for that reason. Despite the fact that hybridauth is a very good library, and apparently a lot of good effort have been spent on it, with a phletora of capabilities.
Therefore, you should refrain from using exceptions in your code. EVEN if the code you are doing right now, is the top level code that will run your application, and not a library, it is possible that you may want to include all or part of your code in other projects, or have to integrate parts or entirety of it with other code of yours or 3rd party code. And if you used exceptions, you will end up with the same situation - entire applications/integrations dying in your face even if you have proper means to handle whatever issue a piece of code provides.