Successfully reported this slideshow.
Your SlideShare is downloading. ×

PHP Language Trivia

Ad

PHP Language Trivia
Nikita Popov
(nikic)

Ad

Object properties

Ad

Object properties
Name mangling

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Loading in …3
×

Check these out next

1 of 77 Ad
1 of 77 Ad
Advertisement

More Related Content

Advertisement

PHP Language Trivia

  1. 1. PHP Language Trivia Nikita Popov (nikic)
  2. 2. Object properties
  3. 3. Object properties Name mangling
  4. 4. class Test { public $pub = 1; protected $prot = 2; private $priv = 3; } $obj = new Test; $arr = (array) $obj; var_dump($arr);
  5. 5. class Test { public $pub = 1; protected $prot = 2; private $priv = 3; } $obj = new Test; $arr = (array) $obj; var_dump($arr); array(3) { ["pub"]=> int(1) ["*prot"]=> int(2) ["Testpriv"]=> int(3) }
  6. 6. class Test { public $pub = 1; protected $prot = 2; private $priv = 3; } $obj = new Test; $arr = (array) $obj; var_dump($arr); var_dump($arr["*prot"]); // Notice: Undefined index: *prot array(3) { ["pub"]=> int(1) ["*prot"]=> int(2) ["Testpriv"]=> int(3) }
  7. 7. class Test { public $pub = 1; protected $prot = 2; private $priv = 3; } $obj = new Test; $arr = (array) $obj; var_dump($arr); var_dump($arr["0*0prot"]); // int(2) array(3) { ["pub"]=> int(1) ["0*0prot"]=> int(2) ["0Test0priv"]=> int(3) }
  8. 8. Why name mangling?
  9. 9. class A { private $prop = 'A'; public function getPropA() { return $this->prop; } } class B extends A { protected $prop = 'B'; public function getPropB() { return $this->prop; } } class C extends B { public $prop = 'C'; public function getPropC() { return $this->prop; } } $obj = new C; var_dump($obj->getPropA()); // string(1) "A" var_dump($obj->getPropB()); // string(1) "C" var_dump($obj->getPropC()); // string(1) "C"
  10. 10. class A { private $prop = 'A'; public function getPropA() { return $this->prop; } } class B extends A { protected $prop = 'B'; public function getPropB() { return $this->prop; } } class C extends B { public $prop = 'C'; public function getPropC() { return $this->prop; } } $obj = new C; var_dump($obj->getPropA()); // string(1) "A" var_dump($obj->getPropB()); // string(1) "C" var_dump($obj->getPropC()); // string(1) "C" Refer to same property
  11. 11. class A { private $prop = 'A'; public function getPropA() { return $this->prop; } } class B extends A { protected $prop = 'B'; public function getPropB() { return $this->prop; } } class C extends B { public $prop = 'C'; public function getPropC() { return $this->prop; } } $obj = new C; var_dump($obj->getPropA()); // string(1) "A" var_dump($obj->getPropB()); // string(1) "C" var_dump($obj->getPropC()); // string(1) "C" Refer to same property Private property is independent
  12. 12. class A { private $prop = 'A'; public function getPropA() { return $this->prop; } } class B extends A { protected $prop = 'B'; public function getPropB() { return $this->prop; } } class C extends B { public $prop = 'C'; public function getPropC() { return $this->prop; } } $obj = new C; var_dump((array) $obj); Refer to same property Private property is independent
  13. 13. class A { private $prop = 'A'; public function getPropA() { return $this->prop; } } class B extends A { protected $prop = 'B'; public function getPropB() { return $this->prop; } } class C extends B { public $prop = 'C'; public function getPropC() { return $this->prop; } } $obj = new C; var_dump((array) $obj); array(2) { ["0A0prop"]=> string(1) "A" ["prop"]=> string(1) "C" } Refer to same property Private property is independent
  14. 14. Object can have multiple properties with same name Name mangling ensures unique property names
  15. 15. No reason to expose this internal detail …
  16. 16. No reason to expose this internal detail … … but libraries rely on it now to access private properties
  17. 17. Object properties Integer property names
  18. 18. $array = []; $array[123] = "foo"; $array["123"] = "bar"; var_dump($array); array(1) { [123]=> string(3) "bar" }
  19. 19. $array = []; $array[123] = "foo"; $array["123"] = "bar"; var_dump($array); array(1) { [123]=> string(3) "bar" } $object = new stdClass; $object->{123} = "foo"; $object->{"123"} = "bar"; var_dump($object); object(stdClass)#1 (1) { ["123"]=> string(3) "bar" }
  20. 20. $array = []; $array[123] = "foo"; $array["123"] = "bar"; var_dump($array); array(1) { [123]=> string(3) "bar" } $object = new stdClass; $object->{123} = "foo"; $object->{"123"} = "bar"; var_dump($object); object(stdClass)#1 (1) { ["123"]=> string(3) "bar" } Normalize to int Normalize to string
  21. 21. $array = []; $array[123] = "foo"; $array["123"] = "bar"; var_dump($array); array(1) { [123]=> string(3) "bar" } $object = new stdClass; $object->{123} = "foo"; $object->{"123"} = "bar"; var_dump($object); object(stdClass)#1 (1) { ["123"]=> string(3) "bar" } Normalize to int Normalize to string What happens if we mix both?
  22. 22. $array = [123 => "foo"]; $object = (object) $array; var_dump($object->{123}); // Notice: Undefined property: stdClass::$123 var_dump($object->{"123"}); // Notice: Undefined property: stdClass::$123
  23. 23. $array = [123 => "foo"]; $object = (object) $array; var_dump($object->{123}); // Notice: Undefined property: stdClass::$123 var_dump($object->{"123"}); // Notice: Undefined property: stdClass::$123 var_dump($object); object(stdClass)#1 (1) { [123]=> string(3) "foo" }
  24. 24. $array = [123 => "foo"]; $object = (object) $array; var_dump($object->{123}); // Notice: Undefined property: stdClass::$123 var_dump($object->{"123"}); // Notice: Undefined property: stdClass::$123 var_dump($object); object(stdClass)#1 (1) { [123]=> string(3) "foo" } Unnormalized integer property name
  25. 25. $object = new stdClass; $object->{123} = "foo"; $array = (array) $object; var_dump($array[123]); // Notice: Undefined offset: 123 var_dump($array["123"]); // Notice: Undefined offset: 123
  26. 26. $object = new stdClass; $object->{123} = "foo"; $array = (array) $object; var_dump($array[123]); // Notice: Undefined offset: 123 var_dump($array["123"]); // Notice: Undefined offset: 123 var_dump($array); array(1) { ["123"]=> string(3) "foo" } Unnormalized integral string key
  27. 27. Fixed in PHP 7.2! Now integer keys are renormalized on array->object and object->array casts
  28. 28. $array = [123 => "foo"]; $object = (object) $array; var_dump($object->{123}); string(3) "foo" $object = new stdClass; $object->{123} = "foo"; $array = (array) $object; var_dump($array[123]); string(3) "foo"
  29. 29. Object properties Memory usage
  30. 30. $array = [ "key1" => 1, "key2" => 2, // ... ]; class Value { public $key1; public $key2; } $object = new Value; $object->key1 = 1; $object->key2 = 2; vs.
  31. 31. 0 100 200 300 400 500 600 700 800 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Memoryusage(bytes) Number of properties/keys Array Array (real) Object Object (real)
  32. 32. 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Arraysize/objectsize Number of properties/keys Ratio Ratio (real)
  33. 33. Optimized for different usecases Objects: Good for fixed set of keys Arrays: Good for dynamic set of keys
  34. 34. Class entry Property 0 Property 1 … Object
  35. 35. Class entry Property 0 Property 1 … Object Contains [property name => property offset] map
  36. 36. Class entry Properties array Property 0 Property 1 … Object Contains [property name => property offset] map [property name => property value] map, used if there are dynamic properties
  37. 37. Class entry Properties array Property 0 Property 1 … Object Contains [property name => property offset] map [property name => property value] map, used if there are dynamic properties Arrays: • Store keys (and hashes) explicitly • Always have power of two size (8, 16, …) for faster insertions
  38. 38. class Value { public $x; } $obj = new Value; // $obj size: 56 bytes foreach ($obj as $k => $v) { } // $obj size: 432 bytes
  39. 39. class Value { public $x; } $obj = new Value; // $obj size: 56 bytes foreach ($obj as $k => $v) { } // $obj size: 432 bytes Forces creation of properties array
  40. 40. class Value { public $x; } $obj = new Value; // $obj size: 56 bytes foreach ($obj as $k => $v) { } // $obj size: 432 bytes Forces creation of properties array … no way to get rid of it afterwards
  41. 41. // PhpParser node iteration $names = $node->getSubNodeNames(); foreach ($names as $name) { $value = $node->$name; }
  42. 42. // PhpParser node iteration $names = $node->getSubNodeNames(); foreach ($names as $name) { $value = $node->$name; } Dynamic lookup is slow, but this avoids large memory usage increase
  43. 43. Object properties Magic get & set
  44. 44. Direct property access baseline getProperty() method 2.2x slower __get() magic 6.0x slower
  45. 45. Direct property access baseline getProperty() method 2.2x slower __get() magic 6.0x slower Userland  internal  userland is slow
  46. 46. class Test { public function __get($name) { return $this->$name; } }
  47. 47. class Test { public function __get($name) { return $this->$name; } } Does not recurse into __get() Will access property directly
  48. 48. class Test { public function __get($name) { return $this->$name; } } Does not recurse into __get() Will access property directly Recursion guards are property name + accessor type specific
  49. 49. class Test { public function __get($name) { return $this->$name; } } Does not recurse into __get() Will access property directly Recursion guards are property name + accessor type specific In __get("foo"): • $this->foo will access property • $this->bar will call __get("bar") • $this->foo = 42 will call __set("foo", 42)
  50. 50. __get("foo") __get("bar") __set("bar", 42) Recursion guards: [ "foo" => GET, "bar" => GET|SET, ]
  51. 51. __get("foo") __get("bar") [ "foo" => GET, "bar" => GET, ] Recursion guards:
  52. 52. __get("foo") [ "foo" => GET, "bar" => 0, ] Recursion guards:
  53. 53. [ "foo" => 0, "bar" => 0, ] Recursion guards:
  54. 54. [ "foo" => 0, "bar" => 0, ] Recursion guards: Never cleaned up
  55. 55. [ "foo" => 0, "bar" => 0, ] Recursion guards: Never cleaned up PHP 7.1: Recursion guard array not used if magic accessors used only for one property at a time
  56. 56. Object properties Unset properties
  57. 57. class Test { public $prop; } $obj = new Test; unset($obj->prop); var_dump($obj->prop); // Notice: Undefined property: Test::$prop
  58. 58. class Test { public $prop; } $obj = new Test; unset($obj->prop); var_dump($obj->prop); // Notice: Undefined property: Test::$prop Once unset, __get() will be called on access -> Lazy initialization
  59. 59. class Test { public $prop; public function __construct() { unset($this->prop); } public function __get($name) { echo "__get($name)n"; $this->$name = "init"; return $this->$name; } } $obj = new Test; var_dump($obj->prop); var_dump($obj->prop);
  60. 60. class Test { public $prop; public function __construct() { unset($this->prop); } public function __get($name) { echo "__get($name)n"; $this->$name = "init"; return $this->$name; } } $obj = new Test; var_dump($obj->prop); var_dump($obj->prop); Calls __get() Does not call __get()
  61. 61. Scoped calls
  62. 62. Foo::bar() Static method call … or is it?
  63. 63. class A { public function method() { /* ... */ } } class B extends A { public function method() { parent::method(); /* ... */ } }
  64. 64. class A { public function method() { /* ... */ } } class B extends A { public function method() { A::method(); /* ... */ } }
  65. 65. class A { public function method() { /* ... */ } } class B extends A { public function method() { A::method(); /* ... */ } } Scoped instance call: Call A::method() with current $this
  66. 66. class A { public function method() { /* ... */ } } class B extends A { public function method() { /* ... */ } } class C extends B { public function method() { A::method(); /* ... */ } } Can also call grandparent method
  67. 67. class A { public function method() { echo 'A::method with $this=' . get_class($this) . "n"; } } class B /* does not extend A */ { public function method() { A::method(); } } (new B)->method();
  68. 68. class A { public function method() { echo 'A::method with $this=' . get_class($this) . "n"; } } class B /* does not extend A */ { public function method() { A::method(); } } (new B)->method(); // PHP 5: A::method with $this=B (+ deprecation)
  69. 69. class A { public function method() { echo 'A::method with $this=' . get_class($this) . "n"; } } class B /* does not extend A */ { public function method() { A::method(); } } (new B)->method(); // PHP 5: A::method with $this=B (+ deprecation) // PHP 7.0: Undefined variable: this // PHP 7.1: Error: Using $this when not in object context
  70. 70. class Test { public function __call($name, $args) { echo "__call($name)n"; } public static function __callStatic($name, $args) { echo "__callStatic($name)n"; } public function doCall() { Test::foobar(); } } Test::foobar(); (new Test)->doCall();
  71. 71. class Test { public function __call($name, $args) { echo "__call($name)n"; } public static function __callStatic($name, $args) { echo "__callStatic($name)n"; } public function doCall() { Test::foobar(); } } Test::foobar(); // __callStatic(foobar) (new Test)->doCall();
  72. 72. class Test { public function __call($name, $args) { echo "__call($name)n"; } public static function __callStatic($name, $args) { echo "__callStatic($name)n"; } public function doCall() { Test::foobar(); // __call(foobar) } } Test::foobar(); // __callStatic(foobar) (new Test)->doCall();
  73. 73. Static Closures
  74. 74. class Test { public function __construct() { $this->fn = function() { /* $this can be used here */ }; } }
  75. 75. class Test { public function __construct() { $this->fn = static function() { /* $this CANNOT be used here */ }; } }
  76. 76. class Test { public function __construct() { $this->fn = static function() { /* $this CANNOT be used here */ }; } } Without static: • Closure references $this • $this->fn references Closure
  77. 77. class Test { public function __construct() { $this->fn = static function() { /* $this CANNOT be used here */ }; } } Without static: • Closure references $this • $this->fn references Closure  Cycle causes delayed GC

×