📜 ⬆️ ⬇️

We get access to private properties of objects in PHP without reflection

A few weeks ago, I worked on a problem in ProxyManager . The problem was simple: ReflectionClass and ReflectionProperty are very, very, and sooo slow!
The reason for this research is my attempt to optimize the " hydrator " for working with large amounts of data without the overhead of initialization.

PHP 5.4 help me out!



PHP 5.4 gave us a new closure API and the Closure#bind() method.
Closure#bind() basically allows you to get an instance of a closure with the scope of a given class or object. Gracefully! This is how you can add API to existing objects!
Let's break the object encapsulation according to our needs.
Methods of accessing private properties have already been explained in the documentation, but I still give an example.
Stealing Kitchen#yummy private property:
 <?php class Kitchen { private $yummy = 'cake'; } 

Define the closure to get this field:
 <?php $sweetsThief = function (Kitchen $kitchen) { return $kitchen->yummy; } 

Now let's steal the yummy from the Kitchen instance:
 <?php $kitchen = new Kitchen(); var_dump($sweetsThief($kitchen)); 

Unfortunately, we get a fatal error in $sweetsThief :
 Fatal error: Cannot access private property Kitchen::$yummy in [...] on line [...] 

Let's make our thief smarter Closure#bind() :
 <?php $kitchen = new Kitchen(); // Closure::bind()       $sweetsThief = Closure::bind($sweetsThief, null, $kitchen); var_dump($sweetsThief($kitchen)); 

Good luck !
')

Closure :: bind vs Reflection: speed


I made a simple benchmark for 100,000 iterations of initialization:
 <?php for ($i = 0; $i < 100000; $i += 1) { $sweetsThief = Closure::bind(function (Kitchen $kitchen) { return $kitchen->yummy; }, null, 'Kitchen'); } <?php for ($i = 0; $i < 100000; $i += 1) { $sweetsThief = new ReflectionProperty('Kitchen', 'yummy'); $sweetsThief->setAccessible(true); } 

On the newly compiled PHP 5.5 (Ubuntu 13.04 amd64 box), the first test took 0.325 seconds, and the second 0.658 .

Reflection is much slower . (49%)

But this is not at all interesting, since no one will need to initialize 100,000 times the same thing, at least to me for sure. What is really interesting is access to private properties. I tested it too:
 <?php $kitchen = new Kitchen(); $sweetsThief = Closure::bind(function (Kitchen $kitchen) { return $kitchen->yummy; }, null, 'Kitchen'); for ($i = 0; $i < 100000; $i += 1) { $sweetsThief($kitchen); } <?php $kitchen = new Kitchen(); $sweetsThief = new ReflectionProperty('Kitchen', 'yummy'); $sweetsThief->setAccessible(true); for ($i = 0; $i < 100000; $i += 1) { $sweetsThief->getValue($kitchen); } 

The first test took ~ 0.110 seconds, and the second ~ 0.199!
This is much faster reflection, impressive! (55%)

Access to private properties by reference


There is one more advantage: using closures instead of reflection, we can work with object properties by reference!
 <?php $sweetsThief = Closure::bind(function & (Kitchen $kitchen) { return $kitchen->yummy; }, null, $kitchen); $cake = & $sweetsThief($kitchen); $cake = 'lie'; var_dump('the cake is a ' . $sweetsThief($kitchen)); 


Universal Access Method


Given the above, we can write a simple wrapper to obtain any property of any object:
 <?php $reader = function & ($object, $property) { $value = & Closure::bind(function & () use ($property) { return $this->$property; }, $object, $object)->__invoke(); return $value; }; $kitchen = new Kitchen(); $cake = & $reader($kitchen, 'cake'); $cake = 'sorry, I ate it!'; var_dump($kitchen); 

Working example
We have access to any property, anywhere, and even by reference. Hooray! We broke the rules again!

Conclusion


Once again, PHP showed itself from the good and the bad side at the same time. This is a terrible language, with terrible syntax, but it allows us to bypass any language limitations pleasing us with new functionality with each release.
I will not use this technique myself, but if I need to get a private property of the object, and even by reference, I will do it that way.

Disclaimer: use this opportunity with caution!

Source: https://habr.com/ru/post/186718/


All Articles