Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Understanding PHP objects

12,117 views

Published on

Published in: Technology

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

×