How do you get value from private properties in PHP [duplicate] - php

It doesn't seem to work:
$ref = new ReflectionObject($obj);
if($ref->hasProperty('privateProperty')){
print_r($ref->getProperty('privateProperty'));
}
It gets into the IF loop, and then throws an error:
Property privateProperty does not exist
:|
$ref = new ReflectionProperty($obj, 'privateProperty') doesn't work either...
The documentation page lists a few constants, including IS_PRIVATE. How can I ever use that if I can't access a private property lol?

class A
{
private $b = 'c';
}
$obj = new A();
$r = new ReflectionObject($obj);
$p = $r->getProperty('b');
$p->setAccessible(true); // <--- you set the property to public before you read the value
var_dump($p->getValue($obj));

Please, note that accepted answer will not work if you need to get the value of a private property which comes from a parent class.
For this you can rely on getParentClass method of Reflection API.
Also, this is already solved in this micro-library.
More details in this blog post.

getProperty throws an exception, not an error. The significance is, you can handle it, and save yourself an if:
$ref = new ReflectionObject($obj);
$propName = "myProperty";
try {
$prop = $ref->getProperty($propName);
} catch (ReflectionException $ex) {
echo "property $propName does not exist";
//or echo the exception message: echo $ex->getMessage();
}
To get all private properties, use $ref->getProperties(ReflectionProperty::IS_PRIVATE);

In case you need it without reflection:
public function propertyReader(): Closure
{
return function &($object, $property) {
$value = &Closure::bind(function &() use ($property) {
return $this->$property;
}, $object, $object)->__invoke();
return $value;
};
}
and then just use it (in the same class) like this:
$object = new SomeObject();
$reader = $this->propertyReader();
$result = &$reader($object, 'some_property');

Without reflection, one can also do
class SomeHelperClass {
// Version 1
public static function getProperty1 (object $object, string $property) {
return Closure::bind(
function () use ($property) {
return $this->$property;
},
$object,
$object
)();
}
// Version 2
public static function getProperty2 (object $object, string $property) {
return (
function () use ($property) {
return $this->$property;
}
)->bindTo(
$object,
$object
)->__invoke();
}
}
and then something like
SomeHelperClass::getProperty1($object, $propertyName)
SomeHelperClass::getProperty2($object, $propertyName)
should work.
This is a simplified version of Nikola Stojiljković's answer

Related

Anonymous classes, accessing private properties of extended class [duplicate]

It doesn't seem to work:
$ref = new ReflectionObject($obj);
if($ref->hasProperty('privateProperty')){
print_r($ref->getProperty('privateProperty'));
}
It gets into the IF loop, and then throws an error:
Property privateProperty does not exist
:|
$ref = new ReflectionProperty($obj, 'privateProperty') doesn't work either...
The documentation page lists a few constants, including IS_PRIVATE. How can I ever use that if I can't access a private property lol?
class A
{
private $b = 'c';
}
$obj = new A();
$r = new ReflectionObject($obj);
$p = $r->getProperty('b');
$p->setAccessible(true); // <--- you set the property to public before you read the value
var_dump($p->getValue($obj));
Please, note that accepted answer will not work if you need to get the value of a private property which comes from a parent class.
For this you can rely on getParentClass method of Reflection API.
Also, this is already solved in this micro-library.
More details in this blog post.
getProperty throws an exception, not an error. The significance is, you can handle it, and save yourself an if:
$ref = new ReflectionObject($obj);
$propName = "myProperty";
try {
$prop = $ref->getProperty($propName);
} catch (ReflectionException $ex) {
echo "property $propName does not exist";
//or echo the exception message: echo $ex->getMessage();
}
To get all private properties, use $ref->getProperties(ReflectionProperty::IS_PRIVATE);
In case you need it without reflection:
public function propertyReader(): Closure
{
return function &($object, $property) {
$value = &Closure::bind(function &() use ($property) {
return $this->$property;
}, $object, $object)->__invoke();
return $value;
};
}
and then just use it (in the same class) like this:
$object = new SomeObject();
$reader = $this->propertyReader();
$result = &$reader($object, 'some_property');
Without reflection, one can also do
class SomeHelperClass {
// Version 1
public static function getProperty1 (object $object, string $property) {
return Closure::bind(
function () use ($property) {
return $this->$property;
},
$object,
$object
)();
}
// Version 2
public static function getProperty2 (object $object, string $property) {
return (
function () use ($property) {
return $this->$property;
}
)->bindTo(
$object,
$object
)->__invoke();
}
}
and then something like
SomeHelperClass::getProperty1($object, $propertyName)
SomeHelperClass::getProperty2($object, $propertyName)
should work.
This is a simplified version of Nikola Stojiljković's answer

Getting a class name from a property reference

I was wondering if you could get the class name and property name from a property reference in PHP?
class Test {
public static $TestProp;
}
GetDoc(& Test::$TestProp);
function GetDoc($prop) {
$className = getClassName($prop);
$propertyName = getPropertyName($prop);
}
what I'm looking for is if it is possible to create the functions getClassName and getPropertyName?
What you want is basically not possible; a property doesn't know its parent structure.
The only sane thing I could think of is to use reflection for it:
class Test
{
public static $TestProp = '123';
}
//GetDoc(& Test::$TestProp);
GetDoc('Test', 'TestProp');
function GetDoc($className, $propName)
{
$rc = new ReflectionClass($className);
$propValue = $rc->getStaticPropertyValue($propName);
}
Within the Test class you could use __CLASS__ as a convenient reference for the class name.
I have figured out the way to get this to work there is a lot of magic that goes on just to get this to work, but in my case it's worth it.
class Test {
private $props = array();
function __get($name) {
return new Property(get_called_class(), $name, $this->props[$name]);
}
function __set($name, $value) {
$props[$name] = $value;
}
}
class Property {
public $name;
public $class;
public $value;
function __construct($class, $name, $value) {
$this->name = $name;
$this->class = $class;
$this->value = $value;
}
function __toString() {
return $value.'';
}
}
function GetClassByProperty($prop) {
return $prop->class.'->'.$prop->name;
}
$t = new Test();
$t->Name = "Test";
echo GetClassByProperty($t->Name);
this example yes I know it's complex, but it does the job how I'd want it to, will print out "Test->Name" I can also get the value by saying $prop->value. If I want to compare the value to another object I can simply do this:
if($t->Name == "Test") { echo "It worked!!"; }
hope this isn't too confusing but it was a fun exploration into PHP.
Php have a build in function called get_class

Wrong Static Method

PHP calls private method in parent class instead of method define in current class called by call_user_func
class Car {
public function run() {
return call_user_func(array('Toyota','getName')); // should call toyota
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); //Car instead of Toyota
$toyota = new Toyota();
echo $toyota->run(); //Car instead of Toyota
I have found a solution with a different approach..
<?php
class Car {
public static function run() {
return static::getName();
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
echo Car::run();
echo Toyota::run();
?>
Using Late Static Binding..
You might use something like this:
<?php
class Car {
public function run() {
return static::getName();
}
private static function getName(){
return 'Car';
}
}
class Toyota extends Car {
public static function getName(){
return 'Toyota';
}
}
$car = new Car();
echo $car->run();
echo PHP_EOL;
$toyota = new Toyota();
echo $toyota->run();
?>
Output:
Car
Toyota
PHP 5.4.5
This is a bug that appears to have fluctuated in and out of existence for a long time (see #deceze's tests in comments on the question). It is possible to "fix" this issue - that is, give consistent behaviour across PHP versions - using reflection:
Works in PHP 5.3.2 and later due to a dependency on ReflectionMethod::setAccessible() to invoke private/protected methods. I will add further explanation for this code, what it can and can't do and how it works very shortly.
Unfortunately it's not possible to test this directly on 3v4l.org because the code is too large, however this is the first ever real use case for minifying PHP code - it does work on 3v4l if you do this, so feel free to play around and see if you can break it. The only issue I'm aware of is that it doesn't currently understand parent. It is also restricted by the lack of $this support in closures before 5.4, there's not really anything that can be done about this though.
<?php
function call_user_func_fixed()
{
$args = func_get_args();
$callable = array_shift($args);
return call_user_func_array_fixed($callable, $args);
}
function call_user_func_array_fixed($callable, $args)
{
$isStaticMethod = false;
$expr = '/^([a-z_\x7f-\xff][\w\x7f-\xff]*)::([a-z_\x7f-\xff][\w\x7f-\xff]*)$/i';
// Extract the callable normalized to an array if it looks like a method call
if (is_string($callable) && preg_match($expr, $callable, $matches)) {
$func = array($matches[1], $matches[2]);
} else if (is_array($callable)
&& count($callable) === 2
&& isset($callable[0], $callable[1])
&& (is_string($callable[0]) || is_object($callable[0]))
&& is_string($callable[1])) {
$func = $callable;
}
// If we're not interested in it use the regular mechanism
if (!isset($func)) {
return call_user_func_array($func, $args);
}
$backtrace = debug_backtrace(); // passing args here is fraught with complications for backwards compat :-(
if ($backtrace[1]['function'] === 'call_user_func_fixed') {
$called = 'call_user_func_fixed';
$contextKey = 2;
} else {
$called = 'call_user_func_array_fixed';
$contextKey = 1;
}
try {
// Get a reference to the target static method if possible
switch (true) {
case $func[0] === 'self':
case $func[0] === 'static':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of self:: in an invalid context');
}
$contextClass = new ReflectionClass($backtrace[$contextKey][$func[0] === 'self' ? 'class' : 'object']);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
case is_object($func[0]):
$contextClass = new ReflectionClass($func[0]);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if ($method->isStatic()) {
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
} else {
$invokeContext = $func[0];
}
break;
default:
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
$method = new ReflectionMethod($func[0], $func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if (empty($backtrace[$contextKey]['object'])
|| $func[0] !== $contextClass->getName()
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
if (empty($backtrace[$contextKey]['object']) || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method outside a class context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
}
// Invoke the method with the passed arguments and return the result
return $method->invokeArgs($invokeContext, $args);
} catch (Exception $e) {
trigger_error($called . '() expects parameter 1 to be a valid callback: ' . $e->getMessage(), E_USER_ERROR);
return null;
}
}
Use "protected" modifier if you want to get access from parent and descendants only. IMO, it's obvious. For example:
<?php
class Car {
public function run() {
return call_user_func(array('static','getName'));
}
protected static function getName() {
return 'Car';
}
}
class Toyota extends Car {
protected static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); // "Car"
$toyota = new Toyota();
echo $toyota->run(); // "Toyota"
You can use get_called_class() instead of 'static'.
The problem is, I think, with the different access levels of the two getname functions. If you make the base class version of getname() public (the same as the derived class version), then in php 5.3.15 (on my Mac), you get Toyota. I think that, because of the different access levels, you end up with two different versions of the getname() function in the Toyota class, rather than the derived class version overriding the base class version. In other words, you have overloading rather than overriding. Therefore, when the run() function looks for a getname() function in the Toyota class to execute, it finds two and takes the first one, which would be the first to be declared (from the base class).
Granted this is just supposition on my part, but it sounds plausible.
use the get_called_called function todo this
public function run() {
$self = get_called_class();
return $self::getName();
}
I believe you're functions are overriding each other and by default going to the first one. Unless you change the parameters of one function, or rename the function it will always default to the parent class function.

Can I get the value of a private property with Reflection?

It doesn't seem to work:
$ref = new ReflectionObject($obj);
if($ref->hasProperty('privateProperty')){
print_r($ref->getProperty('privateProperty'));
}
It gets into the IF loop, and then throws an error:
Property privateProperty does not exist
:|
$ref = new ReflectionProperty($obj, 'privateProperty') doesn't work either...
The documentation page lists a few constants, including IS_PRIVATE. How can I ever use that if I can't access a private property lol?
class A
{
private $b = 'c';
}
$obj = new A();
$r = new ReflectionObject($obj);
$p = $r->getProperty('b');
$p->setAccessible(true); // <--- you set the property to public before you read the value
var_dump($p->getValue($obj));
Please, note that accepted answer will not work if you need to get the value of a private property which comes from a parent class.
For this you can rely on getParentClass method of Reflection API.
Also, this is already solved in this micro-library.
More details in this blog post.
getProperty throws an exception, not an error. The significance is, you can handle it, and save yourself an if:
$ref = new ReflectionObject($obj);
$propName = "myProperty";
try {
$prop = $ref->getProperty($propName);
} catch (ReflectionException $ex) {
echo "property $propName does not exist";
//or echo the exception message: echo $ex->getMessage();
}
To get all private properties, use $ref->getProperties(ReflectionProperty::IS_PRIVATE);
In case you need it without reflection:
public function propertyReader(): Closure
{
return function &($object, $property) {
$value = &Closure::bind(function &() use ($property) {
return $this->$property;
}, $object, $object)->__invoke();
return $value;
};
}
and then just use it (in the same class) like this:
$object = new SomeObject();
$reader = $this->propertyReader();
$result = &$reader($object, 'some_property');
Without reflection, one can also do
class SomeHelperClass {
// Version 1
public static function getProperty1 (object $object, string $property) {
return Closure::bind(
function () use ($property) {
return $this->$property;
},
$object,
$object
)();
}
// Version 2
public static function getProperty2 (object $object, string $property) {
return (
function () use ($property) {
return $this->$property;
}
)->bindTo(
$object,
$object
)->__invoke();
}
}
and then something like
SomeHelperClass::getProperty1($object, $propertyName)
SomeHelperClass::getProperty2($object, $propertyName)
should work.
This is a simplified version of Nikola Stojiljković's answer

How to find the last chain property in the object?

To avoid getting the error message as in this previous question, I decided to change the class with __get() like this below,
class property
{
public function __get($name)
{
return isset($this->$name) ? $this->$name : new property;
}
}
class objectify
{
public function array_to_object($array = array(), $property_overloading = false)
{
# if $array is not an array, let's make it array with one value of former $array.
if (!is_array($array)) $array = array($array);
# Use property overloading to handle inaccessible properties, if overloading is set to be true.
# Else use std object.
if($property_overloading === true) $object = new property();
else $object = new stdClass();
foreach($array as $key => $value)
{
$key = (string) $key ;
$object->$key = is_array($value) ? self::array_to_object($value, $property_overloading) : $value;
}
return $object;
}
}
$object = new objectify();
$type = null;
$type = $object->array_to_object($type,true);
var_dump($type->a->b->c);
so I get this result in the end,
object(property)#3 (0) { }
but it is still not perfect. as my understanding, the above solution processes the object in a chain like this,
$type = object{}->object{}->object{}
so I wonder if I can find whether it is the last chain and it is empty then just output a null?
$type = object{}->object{}->NULL
is it possible with PHP?
EDIT:
I have thought of an idea which is to count how many times the property class has been instantiated,
class property
{
public static $counter = 0;
function __construct() {
self::$counter++;
}
public function __get($name)
{
if(isset($this->$name))
{
return $this->$name;
}
elseif(property::$counter < 3)
{
return new property;
}
else
{
return null;
}
}
}
but my only problem is how to make the number 3 dynamic. Any ideas?
Sounds like you're looking for a PHP version of Groovy's ?. operator: http://groovy.codehaus.org/Null+Object+Pattern
Afaik, you can't overload or create a new operator in PHP. You could perhaps simulate it by passing all of your nested calls to a function, and the function knows when to return null.
Edit: other options posted here - http://justafewlines.com/2009/10/groovys-operator-in-php-sort-of/

Categories