by Dominik Jungowski
29. April 2009
No Comment
There's a strange bug in PHP we recently came across: When using func_get_args() is used as second (or third or fourth etc) parameter, PHP crashes with a Fatal Error.
Try this code:
function returnSomething()
{
return func_get_args();
}
function callSomeFunction($message)
{
someFunction(func_get_args(), 'Hello World');
someFunction(1, returnSomething());
someFunction(1, func_get_args());
}
function someFunction($code, $message)
{
print($message . PHP_EOL);
}
callSomeFunction('Hello World');
The result should be
Hello World
Array
Array
yet you get
Hello World
Array
Fatal error: func_get_args(): Can't be used as a function parameter
in /home/dominik/test.php on line 12
The code works with any other function (see example code), only func_get_args() causes a crash.
So I thought: Let's commit a bug! I then found out that this very bug has already been committed in 2005: http://bugs.php.net/bug.php?id=27887. The bug was closed (unfixed) with this really unsatisfactory explanation:
[16 Jun 2005 1:34am UTC] tony2001@php.net
Damien, the docs say: "This function cannot be used directly as a function
parameter. Instead, its result may be assigned to a variable, which can
then be passed to the function.", so don't use it as a parameter.
[16 Jun 2005 9:04am UTC] derick@php.net
So there is no bug, and the docs already describe it -> bogus.
Of course: Why should I fix a bug, when I can just put a note in the documentation that says "don't use it as parameter".
Edit: I filed a bug and it was closed with the comment "This is a known issue and have been fixed in 5.3.". Good to know!
by Dominik Jungowski
7. April 2009
Comments (4)
Everyone surely has already encountered it: A fatal error on a page that is online including the white page you get to see because of it. There's a nice and easy way of showing an error page instead of a white page if a Fatal Error occurs by using output buffering. According to its documentation, ob_start() supports a callback as first parameter. The cool thing is that this callback is also called in case of a Fatal Error!
Here's how it works:
class Redirector
{
public static function redirectOnError($buffer)
{
$lastError = error_get_last();
if(!is_null($lastError) && $lastError['type'] === E_ERROR) {
header('HTTP/1.1 302 Moved Temporarily');
header('Status: 302 Moved Temporarily');
header('Location: error.php');
exit();
}
return $buffer;
}
}
ob_start(array('Redirector', 'redirectOnError'));
print('Hello');
foobar();
ob_end_flush();
The callback is called in the moment the content is flushed, that's why the flush should always be the last line in your output script if you wanna catch all Fatal errors. Since the callback is always executed, I added a check whether a fatal error occured, since we don't want a redirect when no error occurs. Also keep in mind that the callback has to return the buffered output (that is already passed to the callback as a parameter) which will be printed after its execution. (Note that error_get_last() is PHP >= 5.2.0)
Note: Instead of an 302 redirect, which I used in the example, you could also just output an error message on the same page:
public static function redirectOnError($buffer)
{
$lastError = error_get_last();
if(!is_null($lastError) && $lastError['type'] === E_ERROR) {
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Status: 503 Service Temporarily Unavailable');
$buffer = 'Sorry, an error occured';
}
return $buffer;
}
Thanks to Jakob and Lars for some input and refinements on this blogpost.
by Dominik Jungowski
4. April 2009
Comments (4)
In case you're planning to work or already do work with the Zend Framework, there's a really good (german) book that just got out, which i test-read: Das Zend Framework: Von den Grundlagen bis zur fertigen Anwendung by Ralf Eggert.
Although this book is primarily for Zend Framework beginners, there is also a lot of content you may still not know even though you already work with ZF a bit longer. The code examples are really good and helpful and you really get to know a lot about ZF. The book shows how to build a web application step by step using ZF.
You can order the book at Amazon.
by Dominik Jungowski
3. April 2009
Comments (5)
When unit testing, you'd also want to test whether your application throws Exceptions as expected (the following examples are based on SimpleTest). Assumption for the examples is, that we have a method that expects an integer as parameter.
First way you probably come up with is this:
try {
$class->method('abc');
} catch(Exception $e) {
$this->assertIsA('Exception', $e);
}
Generally this looks ok, but it's not. If the method doesn't throw an exception, the test won't fail since the catch block is never executed. That's why we simply drag the test out of the catch block:
try {
$class->method('abc');
} catch(Exception $e) {
}
$this->assertIsA('Exception', $e);
unset($e);
Now the test fails when the exception isn't thrown because first of all $e won't be set and will surely not be an exception. It is important to add an unset($e), especially if you're testing for more exceptions directly afterwards.
Let's now assume that the method throws an InvalidArgumentException if the given parameter is not an integer.
try {
$class->method('abc');
} catch(Exception $e) {
}
$this->assertIsA('InvalidArgumentException', $e);
unset($e);
Now the test is in a state where it fails when no exception is thrown or when the thrown exception is not an InvalidArgumentException.
In case you're not lazy on typing, you might add one more line, which also allows you to put the assert back into the catch block:
try {
$class->method('abc');
$this->fail('Excepted exception was not thrown');
} catch(Exception $e) {
$this->assertIsA('InvalidArgumentException', $e);
unset($e);
}
by Dominik Jungowski
1. April 2009
Comments (5)
Today we had a database crisis due to a powercut. Apart from corrupted tables (because of the immediate "shutdown") we also had another problem: CLI scripts/cronjobs suddenly threw a MySQL server has gone away Exception after a really short amount of time (maybe 30 seconds)
I found this useful list, the only problem was: all the points didn't match for my case. So we came up with an own list that we worked through:
- Flush privileges
- Restore the User grants/privileges
- Restart the MySQL server
- dpkg-reconfigure mysql-server-5.0 (or similar)
In our case restarting the MySQL server fixed the problem.
< März 2009 | Mai 2009 >