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. 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. 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. 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. 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. Object can have multiple properties
with same name
Name mangling ensures
unique property names
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. 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. class Value {
public $x;
}
$obj = new Value;
// $obj size: 56 bytes
foreach ($obj as $k => $v) { }
// $obj size: 432 bytes
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. 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
47. class Test {
public function __get($name) {
return $this->$name;
}
} Does not recurse into __get()
Will access property directly
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. 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)
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
57. class Test {
public $prop;
}
$obj = new Test;
unset($obj->prop);
var_dump($obj->prop);
// Notice: Undefined property: Test::$prop
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. 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. 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()
63. class A {
public function method() {
/* ... */
}
}
class B extends A {
public function method() {
parent::method();
/* ... */
}
}
64. class A {
public function method() {
/* ... */
}
}
class B extends A {
public function method() {
A::method();
/* ... */
}
}
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. 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. 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. 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. 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. 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. 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. 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();
74. class Test {
public function __construct() {
$this->fn = function() {
/* $this can be used here */
};
}
}
75. class Test {
public function __construct() {
$this->fn = static function() {
/* $this CANNOT be used here */
};
}
}
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. 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