Question

I have a object having some protected property that I want to get and set. The object looks like

Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] => 


[_description:protected] => 
[_disableLoadDefaultDecorators:protected] => 
[_errorMessages:protected] => Array
    (
    )

[_errors:protected] => Array
    (
    )
[_isErrorForced:protected] => 
[_label:protected] => Current City


[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)

I want to get value property of the object. When I try $obj->_value or $obj->value it generates error. I searched and found the solution to use PHP Reflection Class. It worked on my local but on server PHP version is 5.2.17 So I cannot use this function there. So any solution how to get such property?

Was it helpful?

Solution 3

That's what "protected" is meant for, as the Visibility chapter explains:

Members declared protected can be accessed only within the class itself and by inherited and parent classes.

If you need to access the property from outside, pick one:

  • Don't declare it as protected, make it public instead
  • Write a couple of functions to get and set the value (getters and setters)

If you don't want to modify the original class (because it's a third-party library you don't want to mess) create a custom class that extends the original one:

class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}

... and add your getter/setter there.

OTHER TIPS

Here's the really simple example (with no error checking) of how to use ReflectionClass:

function accessProtected($obj, $prop) {
  $reflection = new ReflectionClass($obj);
  $property = $reflection->getProperty($prop);
  $property->setAccessible(true);
  return $property->getValue($obj);
}

I know you said you were limited to 5.2, but that was 2 years ago, 5.5 is the oldest supported version and I'm hoping to help people with modern versions.

Object can be typecasted into (associative) array and the protected members have keys prefixed with chr(0).'*'.chr(0) (see @fardelian's comment here). Using this undocummented feature you can write an "exposer":

function getProtectedValue($obj, $name) {
  $array = (array)$obj;
  $prefix = chr(0).'*'.chr(0);
  return $array[$prefix.$name];
}

Alternatively, you can parse the value from serialized string, where (it seems) protected members have the same prefix.

This works in PHP 5.2 without the overhead of ReflectionClass. However, there are reasons why some property is protected and hidden from client code. The reading or writing can make the data inconsistent or the author provides some other way to expose it in effort to make the interface as lean as possible. When there are reasons to read the protected property directly, the then-correct approach was to implement __get() magic method, so always check if there is any and see what it does. This counter intuitive lookup was finally solved in PHP 8.1 with readonly properties.

Since PHP 8.0, there also attributes metadata accessible by ReflectionClass, make sure to check them also before performing attempts to break into protected members. Attributes superseded "Annotations"1, so check them, too.

1: annotations are a very nasty surprise to client coders: they parse comments to add crazy fancy black-box useless confusing functionality, should not be used anymore, but they still exist

If you want to tinker with a class without adding getters and setters....

PHP 7 adds a call($obj) method (faster than old bindTo) on closures allowing you to call a function so the $this variable will act just as it would within a class -with full permissions.

 //test class with restricted properties
 class test{
    protected $bar="protected bar";
    private $foo="private foo";
    public function printProperties(){
        echo $this->bar."::".$this->foo;   
     }
 }

$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
    $this->bar="I changed bar";
    $this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0 

For PHP 7.4+, we can use an Arrow Function and the Closure::call to access private and protected members using just one small line:

PHP 7.4+

Retrieving protected/private members:

class Test {
  protected $data = 'Protected variable!';
}

// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);

Altering protected/private members:

class Test {
  protected $data = 'Testing';
}

$test = new Test;

(fn() => $this->data = "New Data!")->call($test);

// Will output "New Data!"
echo (fn() => $this->data)->call($test);

Of course, we can use a normal Closure function if we want to alter/use multiple members:

class Test {
  protected $data = 'Data!';
}

$test = new Test;

(function() {
  $this->new_data = "New {$this->data}";
})->call($test);

// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);

If you cannot modify the original class and extending it is not an option either, you can use the ReflectionProperty interface.

The phptoolcase library has a handy method for this:

$value = PtcHandyMan::getProperty($your_object , 'propertyName');

Static property from a singleton class:

$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');

You can find the tool here: http://phptoolcase.com/guides/ptc-hm-guide.html

What I like to do is declare every property that would be writable from outside as public. The properties that you want to be visible for the outside world but not writable you should declare as protected and write __get() magic method so you can read them. Example:

/**
 * Class Test
 *
 * @property int $protected
 *
 */
class Test
{
    
    private const READABLE = ['protected'];
    
    protected $protected = 1;
    
    public $public = 2;
    
    public function __get($property)
    {
        //if you want to read every protected or private
        return $this->$property ?? null;
    
        //if you want only some protected and private values to be readable
        if (in_array($property, self::READABLE)) {
            return $this->$property;
        }
    }
}

$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2

$test->protected = 3; //outputs error - protected property

The best would be to have property declaration like:

public readonly $protected = 1; //only readable from the outside
public  $public = 2; //readable and writable from the outside

but no such syntax exists yet(or... at least I don't know about it). P.S. you should declare the protected/private properties that will be readable in the Class DockBlock as shown, so you can autocomplete them, otherwise you will be able to access them, but your IDE won't recognize them on autocomplete when you are writing code.

$a=json_encode((array)$obj);
$b=(array)json_decode(str_replace('\u0000*\u0000','',$a));

echo($b['value']);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top