1293 1015 1037 1595 1268 1260 1930 1130 1488 1534 1068 1360 1164 1131 1743 1532 1488 1249 1053 1569 1261 1342 1032 1238 1074 1087 1583 1758 1517 1184 1347 1207 1562 1964 1782 1951 1934 1952 1848 1890 1698 1173 1978 1943 1790 1348 1922 1201 1489 1637 1040 1369 1479 1799 1602 1958 1971 1935 1403 1077 1918 1267 1103 1445 1286 1883 1456 1160 1582 1837 1720 1232 1893 1982 1360 1035 1586 1155 1774 1088 1022 1035 1310 1840 1414 1773 1729 1059 1394 1722 1107 1227 1764 1263 1713 1490 1595 1241 1203 Invading private properties and methods in PHP | PHPnews.io

PHPnews.io

Invading private properties and methods in PHP

Written by murze.be / Original link on Feb. 14, 2022

Last week, Caleb tweeted about a nifty function called invade - that he had made to easily work with private properties and methods.

😈 Whatcha think of my new favorite helper method?
That property/method protected or private? No problemoooo 🔓 pic.twitter.com/HqMXKKpRsJ

— Caleb Porzio (@calebporzio) February 11, 2022

He added that invade function to Livewire. Because I could see myself using this in non-Laravel projects, I packaged up the function in a new package called spatie/invade.

Using the package

Imagine you have this class defined which has a private property and method.

classMyClass
{
privatestring $privateProperty ='private value';

privatefunctionprivateMethod():string
    {
return'private return value';
    }
}

$myClass =newMyclass();

This is how you can get the value of the private property using the invade function.

invade($myClass)->privateProperty; // returns 'private value'

The invade function also allows you to change private values.

invade($myClass)->privateProperty ='changed value';
invade($myClass)->privateProperty; // returns 'changed value

Using invade, you can also call private functions.

invade($myClass)->privateMethod(); // returns 'private return value'

How the package works under the hood

Accessing private properties and methods seems magical, but it's pretty easy to achieve using reflection. On its reflection classes, PHP has a method setAccessible that can make private things public in runtime.

The invade function will pass the given object to an Invader class. That invader class has magic __get, __set and __call methods that will execute on each interaction with the given object. Before forwarding the call to the object, it will be made accessible.

The source code is so small that I can share it in full in this post.

namespaceSpatie\Invade;

useReflectionClass;

classInvader
{
publicobject $obj;
publicReflectionClass $reflected;

publicfunction__construct(object $obj)
    {
$this->obj = $obj;
$this->reflected =newReflectionClass($obj);
    }

publicfunction__get(string $name):mixed
    {
        $property =$this->reflected->getProperty($name);

        $property->setAccessible(true);

return $property->getValue($this->obj);
    }

publicfunction__set(string $name, mixed $value):void
    {
        $property =$this->reflected->getProperty($name);

        $property->setAccessible(true);

        $property->setValue($this->obj, $value);
    }

publicfunction__call(string $name, array $params = []):mixed
    {
        $method =$this->reflected->getMethod($name);

        $method->setAccessible(true);

return $method->invoke($this->obj, ...$params);
    }
}

And that's all there is to it!

Should you use this?

I don't think you should use this in the code of regular projects. In that context, you'll have to design your code in such a way that everything that should be called is easily callable.

Three situations come to mind where I can see invade being used:

  1. Inside packages that add specific, magical behaviour, like Livewire
  2. Inside a package to test some of its own behaviour that is not publicly accessible. The first example that comes to mind is non-public methods inside a package service provider (although it could be argued to make such a method public as well).
  3. Inside the test suite of package or project where you need to hook into private state/behaviour of other packages. In this case, it might also be a good idea to create an issue on that other package, asking the maintainer to make the state/behaviour accessible in a regular way.

Even though you might argue that it's not a good idea to do so, it's very cool that PHP allows functions like invade to be created.

murze

« Broadcasting Events in Laravel - Using Scout APM to Monitor a Laravel Application »