PHP Autoloader solves the session problem
PHP Autoloader serves not only for comfort purposes. You might have a problem with incomplete objects after calling unserialize(). Especially missing class definitions during session deserialization are a common error. If a class definitions is not defined, PHP creates incomplete objects of the class __PHP_Incomplete_Class. This degenerated object misses every method. To avoid this error, every class definition must be included before starting the session.
Following examples will show the problem and its solutions in PHP-Code.
Therefore we will read a MyClass object from the session. The
class MyClass is defined in MyClass.php:
<?php
class MyClass
{
private
/**
* @var int
*/
$_counter = 0;
/**
* @return int
*/
public function increment()
{
return $this->_counter++;
}
}
The next examples do all the same things:
- If the object $_SESSION['myObject'] doesn't exist, a new object will be stored in the session.
- The class name of the object in the session is printed out.
-
Finally the method
MyClass::increment()is called on that object and the result is printed out.
During the first execution every example is correct and has following output:
MyClass 0In this iteration they did only create a new object and store it in the session.
A second execution should load the stored object from the session and print the following output:
MyClass 1__PHP_Incomplete_Class - a common mistake
This example starts a new session (this could implicitly happen by session.auto_start=true). If the object doesn't exist its class definition is loaded and a new object is stored into the session.
<?php
session_start();
if (! isset($_SESSION['myObject'])) {
require_once dirname(__FILE__) . '/MyClass.php';
$_SESSION['myObject'] = new MyClass();
}
echo get_class($_SESSION['myObject']), "\n";
echo $_SESSION['myObject']->increment(), "\n";
The second execution fails with a fatal error:
__PHP_Incomplete_Class Fatal error: main(): The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "MyClass" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition in fail.php on line 11
As the class MyClass is only defined if the session is empty,
PHP doesn't know the class defintion during deserialization of the session.
Instead of MyClass PHP creates an instance of
__PHP_Incomplete_Class. This incomplete object doesn't
know the semantic of increment().
PHP says in its error message how to repair this error. You have to take care that the class definition exists before the session start. The two next examples show solutions to this problem.
require 'MyClass.php' before session_start()
Incomplete objects are avoided by including the class definition before
session_start(). In our example we just move the
require_once dirname(__FILE__) . '/MyClass.php' out of the
if-block before the session_start().
<?php
require_once dirname(__FILE__) . '/MyClass.php';
session_start();
if (! isset($_SESSION['myObject'])) {
$_SESSION['myObject'] = new MyClass();
}
echo get_class($_SESSION['myObject']), "\n";
echo $_SESSION['myObject']->increment(), "\n";
A second execution prints now the expected output:
MyClass 1That's an easy constructed example. You could also imagine a usecase where you cannot predict the content of the session. A shopping cart might be such an example. The shopping cart might contain instances of different product classes. You could solve this problem by including all product classes before the session starts. But then you require memory for unused classes, which also consumes time for loading. The next example will solve this efficiency problem.
PHP Autoloader - class definitions on demand
The error message of the first example told already that an autoloader would also solve the problem. The next example doesn't load any class definition. This is done by the PHP Autoloader.
<?php
require dirname(__FILE__) . '/autoloader/Autoloader.php';
session_start();
if (! isset($_SESSION['myObject'])) {
$_SESSION['myObject'] = new MyClass();
}
echo get_class($_SESSION['myObject']), "\n";
echo $_SESSION['myObject']->increment(), "\n";
A second execution produces the correct output:
MyClass 1
The difference to the previous example is that only required class
definitions are loaded. In this example there is no real difference, as
the only loaded class is MyClass. But this solution makes a
big difference in the shopping cart usecase. The autoloader
solution loads only the required classes for the products in the shopping
cart.