Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Invading private properties and methods in PHP

Link –

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

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.

class MyClass 
{
    private string $privateProperty = 'private value';

    private function privateMethod(): string
    {
        return 'private return value';
    }
}

$myClass = new Myclass();

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.

namespace Spatie\Invade;

use ReflectionClass;

class Invader
{
    public object $obj;
    public ReflectionClass $reflected;

    public function __construct(object $obj)
    {
        $this->obj = $obj;
        $this->reflected = new ReflectionClass($obj);
    }

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

        $property->setAccessible(true);

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

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

        $property->setAccessible(true);

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

    public function __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.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

Carlos avatar

Hey! Nice one! It reminds me a project I built a few years back. Its called Communism because, you know, it makes public all private properties :-D

Anyway, I like this project and the non-reflection solution you used. Good stuff!

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.