Understanding objects
PHP 5 objects internal design
Hello everybody
 Julien PAULI
 Programming in PHP since ~10y
 PHP Internals reviewer
 PHP 5.5 and 5.6 Release Manager or Co-RM
 Working at SensioLabs in Paris
 http://www.phpinternalsbook.com
 @julienpauli - github.com/jpauli - jpauli@php.net
What we'll cover together
 Covering PHP 5 only
 Quick recall on zvals
 Object structures internally
 zend_object_value
 zend_class_entry
 zend_object_handlers
 zend_object_store
 PHP objects lifecycle
 Creating and destroying an object
 Memory management & garbage collection
Zvals
Zvals
 PHP variables can carry several types
 PHP is not strongly typed
 Type juggling
 Internally, all variables are represented into a
container which can carry all supported PHP
types : "zval"
Zvals
zval management
 Classic management example :
 Reference counting management example :
zval *myval;
ALLOC_INIT_ZVAL(myval); // malloc()
ZVAL_STRINGL(myval, "foobar", sizeof("foobar")-1, 1);
/* use myval */
zval_ptr_dtor(&myval);
Z_ADDREF_P(myval);
Z_DELREF_P(myval);
Z_SET_ISREF_P(myval);
Z_UNSET_ISREF_P(myval);
zvals refcount
 Every PHP variable is a zval :
 PHP does not duplicate memory when you
duplicate variables in the same scope
 It plays with the refcount of the zval
 refcount is how many variables point to the zval
<?php
$o = new MyClass;
<?php
$o = new MyClass; // refcount = 1
$o2 = $o; // refcount = 2
zvals refcount
 The zval is automatically freed when refcount
reaches zero, and never before
 When a zval is freed, its content is freed if not
shared elsewhere
 In our case : an object
<?php
$o = new MyClass; // refcount = 1
$o2 = $o; // refcount = 2
unset($o); // refcount = 1, zval is not freed
unset($o2); // refcount = 0, zval is freed
Statement
 Objects are variables
 Variables are zvals
 Objects are not freed until their zval's refcount
reaches zero
PHP objects
PHP Objects ?
 Objects are zvals of type IS_OBJECT
 The value field used is "obj" in the zval
 zend_object_value type
PHP objects details
 An object carries :
 A handle
 Some handlers
 The handle is a unique integer designed to
fetch the "real" object from an internal store
Showing the object handle
$o = new MyClass;
$a = $o;
$b = $o;
var_dump($a, $b, $o);
object(MyClass)#1 (0) {
}
object(MyClass)#1 (0) {
}
object(MyClass)#1 (0) {
}
Objects ARE NOT references
Objects are NOT references
 Simple proof
function foo($var) {
$var = 42;
}
$o = new MyClass;
foo($o);
var_dump($o);
object(MyClass)#1 (0) {
}
Objects borrow ref. behavior
 Because each variable (zval) encapsulates the
same object handle
function foo($var) {
$var->name = 'foo';
}
$o = new MyClass;
$o->name = 'bar';
foo($o);
var_dump($o);
object(MyClass)#1 (0) {
public $name =>
string(3) "foo"
}
zvals vs object handles
 This writes to the zval container $object :
 This changes the zval value
 Before it was an object, now it's a string
 The object that was into it has never been changed here
 This fetches the object using its handle, and
writes to that object :
 All other zvals using this same object handle are
affected, whatever their scope
$object = 'overwritten';
$object->var = 'changed';
Creating a new object
 The two only ways to create a new object in the
store are :
 new keyword (unserialize() may use new as well)
 clone keyword
$o = new MyClass;
$a = $o;
$a->name = 'foo';
$b = clone $a;
$c = $b;
$b->name = 'bar';
$a = 'string';
Object#1
name => "foo"
Object#1
name => "bar"
zval1
obj_handle => #1
Object#1
name => "bar"
zval2
'string'$b
$o
object storezval storesym tables
$a
$c zval3
obj_handle => #2
Object#2
name => "bar"
zval duplication with objects
 Even when you force PHP to duplicate a zval, if it represents an
object, this latter won't be copied :
 This is PHP5 behavior
 The objects are not duplicated, weither you use PHP references or not
 Zvals may get duplicated (if you abuse PHP references usage !)
 Objects themselves also carry a refcount
$o = new MyClass;
$a = &$o; // take a reference
/* Force PHP to duplicate the zval */
$b = $a;
/* We all agree that here, modifying $a or $b or $o
will modify the *same* object */
zval duplication and objects
 Even if you force PHP to dup. a zval container,
the object stored in it won't be dup.
$o = new MyClass;
$a = &$o;
$a->name = 'foo';
$b = $a;
Object#1
name => "foo"
Object#1
name => "bar"
zval1
obj_handle => #1
$b
$o
object storezval storesym tables
$a
zval2
obj_handle => #1
First step conclusion
 Having lots of variables pointing to the same
object is not bad for memory usage
 "clone", "new" and "unserialize" are the only
ways to create an object in the store
 Thus to consume more memory
 To free (destroy) an object from memory, one
must destroy all zvals in all scopes pointing to
it
 Keeping track of this can be hard
 use xdebug, master your code, remember
Garbage collector (GC)
Circular references GC
Statements :
 PHP garbage collector is NOT object specific
 It is zval based (any PHP type so)
 PHP GC is a circular references GC
 It's only goal is to free unfetchable circular
references from userland
 PHP has always freed unused zvals of which
refcount reached zero
 GC has nothing to do with this behavior
 PHP Circular references GC appeared in 5.3
Some circular references
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
zval1 zval2
refcount = 1
obj_handle => #2
refcount = 1
obj_handle => #1
Some circular references
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
zval1 zval2
refcount = 1
obj_handle => #2
refcount = 1
obj_handle => #1
Some circ.ref. cleaned by GC
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
echo gc_collect_cycles(); // 2
Objects circ.ref are common
 It is very easy to create a
circ.ref leak with objects
 This will have an impact if :
 Your objects are "heavy"
 You run long living process
 Ex are some SF2 commands
 ... with doctrine 2
class A
{
private $b;
function __construct() {
$this->b = new B($this);
}
}
class B
{
private $a;
function __construct(A $a) {
$this->a = $a;
}
}
$a = new A;
unset($a);
Diving into objects
zend_object type
 Objects in PHP are zend_object
 Objects live in a global "store"
 They are indexed using their unique handle
 As we've seen, PHP does all it can do not to
duplicate the object into the store
 Only way to duplicate : "clone"
zend_class_entry
 Represents a PHP class
or an interface
 By far the biggest
structure !
 This structure's been
shrinked to fit the slide
 The structure size is
~500 bytes
Object memory consumption
 Object declared attributes are stored once in the class
structure at compile time
 When you create an object (new), PHP will duplicate
the zval attributes from the class to the object
 The object now effectively owns its own copy of attributes
 zvals pointers are copied, not zval values. COW still effective
 Conclusion : An object weight is directly bound to its
attributes weight
 As class informations are shared between objects
Object memory consumption
 Every declared property is stored in the class structure
 They are stored with info structures
 Those also consume memory
 The class also embeds
 Its own static properties
 its own constants
 an array of interfaces it implements
 an array of traits it uses
 more info
 class consumes much more memory than an object
 But the same class is shared between all its children objects
Lifetimes
 Objects start living when you create them and
stop living when they are destroyed
 when the last zval pointing to the object is destroyed
 Classes start living when PHP starts parsing a
T_CLASS (class {) token
 Classes stop living when PHP shuts down (end
of request)
 Every class info, e.g its static members, have a
lifetime of the class itself
unset(MyClass::$variable);
Fatal error: Attempt to unset static property
Object handlers
Object handlers
 Every operation on objects is done by handlers
Object handlers
 Every single tiny operation on objects is done
using a handler (a redefinable function)
 For example
 calling a method on an object
 Fetching an object property
 But also :
 Casting an object (zend_object_cast_t)
 Comparing an object with something (zend_object_compare_t)
 ...
Object default handlers
 PHP uses default handlers
Object default handler example
 Default handlers implement default behavior
we all are used to :
static union _zend_function *zend_std_get_method(zval **object_ptr,
char *method_name, int method_len, const zend_literal *key TSRMLS_DC) {
// ...
if (UNEXPECTED(zend_hash_quick_find(&zobj->ce->function_table,
lc_method_name, method_len+1, hash_value, (void **)&fbc) == FAILURE)) {
if (zobj->ce->__call) {
return zend_get_user_call_function(zobj->ce, method_name, method_len);
} else {
return NULL;
}
}
Overriding object handlers
 You should know about "special behaving PHP
objects" don't you ?
Overriding object handlers
 You should know about "special behaving PHP
objects" don't you ?
 SimpleXmlElement
 PDOStatement
 DateTime
 ...
 They all redefine default handlers
And from PHP land ?
 You may overwrite some handlers from PHP
Land
 ArrayAccess
 Serializable
 Countable
 Designing a PHP extension, you may overwrite
any handler you want
 That's great
 Customize PHP object behavior
PHP OOP gotchas
construct. params strangeness
 Please, explain that behavior :
new StdClass($a=5);
var_dump($a);
PHP Notice : undefined variable $a
new DateTime($a='now');
var_dump($a);
string(3) "now"
Destructor secrets
 __destruct() is called when an object is
destroyed
 Destroyed == freeed (often)
 Reminder : An object is destroyed when no
more zvals point to it
$a = new SomeClass; // refcount = 1
/* calls __destruct() */
unset($a); // refcount reaches 0
Destructor secrets
 Let's now see what happens at shutdown
 When you don't destruct your objects yourself
 That's bad, you'll see
 When you leave PHP's shutdown clean your objects
 Yes, that's bad
$a = new SomeClass; // refcount = 1
/* Shutdown sequence */
Destructors at shutdown
 3-step shutdown
1
2
3
Shutdown and destructors #1
 PHP will loop backward the global symbol table
and destroy objects which zval's refcount = 1
 Last object created = first cleared
1 2 3
class Foo { public function __destruct() { var_dump("Destroyed Foo"); } }
class Bar { public function __destruct() { var_dump("Destroyed Bar"); } }
$a = new Foo,
$b = new Bar;
$a = new Bar,
$b = new Foo;
"Destroyed Bar"
"Destroyed Foo"
"Destroyed Foo"
"Destroyed Bar"
$a = new Bar,
$b = new Foo;
$c = $b;
"Destroyed Bar"
"Destroyed Foo"
Shutdown and destructors #2
 Then (for objects where refcount > 1) PHP will
loop forward the object store and destroy all
objects
 In order of their creation so (forward)
$a = new Foo;
$a2 = $a;
$b = new Bar;
$b2 = $b;
"Destroyed Foo"
"Destroyed Bar"
Shutdown and destructors #3
 If a destructor exit()s or die()s, the other
destructors are not executed
 But the objects are "marked as destructed"
$a = new Foo;
$a2 = $a;
$b = new Bar;
$b2 = $b;
"Destroyed Foo"
class Foo { public function __destruct() { var_dump("Destroyed Foo"); die(); } }
class Bar { public function __destruct() { var_dump("Destroyed Bar"); } }
__destruct() weirdness
 So, knowing that, we can meet weird behaviors
class Foo
{
public $state = 'alive';
function __destruct() {
$this->state = 'destructed';
}
}
class Bar
{
public $foo;
function __destruct() {
var_dump($this->foo->state);
}
}
$foo = new Foo;
$bar = new Bar;
$bar->foo = $foo;
$foo = new Foo;
$bar = new Bar;
$bar->foo = $foo;
$a = $bar;
"alive"
"destructed"
"Destroying" an object
 When you destroy an object, PHP will
immediatly free it
 When PHP destroys an object during shutdown
sequence, it will only call __destruct() and will
not free the object immediately
 That's why you can reuse "destroyed" objects
 See preceding slide
 The objects will be freed when the engine will
shutdown
$a = new SomeClass;
unset($a);
__destruct() in shutdown
 PHP's shutdown sequence is clear
 http://lxr.php.net/xref/PHP_5_4/main/main.c#1728
 1. call shutdown functions
 2. call object destructors
 3. end output buffering
 4. shutdown all extensions
 5. destroy superglobals
 6. shutdown scanner/executor/compiler
 Frees object storage
 Every object handling done after phase #2 can
lead to weird behavior and/or crash PHP
A great conclusion of this
 Don't rely on PHP's shutdown behavior
 It has changed throughout PHP versions
 It will change in the future
 It can make PHP hang or crash in worst cases
 Just destroy and free the resources yourself !
function stack serialized
 serializing an Exception serializes its stack trace
 Which itself could be not serializable ...
function foo(SimpleXMlElement $x, $a)
{
echo serialize(new Exception());
}
foo(new SimpleXmlElement('<a />'), 'a');
Fatal error: Uncaught exception 'Exception' with message
'Serialization of 'SimpleXMLElement' is not allowed'
Class Early Binding
 Early binding = compiler declares solo classes
 Inheritence is honnored at runtime
 Conditionnal declarations are honnored at runtime
 Declare your classes in the "right" order
 Use runtime autoloader
class C extends B {}
class B extends A {}
class A {}
Fatal error: Class 'B' not found
class C extends A {}
class A {}
/* all right */
Thank you for listening

Understanding PHP objects

  • 1.
    Understanding objects PHP 5objects internal design
  • 2.
    Hello everybody  JulienPAULI  Programming in PHP since ~10y  PHP Internals reviewer  PHP 5.5 and 5.6 Release Manager or Co-RM  Working at SensioLabs in Paris  http://www.phpinternalsbook.com  @julienpauli - github.com/jpauli - jpauli@php.net
  • 3.
    What we'll covertogether  Covering PHP 5 only  Quick recall on zvals  Object structures internally  zend_object_value  zend_class_entry  zend_object_handlers  zend_object_store  PHP objects lifecycle  Creating and destroying an object  Memory management & garbage collection
  • 4.
  • 5.
    Zvals  PHP variablescan carry several types  PHP is not strongly typed  Type juggling  Internally, all variables are represented into a container which can carry all supported PHP types : "zval"
  • 6.
  • 7.
    zval management  Classicmanagement example :  Reference counting management example : zval *myval; ALLOC_INIT_ZVAL(myval); // malloc() ZVAL_STRINGL(myval, "foobar", sizeof("foobar")-1, 1); /* use myval */ zval_ptr_dtor(&myval); Z_ADDREF_P(myval); Z_DELREF_P(myval); Z_SET_ISREF_P(myval); Z_UNSET_ISREF_P(myval);
  • 8.
    zvals refcount  EveryPHP variable is a zval :  PHP does not duplicate memory when you duplicate variables in the same scope  It plays with the refcount of the zval  refcount is how many variables point to the zval <?php $o = new MyClass; <?php $o = new MyClass; // refcount = 1 $o2 = $o; // refcount = 2
  • 9.
    zvals refcount  Thezval is automatically freed when refcount reaches zero, and never before  When a zval is freed, its content is freed if not shared elsewhere  In our case : an object <?php $o = new MyClass; // refcount = 1 $o2 = $o; // refcount = 2 unset($o); // refcount = 1, zval is not freed unset($o2); // refcount = 0, zval is freed
  • 10.
    Statement  Objects arevariables  Variables are zvals  Objects are not freed until their zval's refcount reaches zero
  • 11.
  • 12.
    PHP Objects ? Objects are zvals of type IS_OBJECT  The value field used is "obj" in the zval  zend_object_value type
  • 13.
    PHP objects details An object carries :  A handle  Some handlers  The handle is a unique integer designed to fetch the "real" object from an internal store
  • 14.
    Showing the objecthandle $o = new MyClass; $a = $o; $b = $o; var_dump($a, $b, $o); object(MyClass)#1 (0) { } object(MyClass)#1 (0) { } object(MyClass)#1 (0) { }
  • 15.
    Objects ARE NOTreferences
  • 16.
    Objects are NOTreferences  Simple proof function foo($var) { $var = 42; } $o = new MyClass; foo($o); var_dump($o); object(MyClass)#1 (0) { }
  • 17.
    Objects borrow ref.behavior  Because each variable (zval) encapsulates the same object handle function foo($var) { $var->name = 'foo'; } $o = new MyClass; $o->name = 'bar'; foo($o); var_dump($o); object(MyClass)#1 (0) { public $name => string(3) "foo" }
  • 18.
    zvals vs objecthandles  This writes to the zval container $object :  This changes the zval value  Before it was an object, now it's a string  The object that was into it has never been changed here  This fetches the object using its handle, and writes to that object :  All other zvals using this same object handle are affected, whatever their scope $object = 'overwritten'; $object->var = 'changed';
  • 19.
    Creating a newobject  The two only ways to create a new object in the store are :  new keyword (unserialize() may use new as well)  clone keyword $o = new MyClass; $a = $o; $a->name = 'foo'; $b = clone $a; $c = $b; $b->name = 'bar'; $a = 'string'; Object#1 name => "foo" Object#1 name => "bar" zval1 obj_handle => #1 Object#1 name => "bar" zval2 'string'$b $o object storezval storesym tables $a $c zval3 obj_handle => #2 Object#2 name => "bar"
  • 20.
    zval duplication withobjects  Even when you force PHP to duplicate a zval, if it represents an object, this latter won't be copied :  This is PHP5 behavior  The objects are not duplicated, weither you use PHP references or not  Zvals may get duplicated (if you abuse PHP references usage !)  Objects themselves also carry a refcount $o = new MyClass; $a = &$o; // take a reference /* Force PHP to duplicate the zval */ $b = $a; /* We all agree that here, modifying $a or $b or $o will modify the *same* object */
  • 21.
    zval duplication andobjects  Even if you force PHP to dup. a zval container, the object stored in it won't be dup. $o = new MyClass; $a = &$o; $a->name = 'foo'; $b = $a; Object#1 name => "foo" Object#1 name => "bar" zval1 obj_handle => #1 $b $o object storezval storesym tables $a zval2 obj_handle => #1
  • 22.
    First step conclusion Having lots of variables pointing to the same object is not bad for memory usage  "clone", "new" and "unserialize" are the only ways to create an object in the store  Thus to consume more memory  To free (destroy) an object from memory, one must destroy all zvals in all scopes pointing to it  Keeping track of this can be hard  use xdebug, master your code, remember
  • 23.
  • 24.
  • 25.
    Statements :  PHPgarbage collector is NOT object specific  It is zval based (any PHP type so)  PHP GC is a circular references GC  It's only goal is to free unfetchable circular references from userland  PHP has always freed unused zvals of which refcount reached zero  GC has nothing to do with this behavior  PHP Circular references GC appeared in 5.3
  • 26.
    Some circular references $a= new StdClass; $b = new StdClass; $a->b = $b; $b->a = $a; unset($a,$b); zval1 zval2 refcount = 1 obj_handle => #2 refcount = 1 obj_handle => #1
  • 27.
    Some circular references $a= new StdClass; $b = new StdClass; $a->b = $b; $b->a = $a; unset($a,$b); zval1 zval2 refcount = 1 obj_handle => #2 refcount = 1 obj_handle => #1
  • 28.
    Some circ.ref. cleanedby GC $a = new StdClass; $b = new StdClass; $a->b = $b; $b->a = $a; unset($a,$b); echo gc_collect_cycles(); // 2
  • 29.
    Objects circ.ref arecommon  It is very easy to create a circ.ref leak with objects  This will have an impact if :  Your objects are "heavy"  You run long living process  Ex are some SF2 commands  ... with doctrine 2 class A { private $b; function __construct() { $this->b = new B($this); } } class B { private $a; function __construct(A $a) { $this->a = $a; } } $a = new A; unset($a);
  • 30.
  • 31.
    zend_object type  Objectsin PHP are zend_object  Objects live in a global "store"  They are indexed using their unique handle  As we've seen, PHP does all it can do not to duplicate the object into the store  Only way to duplicate : "clone"
  • 32.
    zend_class_entry  Represents aPHP class or an interface  By far the biggest structure !  This structure's been shrinked to fit the slide  The structure size is ~500 bytes
  • 33.
    Object memory consumption Object declared attributes are stored once in the class structure at compile time  When you create an object (new), PHP will duplicate the zval attributes from the class to the object  The object now effectively owns its own copy of attributes  zvals pointers are copied, not zval values. COW still effective  Conclusion : An object weight is directly bound to its attributes weight  As class informations are shared between objects
  • 34.
    Object memory consumption Every declared property is stored in the class structure  They are stored with info structures  Those also consume memory  The class also embeds  Its own static properties  its own constants  an array of interfaces it implements  an array of traits it uses  more info  class consumes much more memory than an object  But the same class is shared between all its children objects
  • 35.
    Lifetimes  Objects startliving when you create them and stop living when they are destroyed  when the last zval pointing to the object is destroyed  Classes start living when PHP starts parsing a T_CLASS (class {) token  Classes stop living when PHP shuts down (end of request)  Every class info, e.g its static members, have a lifetime of the class itself unset(MyClass::$variable); Fatal error: Attempt to unset static property
  • 36.
  • 37.
    Object handlers  Everyoperation on objects is done by handlers
  • 38.
    Object handlers  Everysingle tiny operation on objects is done using a handler (a redefinable function)  For example  calling a method on an object  Fetching an object property  But also :  Casting an object (zend_object_cast_t)  Comparing an object with something (zend_object_compare_t)  ...
  • 39.
    Object default handlers PHP uses default handlers
  • 40.
    Object default handlerexample  Default handlers implement default behavior we all are used to : static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) { // ... if (UNEXPECTED(zend_hash_quick_find(&zobj->ce->function_table, lc_method_name, method_len+1, hash_value, (void **)&fbc) == FAILURE)) { if (zobj->ce->__call) { return zend_get_user_call_function(zobj->ce, method_name, method_len); } else { return NULL; } }
  • 41.
    Overriding object handlers You should know about "special behaving PHP objects" don't you ?
  • 42.
    Overriding object handlers You should know about "special behaving PHP objects" don't you ?  SimpleXmlElement  PDOStatement  DateTime  ...  They all redefine default handlers
  • 43.
    And from PHPland ?  You may overwrite some handlers from PHP Land  ArrayAccess  Serializable  Countable  Designing a PHP extension, you may overwrite any handler you want  That's great  Customize PHP object behavior
  • 44.
  • 45.
    construct. params strangeness Please, explain that behavior : new StdClass($a=5); var_dump($a); PHP Notice : undefined variable $a new DateTime($a='now'); var_dump($a); string(3) "now"
  • 46.
    Destructor secrets  __destruct()is called when an object is destroyed  Destroyed == freeed (often)  Reminder : An object is destroyed when no more zvals point to it $a = new SomeClass; // refcount = 1 /* calls __destruct() */ unset($a); // refcount reaches 0
  • 47.
    Destructor secrets  Let'snow see what happens at shutdown  When you don't destruct your objects yourself  That's bad, you'll see  When you leave PHP's shutdown clean your objects  Yes, that's bad $a = new SomeClass; // refcount = 1 /* Shutdown sequence */
  • 48.
    Destructors at shutdown 3-step shutdown 1 2 3
  • 49.
    Shutdown and destructors#1  PHP will loop backward the global symbol table and destroy objects which zval's refcount = 1  Last object created = first cleared 1 2 3 class Foo { public function __destruct() { var_dump("Destroyed Foo"); } } class Bar { public function __destruct() { var_dump("Destroyed Bar"); } } $a = new Foo, $b = new Bar; $a = new Bar, $b = new Foo; "Destroyed Bar" "Destroyed Foo" "Destroyed Foo" "Destroyed Bar" $a = new Bar, $b = new Foo; $c = $b; "Destroyed Bar" "Destroyed Foo"
  • 50.
    Shutdown and destructors#2  Then (for objects where refcount > 1) PHP will loop forward the object store and destroy all objects  In order of their creation so (forward) $a = new Foo; $a2 = $a; $b = new Bar; $b2 = $b; "Destroyed Foo" "Destroyed Bar"
  • 51.
    Shutdown and destructors#3  If a destructor exit()s or die()s, the other destructors are not executed  But the objects are "marked as destructed" $a = new Foo; $a2 = $a; $b = new Bar; $b2 = $b; "Destroyed Foo" class Foo { public function __destruct() { var_dump("Destroyed Foo"); die(); } } class Bar { public function __destruct() { var_dump("Destroyed Bar"); } }
  • 52.
    __destruct() weirdness  So,knowing that, we can meet weird behaviors class Foo { public $state = 'alive'; function __destruct() { $this->state = 'destructed'; } } class Bar { public $foo; function __destruct() { var_dump($this->foo->state); } } $foo = new Foo; $bar = new Bar; $bar->foo = $foo; $foo = new Foo; $bar = new Bar; $bar->foo = $foo; $a = $bar; "alive" "destructed"
  • 53.
    "Destroying" an object When you destroy an object, PHP will immediatly free it  When PHP destroys an object during shutdown sequence, it will only call __destruct() and will not free the object immediately  That's why you can reuse "destroyed" objects  See preceding slide  The objects will be freed when the engine will shutdown $a = new SomeClass; unset($a);
  • 54.
    __destruct() in shutdown PHP's shutdown sequence is clear  http://lxr.php.net/xref/PHP_5_4/main/main.c#1728  1. call shutdown functions  2. call object destructors  3. end output buffering  4. shutdown all extensions  5. destroy superglobals  6. shutdown scanner/executor/compiler  Frees object storage  Every object handling done after phase #2 can lead to weird behavior and/or crash PHP
  • 55.
    A great conclusionof this  Don't rely on PHP's shutdown behavior  It has changed throughout PHP versions  It will change in the future  It can make PHP hang or crash in worst cases  Just destroy and free the resources yourself !
  • 56.
    function stack serialized serializing an Exception serializes its stack trace  Which itself could be not serializable ... function foo(SimpleXMlElement $x, $a) { echo serialize(new Exception()); } foo(new SimpleXmlElement('<a />'), 'a'); Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'SimpleXMLElement' is not allowed'
  • 57.
    Class Early Binding Early binding = compiler declares solo classes  Inheritence is honnored at runtime  Conditionnal declarations are honnored at runtime  Declare your classes in the "right" order  Use runtime autoloader class C extends B {} class B extends A {} class A {} Fatal error: Class 'B' not found class C extends A {} class A {} /* all right */
  • 58.
    Thank you forlistening