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 ...
What we'll cover together
 Covering PHP 5 only
 Quick recall on zvals
 Object structures internally
 zend_object_value...
Zvals
Zvals
 PHP variables can carry several types
 PHP is not strongly typed
 Type juggling
 Internally, all variables are ...
Zvals
zval management
 Classic management example :
 Reference counting management example :
zval *myval;
ALLOC_INIT_ZVAL(myva...
zvals refcount
 Every PHP variable is a zval :
 PHP does not duplicate memory when you
duplicate variables in the same s...
zvals refcount
 The zval is automatically freed when refcount
reaches zero, and never before
 When a zval is freed, its ...
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 th...
Showing the object handle
$o = new MyClass;
$a = $o;
$b = $o;
var_dump($a, $b, $o);
object(MyClass)#1 (0) {
}
object(MyCla...
Objects ARE NOT references
Objects are NOT references
 Simple proof
function foo($var) {
$var = 42;
}
$o = new MyClass;
foo($o);
var_dump($o);
objec...
Objects borrow ref. behavior
 Because each variable (zval) encapsulates the
same object handle
function foo($var) {
$var-...
zvals vs object handles
 This writes to the zval container $object :
 This changes the zval value
 Before it was an obj...
Creating a new object
 The two only ways to create a new object in the
store are :
 new keyword (unserialize() may use n...
zval duplication with objects
 Even when you force PHP to duplicate a zval, if it represents an
object, this latter won't...
zval duplication and objects
 Even if you force PHP to dup. a zval container,
the object stored in it won't be dup.
$o = ...
First step conclusion
 Having lots of variables pointing to the same
object is not bad for memory usage
 "clone", "new" ...
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 re...
Some circular references
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
zval1 zval2
refcount ...
Some circular references
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
zval1 zval2
refcount ...
Some circ.ref. cleaned by GC
$a = new StdClass;
$b = new StdClass;
$a->b = $b;
$b->a = $a;
unset($a,$b);
echo gc_collect_c...
Objects circ.ref are common
 It is very easy to create a
circ.ref leak with objects
 This will have an impact if :
 You...
Diving into objects
zend_object type
 Objects in PHP are zend_object
 Objects live in a global "store"
 They are indexed using their unique...
zend_class_entry
 Represents a PHP class
or an interface
 By far the biggest
structure !
 This structure's been
shrinke...
Object memory consumption
 Object declared attributes are stored once in the class
structure at compile time
 When you c...
Object memory consumption
 Every declared property is stored in the class structure
 They are stored with info structure...
Lifetimes
 Objects start living when you create them and
stop living when they are destroyed
 when the last zval pointin...
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
 ...
Object default handlers
 PHP uses default handlers
Object default handler example
 Default handlers implement default behavior
we all are used to :
static union _zend_funct...
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
 PDOStat...
And from PHP land ?
 You may overwrite some handlers from PHP
Land
 ArrayAccess
 Serializable
 Countable
 Designing a...
PHP OOP gotchas
construct. params strangeness
 Please, explain that behavior :
new StdClass($a=5);
var_dump($a);
PHP Notice : undefined v...
Destructor secrets
 __destruct() is called when an object is
destroyed
 Destroyed == freeed (often)
 Reminder : An obje...
Destructor secrets
 Let's now see what happens at shutdown
 When you don't destruct your objects yourself
 That's bad, ...
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...
Shutdown and destructors #2
 Then (for objects where refcount > 1) PHP will
loop forward the object store and destroy all...
Shutdown and destructors #3
 If a destructor exit()s or die()s, the other
destructors are not executed
 But the objects ...
__destruct() weirdness
 So, knowing that, we can meet weird behaviors
class Foo
{
public $state = 'alive';
function __des...
"Destroying" an object
 When you destroy an object, PHP will
immediatly free it
 When PHP destroys an object during shut...
__destruct() in shutdown
 PHP's shutdown sequence is clear
 http://lxr.php.net/xref/PHP_5_4/main/main.c#1728
 1. call s...
A great conclusion of this
 Don't rely on PHP's shutdown behavior
 It has changed throughout PHP versions
 It will chan...
function stack serialized
 serializing an Exception serializes its stack trace
 Which itself could be not serializable ....
Class Early Binding
 Early binding = compiler declares solo classes
 Inheritence is honnored at runtime
 Conditionnal d...
Thank you for listening
Prochain SlideShare
Chargement dans…5
×

Understanding PHP objects

10 686 vues

Publié le

Publié dans : Technologie
0 commentaire
21 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
10 686
Sur SlideShare
0
Issues des intégrations
0
Intégrations
161
Actions
Partages
0
Téléchargements
0
Commentaires
0
J’aime
21
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Understanding PHP objects

  1. 1. Understanding objects PHP 5 objects internal design
  2. 2. 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
  3. 3. 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
  4. 4. Zvals
  5. 5. 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"
  6. 6. Zvals
  7. 7. 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);
  8. 8. 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
  9. 9. 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
  10. 10. Statement  Objects are variables  Variables are zvals  Objects are not freed until their zval's refcount reaches zero
  11. 11. PHP objects
  12. 12. PHP Objects ?  Objects are zvals of type IS_OBJECT  The value field used is "obj" in the zval  zend_object_value type
  13. 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. 14. 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) { }
  15. 15. Objects ARE NOT references
  16. 16. Objects are NOT references  Simple proof function foo($var) { $var = 42; } $o = new MyClass; foo($o); var_dump($o); object(MyClass)#1 (0) { }
  17. 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. 18. 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';
  19. 19. 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"
  20. 20. 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 */
  21. 21. 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
  22. 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. 23. Garbage collector (GC)
  24. 24. Circular references GC
  25. 25. 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
  26. 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. 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. 28. 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
  29. 29. 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);
  30. 30. Diving into objects
  31. 31. 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"
  32. 32. 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
  33. 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. 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. 35. 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
  36. 36. Object handlers
  37. 37. Object handlers  Every operation on objects is done by handlers
  38. 38. 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)  ...
  39. 39. Object default handlers  PHP uses default handlers
  40. 40. 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; } }
  41. 41. Overriding object handlers  You should know about "special behaving PHP objects" don't you ?
  42. 42. Overriding object handlers  You should know about "special behaving PHP objects" don't you ?  SimpleXmlElement  PDOStatement  DateTime  ...  They all redefine default handlers
  43. 43. 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
  44. 44. PHP OOP gotchas
  45. 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. 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. 47. 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 */
  48. 48. Destructors at shutdown  3-step shutdown 1 2 3
  49. 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. 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. 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. 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. 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. 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. 55. 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 !
  56. 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. 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. 58. Thank you for listening

×