> Members declared protected can be accessed only within
> the class itself and by inherited classes. Members declared
> as private may only be accessed by the class that defines
> the member.
This is not strictly true. Code outside the object can get and set private and protected members:
<?php
class Sealed { private $value = 'foo'; }
$sealed = new Sealed;
var_dump($sealed); call_user_func(\Closure::bind(
function () use ($sealed) { $sealed->value = 'BAZ'; },
null,
$sealed
));
var_dump($sealed); ?>
The magic lay in \Closure::bind, which allows an anonymous function to bind to a particular class scope. The documentation on \Closure::bind says:
> If an object is given, the type of the object will be used
> instead. This determines the visibility of protected and
> private methods of the bound object.
So, effectively, we're adding a run-time setter to $sealed, then calling that setter. This can be elaborated to generic functions that can force set and force get object members:
<?php
function force_set($object, $property, $value) {
call_user_func(\Closure::bind(
function () use ($object, $property, $value) {
$object->{$property} = $value;
},
null,
$object
));
}
function force_get($object, $property) {
return call_user_func(\Closure::bind(
function () use ($object, $property) {
return $object->{$property};
},
null,
$object
));
}
force_set($sealed, 'value', 'quux');
var_dump(force_get($sealed, 'value')); ?>
You should probably not rely on this ability for production quality code, but having this ability for debugging and testing is handy.