Understanding PHP objects

  • 7,357 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
7,357
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
0
Comments
0
Likes
7

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Understanding objects PHP objects internal design
  • 2. Hello everybody  Julien PAULI  Programming in PHP since ~10y  PHP Internals reviewer since ~3y  PHP 5.5 and 5.6 Release Manager  Working at SensioLabs in Paris  http://www.phpinternalsbook.com  @julienpauli - github.com/jpauli - jpauli@php.net
  • 3. What we'll cover together  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. Zvals
  • 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. Zvals
  • 7. zval management  Classic management example : zval *myval; ALLOC_INIT_ZVAL(myval); // malloc() ZVAL_STRINGL(myval, "foobar", sizeof("foobar")-1, 1); /* use myval */ zval_ptr_dtor(&myval);  Reference counting management example : Z_ADDREF_P(myval); Z_DELREF_P(myval); Z_SET_ISREF_P(myval); Z_UNSET_ISREF_P(myval);
  • 8. zvals refcount  Every PHP variable is a zval : <?php $o = new MyClass;  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; // refcount = 1 $o2 = $o; // refcount = 2
  • 9. zvals refcount  The zval is automatically freed when refcount reaches zero, and never before <?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  When a zval is freed, its content is freed if not shared elsewhere  In our case : an object
  • 10. Statement  Objects are variables  Variables are zvals  Objects are not freed until their zval's refcount reaches zero
  • 11. PHP objects
  • 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 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. Objects ARE NOT references
  • 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. 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 object handles  This writes to the zval container $object : $object = 'overwritten';  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 : $object->var = 'changed';  All other zvals using this same object handle are affected, whatever their scope
  • 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 sym tables $o = new MyClass; $a = $o; $a->name = 'foo'; $a = 'string'; $o $a zval store zval1 Object#1 obj_handle"bar" name => => #1 object store Object#1 name => "foo" Object#1 zval2 $b = clone $a; $c = $b; $b->name = 'bar'; $b $c name => "bar" 'string' zval3 obj_handle => #2 Object#2 name => "bar"
  • 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 : $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 */  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
  • 21. zval duplication and objects  Even if you force PHP to dup. a zval container, the object stored in it won't be dup. sym tables $o = new MyClass; $a = &$o; $a->name = 'foo'; $b = $a; $o $a zval store zval1 Object#1 obj_handle"bar" name => => #1 zval2 $b obj_handle => #1 object store Object#1 name => "foo"
  • 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. Garbage collector (GC)
  • 24. Circular references GC
  • 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. Some circular references $a = new StdClass; $b = new StdClass; $a->b = $b; $b->a = $a; unset($a,$b); zval1 zval2 refcount = 1 obj_handle => #1 refcount = 1 obj_handle => #2
  • 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 => #1 refcount = 1 obj_handle => #2
  • 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. 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. Diving into objects
  • 31. zend_object type  Objects in PHP are zend_object  Objects live in a global "store"  They are indexed using there 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 a PHP class or an interface  By far the biggest structure !  This structure's been shrinked to fit the slide
  • 33. Object memory consumption  Object declared attributes are stored once in the class structure at compile time  At the first time you try to read any declared property, PHP will duplicate all them from the class structure to the object structure  This effectively make those attributes owned by the object : a "private" copy is made, tied to the object  Conclusion : An object weigth is directly bound to its attributes weigth  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  A class consumes much more memory than an object  But the same class is shared between all its children objects
  • 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. Object handlers
  • 37. Object handlers  Every operation on objects is done by handlers
  • 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. Object default handlers  PHP uses default handlers
  • 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. 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 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. PHP OOP gotchas
  • 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  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'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. 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 class Foo { public function __destruct() { var_dump("Destroyed Foo"); } } class Bar { public function __destruct() { var_dump("Destroyed Bar"); } } 1 2 3 $a = new Foo, $b = new Bar; $a = new Bar, $b = new Foo; $a = new Bar, $b = new Foo; $c = $b; "Destroyed Bar" "Destroyed Foo" "Destroyed Foo" "Destroyed Bar" "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 exectued  But the objects are "marked as destructed" class Foo { public function __destruct() { var_dump("Destroyed Foo"); die(); } } class Bar { public function __destruct() { var_dump("Destroyed Bar"); } } $a = new Foo; $a2 = $a; $b = new Bar; $b2 = $b; "Destroyed Foo"
  • 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; "alive" $foo = new Foo; $bar = new Bar; $bar->foo = $foo; $a = $bar; "destructed"
  • 53. "Destroying" an object  When you destroy an object, PHP will immediatly free it $a = new SomeClass; unset($a);  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
  • 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 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. function stack serialized 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'  serializing an Exception serializes its stack trace  Which itself could be not serializable ...
  • 57. Class Early Binding class C extends B {} class B extends A {} class A {} Fatal error: Class 'B' not found class C extends A {} class A {} /* all right */  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
  • 58. Thank you for listening