Fatal error: Exception thrown without a stack frame in Unknown on line 0

Oh, the ever-dreaded “Exception thrown without a stack frame” error. It will haunt you in your sleep. It will steal the spacebar from your keyboard, cut your mouse cord, and uninstall your operating system. It is the bane of all programming existence!

This is possibly the most esoteric error that can be thrown by PHP, but once you know it, you’ll never forget it.

The problem arises when using a custom exception handler, which is typical of many frameworks (you probably landed here because your framework just spit this out at you, right?).

I’m not sure what the heck it means, but you will get this error when throwing an Exception within a custom Exception handler that you have previously set. I presume this is due to some kind of infinite looping scenario, which I shall refer to as the infinite looping scenario problem.

Code Example

// Custom Exception handler.
function myExceptionHandler($exception)
{
	// Exception within an Exception. It's Exception Inception. Ahhhhhhh!
	throw new Exception('Should result in weird stack frame error!');
}

// Set our custom Exception handler.
set_exception_handler('myExceptionHandler');

throw new Exception('Error!');

When running this code, you should get:

Fatal error: Exception thrown without a stack frame in Unknown on line 0

The Solution

Don’t make mistakes in your custom Exception handler! Seriously. This is the method you’ve declared will solve all problems. You must ensure that you don’t throw any Exceptions in this function. Keep the logic simple. Avoid complex logging that can result in additional Exceptions when the database is down, wrap try/catch blocks around possible culprits, do whatever it takes. Even if you can’t ensure that you won’t throw Exceptions in your custom handler, at least you know what the error means now, which may help you debug the problem more quickly in the future!

 

PHP Notice: unserialize(): Error at offset 2 of 65535 bytes

Every now and again, a developer will ping me via IM, email, or other, saying: “hey, do you know what this means?” : PHP Notice:  unserialize(): Error at offset (x) of (y) bytes. Generally, this error surfaces while trying to unserialize() a value pulled from the database. In typical PHP fashion, the esoteric error doesn’t provide the most meaningful debugging information, but the problem is quite simple: The serialized string has been broken, and cannot be decoded. When considering that the value has been stored and subsequently retrieved from the database, the light bulb triggers, and I immediately ask, “what is the size of the field you are storing the serial in?” Typically, the answer tends to be either TEXT or VARCHAR(n).

A Possible Scenario

Perhaps you have a logging system that stores dumps of debug_backtrace() values in a ‘verbose’ member of a debug array. If a particularly long string occurs during log, and it exceeds the storage length for TEXT (64KB), MySQL will simply truncate it at the maximum number of bytes. The value will store, and then on the next retrieval, you’ll have a broken serial string. PHP is not able to unserialize the broken string, so the error is thrown.

An Example of the Problem

First, we’ll need a sample table in our MySQL database:

CREATE TABLE  `test`.`serials` (
  `serial_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `serial` text NOT NULL,
  PRIMARY KEY (`serial_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Now for some PHP code:

<?php
// Generate a serial outside the bounds of TEXT column.
$serial = serialize(str_repeat('!', 65536));

// Connect.
$db = new mysqli('dbserver', 'username', 'password', 'test');

// Clean and insert the serial.
$cleanSerial = $db->real_escape_string($serial);
$db->query("INSERT INTO `serials` SET `serial` = '{$cleanSerial}'");

// Read the serial back.
$result = $db->query("SELECT * FROM `serials` LIMIT 1");
if ($result->num_rows)
{
	$row = $result->fetch_object();

	// Try to unserialize it.
	unserialize($row->serial);
}

When you run the code, you should see some version of the following error:

PHP Notice:  unserialize(): Error at offset 2 of 65535 bytes in /home/john/Desktop/serials.php on line 19
PHP Stack trace:
PHP   1. {main}() /home/john/Desktop/serials.php:0
PHP   2. unserialize() /home/john/Desktop/serials.php:19

I’m using php on the CLI, and have Xdebug installed, so I’ll get a full stack trace.

The Solution

The only way to truly solve this issue, is by ensuring that the serials you store do not exceed the maximum length of the MySQL table field in which you are storing them.

Of course, you could cheat. By using the ever-handy, always-evil error control operator ‘@’, you can suppress the notice-level error that is triggered by bad serialized data.

Consider the following modifications to line 19-20:

// Try to unserialize it.
$unserialized = @unserialize($row->serial);
var_dump($unserialized);

When you run the modified code, you should get the following:

bool(false)

unserialize() will yield a boolean FALSE for broken serials when suppressing the error. If you decide to use this approach, be sure to check the return from unserialize, for instance via something like:

if ($unserialized)
{
	// Serial is good!
}
else
{
	die('serial is bad!');
}

 

Multiple Inheritance in PHP via Traits

PHP 5.4 is going to introduce some new tricks, particularly multiple inheritance, using an ingenious traits system. Installing an alpha version of PHP is the only way to get a peek (at the time of this writing), and there are a few examples provided on the documentation page: http://us3.php.net/traits.

It’s always exciting to see PHP dip a few more of its toes into the OOP waters, so let’s see what we can do with this new feature!

Continue reading

Improve Security, Enable PHP Error Reporting

It is all-too-common to see weak levels of error reporting in development environments, which is unfortunate, because proper error reporting is not only one of the best debugging tools available to a developer, but is also an excellent way to reveal significant security flaws in code before they become a threat in the real world.  Continue reading