Consider this simple generator function in PHP.
function createAGenerator() {
echo 'Before First Yield',"\n";
yield 'First Yield';
echo 'Before Second Yield',"\n";
yield 'Second Yield';
echo 'Before Third Yield',"\n";
yield 'Third Yield';
}
If I throw into this generator using the generator object's throw method
$generator = createAGenerator();
try {
$generator->throw(new Exception('Throwing into a Generator'));
} catch(Exception $e) {
echo 'Caught Exception: ', $e->getMessage(), "\n";
}
echo 'Resuming Main Program Execution',"\n";
the generator function will re-throw the exception for me to catch. This all works as I expect.
However -- my generator now seems permanently stuck. If I try to move on to the next yield, or send a new value in, the generator appears to just return NULL. For example, the following program
<?php
function createAGenerator() {
echo 'Before First Yield',"\n";
yield 'First Yield';
echo 'Before Second Yield',"\n";
yield 'Second Yield';
echo 'Before Third Yield',"\n";
yield 'Third Yield';
}
$generator = createAGenerator();
try {
$generator->throw(new Exception('Throwing into a Generator'));
} catch(Exception $e) {
echo 'Caught Exception: ', $e->getMessage(), "\n";
}
echo 'Resuming Main Program Execution',"\n";
var_dump($generator->send('Some Value'));
var_dump($generator->current());
var_dump($generator->next());
var_dump($generator->current());
Returns the following output.
Before First Yield
Caught Exception: Throwing into a Generator
Resuming Main Program Execution
NULL
NULL
NULL
NULL
Is there a way for a generator to recover from this? Or does an uncaught exception in a generator "break" this current instance of the generator?
When you throw an exception, the generator will skip right to the end of the function. This is exactly what happens in a regular functions, if that function called something that threw an exception.
Your two options are:
To catch it in the generator (or use finally).
To not throw it at all
The idea of throwing an exception and use it as a "temporary" messaging system but resume normal operation is a bit weird. It sounds like a bit of an abuse of exceptions. Perhaps what you are trying to achieve can be accomplished without throwing an exception.
If you're trying to model an operation that has multiple steps and each can individually fail or succeed, one option might be to just yield the exception and not throw it.
Related
Is it possible to catch an exception in an generator and just yield the next value? I tried something similar like the code in the example below, but it stops on an exception and does not yield the next value as "expected".
function generator(){
foreach($aLotOfWork as $task){
try {
$promise = doSomethingThatCanFailBadly($task);
yield $promise;
} catch (Exception $e) {
echo "oh.. there is an error, but I don't care and continue";
}
}
}
IMHO This is not a duplicate of (php: catch exception and continue execution, is it possible?), because this person just wanted to know how to catch an exception in php and go on. In my case I already catch all exceptions, but the generator stops and does not go on as intended.
Your code is right and it will capture the exception and continue, check this out:
$ cat so.php
<?php
function doSomethingThatCanFailBadly($task) {
if ($task == 3) {
throw new Exception();
}
return $task;
}
function generator(){
$aLotOfWork = array(1,2,3,4,5);
foreach($aLotOfWork as $task){
try {
$promise = doSomethingThatCanFailBadly($task);
yield $promise;
} catch (Exception $e) {
echo "oh.. there is an error, but I don't care and continue\n";
}
}
}
foreach (generator() as $number) {
echo "$number\n";
}
?>
$ php so.php
1
2
oh.. there is an error, but I don't care and continue
4
5
Have a look at your error stack trace. Maybe what's happening is that something inside your doSomethingThatCanFailBadly method is producing an exception but it is also catching it and forcing the quit with die() or exit() before it ever gets to your catch block. There's not much you can do in that case. You could maybe use register_shutdown_function and see if that helps, but that is starting too look messy.
You would need to save to output of the generator to an array, and if during individual yields an exception is thrown, with right exception handling, it simply will not get into the array.
In your example the doSomethingThatCanFailBadly throws an exception, then the program flow gets to the catch branch. So actually doSomethingThatCanFailBadly has no return value that could be assigned to $promise - so there's nothing to yield at that point.
#palako explained the problem very nicely, but he does not really provide a solution for my case. I use this generator to generate Promises and the consumer (Guzzle's EachPromise-method) seems to stop working on an Exception. So I replaced the throw new SomeException('error message') statement by return RejectedPromise('error message').
I have to admit that this is a very situation-specific solution, but you can do something similar in other settings too: just return an object instead of using exceptions.
I am currently developing my PHP SDK to interact with a REST API. Let's say the API can create a 'car' model. To create a car I would write something like this...
$car = $api->create('car', array(
'wheels' => 6,
'color' => 'blue'
));
If another developer downloads my SDK and tries to create a car model incorrectly and forgets to include required arguments. How can I throw an exception via the SDK to notify the developer of missing arguments, other than them seeing a PHP error like Warning: Missing argument 1 for BMW::create() which does not include many details.
function foo($bar, $baz) {
if (!isset($bar, $baz)) {
throw new InvalidArgumentException("Detailed description of what's wrong here");
}
...
}
PHP will trigger a warning, but will still execute your function as usual (which is... oh well, let's not dwell on it). That means you can do your regular argument checking inside your function and throw exceptions or trigger_errors all you want in as much detail as you want.
Please go through this page...
http://php.net/manual/en/language.exceptions.php
And try something like following
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo "Hello World\n";
?>
As of PHP 7.1, invoking user-defined functions with too few arguments will result in an Error exception. You do not need to implement the functionality yourself.
https://www.php.net/manual/en/migration71.incompatible.php
A very simple question from someone without much experience. The following try catch block has the section, "(Exception $e)" : is this like sql, where $e becomes an alias of Exception? If so, is this type of alias assignment used elsewhere in php, because I haven't come across it? I have searched for hours without being able to find an explanation on the web.
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
try {
echo inverse(5) . "<br/>";
echo inverse(0) . "<br/>";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "<br/>";
}
echo 'Hello World';
What you mention is a filter construction. It resembles a declaration as known from other, declarative languages. However it has a different meaning in fact. Actually php does not have the concept of an explicit declaration (which is a shame...).
Take a look at this example:
function my_func($x) {
try {
/* do something */
if (1===$x)
throw new SpecialException('x is 1.');
else if (is_numeric($x)) }
throw new SpecialException('x is numeric.');
else return $x;
}
catch (SpecialException $e) {
echo "This is a special exception!";
/* do something with object $e of type SpecialException */
}
catch (Exception $e) {
echo "This is a normal exception!";
/* do something with object $e of type SpecialException */
}
}
Here it becomes clear what the construction is for: it filters out by type of exception. So the question which of several catch blocks is executed can be deligated to the type of exception having been thrown. This allows a very fine granulated exception handling, where required. Without such feature only one catch block would be legal and you'd have to implement conditional tests for potential exception types in each catch block. This feature makes the code much more readable, although it is some kind of break in the php syntax.
You don't have to, but you can create own exception classes with special behavior and, more important, accepting and carrying more information about what actually happened.
It's OO PHP. The $e is an instance of the exception object.
$e could easily be labelled anything else, so long as it's referred to thereon when you want to getmessages, etc.
For instance;
try {
echo inverse(5) . "<br/>";
echo inverse(0) . "<br/>";
} catch (Exception $oops) {
echo 'Caught exception: ', $oops->getMessage(), "<br/>";
}
I would like the error log to show each error on a new line beginning with a time stamp.
Currently the errors just show up in a connected chunk.
catch(PDOException $e) {
file_put_contents('logs/insert_errors.txt', $e->getMessage(), FILE_APPEND);
}
I'm sure this is fairly simple. Just my first time working with PDO and not sure how to accomplish this.
Preamble: As #Phil mentioned this has nothing to do with PDO.
There are two parts to your question:
a) it is all in one connected line
The reason is quite obvious, you are not adding a new line character at the end. What that character(s) is(are) depend on your OS, but PHP solves that for you with the PHP_EOL constant that you can just append to the end of your string.
b) you want a timestamp at the beginning of each line
Again, the issue is because you didn't put it there. Just adding the output of date('r') before printing out the message should be enough.
The real solution: a real logger
You may want to look at using a complete logging solution for your code. Someting like log4php, for example.
Homebrew solution
Alternatively, if that is too much, you could build your own logger. A good small example one can be found in the comments of the documentation of PHP's error_log function that I adapted for your needs:
Class Log {
const ERROR_DIR = '/home/site/error_log/db_errors.log';
// Log PDO errors
public function error($msg) {
$date = date('r');
$log = sprintf('%s: %s%s', $date, %msg, PHP_EOL);
error_log($log, 3, self::ERROR_DIR);
}
// shortcut for PDO exceptions
public function exception($e) {
$this->error($e->getMessage())
}
}
try {
$log = new log();
$log->error($msg); //use for general messages
} except (Exception $e)
$log->exception($e); //use for exceptions
}
Simply format the string to be inserted, eg
catch (PDOException $e) {
$error = sprintf('%s: %s%s', date('c'), $e->getMessage(), PHP_EOL);
file_put_contents('logs/insert_errors.txt', $error, FILE_APPEND);
// don't forget to re-throw the exception otherwise your program will
// continue execution
throw $e;
}
Since a short period of time I'm working with Try Catch in PHP. Now, every time a new error is thrown you get a fatal error on the screen, this isn't really user friendly so I was wondering if there's a way to give the user a nice message like an echo instead of a fatal error.
This is the code I have now:
public static function forceNumber($int){
if(is_numeric($int)){
return $int;
} else {
throw new TypeEnforcerException($int.' must be a number');
}
}
public function setStatus($status) {
try {
$this->status = TypeEnforcer::forceInt($status);
} catch (TypeEnforcerException $e) {
throw new Exception($e->getMessage());
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
This is best solved with a frontend controller that is able to catch all uncatched exceptions:
<?php
require('bootstrap.php');
try {
$controllerService->execute($request);
} catch (Exception $e) {
$controllerService->handleControllerException($e);
}
You can then write code to return the internal server error because an exception signals an exceptional case so it normally is an 500 internal server error. The user must not be interested what went wrong other than it just didn't work out and your program crashed.
If you throw exceptions to give validation notices you need to catch those in a different layer (and you're probably doing it wrong if you use exceptions for that).
Edit: For low-level functions, because PHP is loosely typed, if a function expects and int, cast to intDocs:
public static function forceNumber($int){
$int = (int) $int;
return $int;
}
this will actually force the integer. In case the cast is not possible to do (e.g. $int it totally incompatible) PHP will throw the exception for you.
The example is a bit akward because by the method's name you use it to validate some number and provide an error if not (here wrongly with an exception). Instead you should do some validation. If you expect wrong input, it's not an exceptional case when wrong input is provided, so I would not use exceptions for that.