Skip to content

In your PHP classes, accessing all your private members

Implementation isn’t always clean. Sometimes you’ll have to get your code dirty.

Recently while working on a project, I was reminded of how the pristine model and the tangible implementation are of two different worlds. You might give due diligence to your own code to mitigate pain points but chances are that your project will extend or integrate with some third party software. What if they’ve overlooked a concern? Sometimes, there’s no way around it – you need to get down to the nitty-gritty of the implementation.

My issue boiled down to the necessity of changing an object’s private field, an internal setting to which there was no adequate API for the task. Breaking encapsulation defeats the entire purpose but as I said, the implementation sometimes necessitates dirty tricks. Nonetheless, discovering various ways of breaking PHP encapsulation was fascinating in its own way.

Cast to Array

For read-only access, casting any object to an array produces a hash of member names to member values. Note that names of protected members will be prepended with an asterisk (*) and private members will be prepended with the name of the respective Class of the object.

Class A {
  private $private = 'foo';
  protected $protected = 'bar';
  public $public = 'baz';
}

$a = new A;
print_r( (array) $a);

/*
Outputs the following

Array(
  [Aprivate] => foo
  [*protected] => bar
  [public] => baz
)
*/

Closure Context

With PHP 5.4, it is now possible to bind closures to objects and change their scopes. If you need to perform an operation in the context of a certain object (access or set non-public fields and methods) you can attach a closure to the target object with Closure::bind() and $closure->bindTo() to inject privileged functionality. You can run the closure in a static scope by leaving the context null and providing a class name as the class scope.

Class A {
  private static $shh = 'shh';
  private $private = 'foo';

  static function getShh() { return self::$shh; }
}

$a = new A;

//object context
$foo = Closure::bind(function() {
  $this->private = 'foobard!';
}, $a, $a);

//static context
$foo2 = Closure::bind(function() {
  self::$shh = 'you shh!';
}, null, 'A');

$foo();
$foo2();

print_r( $a );
echo A::getShh();

/*
Outputs the following

Object ( [private:A:private] => foobard! )
you shh!
*/

Reflection

If you require the means to access, modify, or even change privilege of fields and methods, throw away encapsulation and become the king of the sandbox with Reflection. Reflection is best suited to reverse-engineering classes, interfaces, functions, methods and extensions. Additionally, the Reflection API offers ways to retrieve doc comments for functions, classes and methods.

Runkit

Runkit has been discontinued and is no longer a viable solution.

If inference and access control aren’t enough, if you require the power of gods, Runkit is the ultimate trump-all. The Runkit extension provides means to modify constants, user-defined functions, and user-defined classes. It also provides for custom superglobal variables and embeddable sub-interpreters via sandboxing. An interesting gem that caught my eye are the specific abilities to convert a base class to a child class with runkit_class_adopt(), and to convert a child class to a base class with runkit_class_emancipate(). With this API, it is possible to augment functionality by inserting a custom class into the inheritance hierarchy.