Understanding PHP objects

9,206
-1

Published on

Published in: Technology
0 Comments
17 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
9,206
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
0
Comments
0
Likes
17
Embeds 0
No embeds

No notes for slide

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

×