This post assumes that you are familiar with object programming, encapsulation and you know why and how private/protected/public keywords are used.
But one simple fact is often missed. In PHP (and some other popular OO languages) visibility is class based, not object based! This means that your private/protected properties are not so private as you might thought since other instances (objects) of the same class can access it.
Different objects of the same class can access each other private and protected members!
Run this example code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php class Foo { private $_privateMember=1; protected $_protectedMember=2; public function doSomething(Foo $foo) { $foo->_privateMember *= 10; $foo->_protectedMember *= 10; } public function __toString() { return "privateMember=(".$this->_privateMember.") protectedMember=(".$this->_protectedMember.")"; } } $fooA = new Foo(); $fooB = new Foo(); echo "before fooA: $fooA\n"; // call $fooB method on another instance of the Foo class - $fooA $fooB->doSomething($fooA); echo "after fooA: $fooA\n"; ?> |
In this example object $fooB is accessing and changing private and protected properties of another object $fooA.
Output:
1 2 3 |
$ php -q test1.php before fooA: privateMember=(1) protectedMember=(2) after fooA: privateMember=(10) protectedMember=(20) |
And this is not a bug, it is a feature. This is what PHP manual says about it:
Objects of the same type will have access to each others private and protected members even though they are not the same instances. This is because the implementation specific details are already known when inside those objects.
And the same applies to C++, Java and C# to name a few.
Lets run another test to see how class based visibility affects instances of the inherited classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php class Foo { private $_privateMember=1; protected $_protectedMember=2; public function doSomething(Foo $foo) { $foo->_privateMember *= 10; $foo->_protectedMember *= 10; } public function __toString() { return "privateMember=(".$this->_privateMember.") protectedMember=(".$this->_protectedMember.")"; } } class Bar extends Foo { public function doSomethingFromBar(Foo $foo) { $foo->_privateMember *= 100; $foo->_protectedMember *= 100; } } $fooA = new Foo(); $barB = new Bar(); echo "before fooA: $fooA\n"; // call $barB method on another instance of the Foo class - $fooA $barB->doSomethingFromBar($fooA); echo "after fooA: $fooA\n"; ?> |
Output:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ php -q test2.php before fooA: privateMember=(1) protectedMember=(2) PHP Fatal error: Uncaught Error: Cannot access private property Foo::$_privateMember in /home/damir/programming/php/test2.php:25 Stack trace: #0 /home/damir/programming/php/test2.php(37): Bar->doSomethingFromBar(Object(Foo)) #1 {main} Next Error: Cannot access private property Foo::$_privateMember in /home/damir/programming/php/test2.php:25 Stack trace: #0 /home/damir/programming/php/test2.php(37): Bar->doSomethingFromBar(Object(Foo)) #1 {main} thrown in /home/damir/programming/php/test2.php on line 25 |
This is to be expected. PHP interpreter applied class based visibility rules. Since inherited class can not access it’s parent private properties/method it is only logical that object of the inherited class can not access other object private members, when other object is instance of the parent class.
It can, however access protected members of the other object.
This works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<?php class Foo { private $_privateMember=1; protected $_protectedMember=2; public function doSomething(Foo $foo) { $foo->_privateMember *= 10; $foo->_protectedMember *= 10; } public function __toString() { return "privateMember=(".$this->_privateMember.") protectedMember=(".$this->_protectedMember.")"; } } class Bar extends Foo { public function doSomethingFromBar(Foo $foo) { // can't access private properties from parent class, even if it is another instance //$foo->_privateMember *= 100; $foo->_protectedMember *= 100; } } $fooA = new Foo(); $barB = new Bar(); echo "before fooA: $fooA\n"; // call $barB method on another instance of the Foo class - $fooA $barB->doSomethingFromBar($fooA); echo "after fooA: $fooA\n"; ?> |
Output:
1 2 3 |
$ php -q test3.php before fooA: privateMember=(1) protectedMember=(2) after fooA: privateMember=(1) protectedMember=(200) |
$barB object successfully accessed and changed protected property of another object $fooA.