PHP - how to get the "use (...)" section using reflection [duplicate] - php

When you create a method that returns a closure in PHP:
class ExampleClass {
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
The result of print_r contains this (the class whose method created the closure) and static, which appears to be the values bound within the use () statement of the closure:
$instance = new ExampleClass();
$closure = $instance->test();
print_r($closure);
Producing:
Closure Object (
[static] => Array (
[example] => 10
)
[this] => ExampleClass Object()
)
However I cannot for the life of me work out how to capture these values. It is not possible to use any form of property accessor (e.g. $closure->static or $closure->{'static'}) without receiving the following:
PHP Fatal error: Uncaught Error: Closure object cannot have properties in XYZ.
Array access notation obviously does not work either:
PHP Fatal error: Uncaught Error: Cannot use object of type Closure as array in XYZ.
JSON encoding the object, besides this making the values useless were they objects, provides an empty JSON object {} and using the ReflectionFunction class does not provide access to these items.
The closure documentation does not provide any means of accessing these values at all either.
Outside of doing something shameful like output buffering and parsing print_r or similar, I cannot actually see a way to get these values.
Am I missing something obvious?
Note: The use-case is for implementing memoization and these values would be extremely beneficial in identifying whether or not the call matched a previous cached call.

It seems you may have overlooked some of the ReflectionFunction methods.
Take a look at the ReflectionFunction::getClosureThis() method. I tracked it down by looking through the PHP 7 source code by doing a search for the zend_get_closure_this_ptr() which is defined in zend_closures.c.
The manual currently doesn't have a lot of documentation for this function. I'm using 7.0.9; try running this code based on your example:
class ExampleClass {
private $testProperty = 33;
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
$instance = new ExampleClass();
$closure = $instance->test();
print_r($closure);
$func = new ReflectionFunction($closure);
print_r($func->getClosureThis());
You should get output similar to
Closure Object
(
[static] => Array
(
[example] => 10
)
[this] => ExampleClass Object
(
[testProperty:ExampleClass:private] => 33
)
)
ExampleClass Object
(
[testProperty:ExampleClass:private] => 33
)
Regarding the closure static variables, these are returned with ReflectionFunction::getStaticVariables():
php > var_dump($func->getStaticVariables());
array(1) {
["example"]=>
int(10)
}

In order to get value of $example youn can try this
<?php
class ExampleClass {
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
$instance = new ExampleClass();
$number = call_user_func($instance->test(),null);
I found this solution into a Laravel framework and works for me.
call_user_func helps you.

Related

How to call a method of anonymous object php?

I need to create anonymous object and call it's method
$obj = new stdClass();
$obj->Greeting = function (string $d){return "Hello ".$d;};
$greetings = $obj->Greeting("world!");
But when I try to execute this code I get an error
Call to undefined method stdClass::Greeting()
What's wrong?
You created a stdClass object, not an anonymous one:
$obj = new class () {
public function Greeting(string $d)
{
return "Hello $d";
}
};
echo $greetings = $obj->Greeting("world!");
output:
Hello world!
What's wrong?
Nothing, let's just ask, what's behind or happening here?
The stdClass is used for "empty" objects in PHP or when casting an array to an object ($obj = (object) ['hello' => 'world']).
By default it has no properties (like in $obj = new stdClass;) and also no methods. It is empty in terms of both of these.
Properties can be added dynamically to an stdClass object - but not functions as class methods have to be declared in PHP before instantiating the object.
So the function in your case is a property (PHP has two bags here: one for properties and one for functions) and not a new method dynamically added to it (class MyClass { function method() {...} }).
Let's compare with the original example and provoke the error again:
$obj = new stdClass();
$obj->Greeting = function (string $d) {
return "Hello $d";
};
$greetings = $obj->Greeting("world!");
PHP Fatal error: Uncaught Error: Call to undefined method stdClass::Greeting()
However:
echo $greetings = ($obj->Greeting)("world!");
# #
works, the output:
Hello world!
because PHP is now guided to "call" the ($obj->Greeting) property indirectly, so not looking for the stdClass::Greeting method first.
Normally you don't want that indirection, therefore the suggestion to use the anonymous class instead.
Change
$obj->Greeting("world!");
to
($obj->Greeting)("world!");
or use call_user_func() :
call_user_func($obj->Greeting, 'world!')

Reading "this" and "use" arguments from a PHP closure

When you create a method that returns a closure in PHP:
class ExampleClass {
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
The result of print_r contains this (the class whose method created the closure) and static, which appears to be the values bound within the use () statement of the closure:
$instance = new ExampleClass();
$closure = $instance->test();
print_r($closure);
Producing:
Closure Object (
[static] => Array (
[example] => 10
)
[this] => ExampleClass Object()
)
However I cannot for the life of me work out how to capture these values. It is not possible to use any form of property accessor (e.g. $closure->static or $closure->{'static'}) without receiving the following:
PHP Fatal error: Uncaught Error: Closure object cannot have properties in XYZ.
Array access notation obviously does not work either:
PHP Fatal error: Uncaught Error: Cannot use object of type Closure as array in XYZ.
JSON encoding the object, besides this making the values useless were they objects, provides an empty JSON object {} and using the ReflectionFunction class does not provide access to these items.
The closure documentation does not provide any means of accessing these values at all either.
Outside of doing something shameful like output buffering and parsing print_r or similar, I cannot actually see a way to get these values.
Am I missing something obvious?
Note: The use-case is for implementing memoization and these values would be extremely beneficial in identifying whether or not the call matched a previous cached call.
It seems you may have overlooked some of the ReflectionFunction methods.
Take a look at the ReflectionFunction::getClosureThis() method. I tracked it down by looking through the PHP 7 source code by doing a search for the zend_get_closure_this_ptr() which is defined in zend_closures.c.
The manual currently doesn't have a lot of documentation for this function. I'm using 7.0.9; try running this code based on your example:
class ExampleClass {
private $testProperty = 33;
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
$instance = new ExampleClass();
$closure = $instance->test();
print_r($closure);
$func = new ReflectionFunction($closure);
print_r($func->getClosureThis());
You should get output similar to
Closure Object
(
[static] => Array
(
[example] => 10
)
[this] => ExampleClass Object
(
[testProperty:ExampleClass:private] => 33
)
)
ExampleClass Object
(
[testProperty:ExampleClass:private] => 33
)
Regarding the closure static variables, these are returned with ReflectionFunction::getStaticVariables():
php > var_dump($func->getStaticVariables());
array(1) {
["example"]=>
int(10)
}
In order to get value of $example youn can try this
<?php
class ExampleClass {
public function test() {
$example = 10;
return function() use ($example) {
return $example;
};
}
}
$instance = new ExampleClass();
$number = call_user_func($instance->test(),null);
I found this solution into a Laravel framework and works for me.
call_user_func helps you.

PHP serialize() for object injection

I'm currently analyzing the PHP exploitation method called PHP Object Injection, which allows for modification of already defined objects due to unsanitized input in unserialize() function.
Here is the code:
<?php
class foo {}
class SuperClass {}
$ss = 'O:3:"foo":2:{s:4:"test";b:1;s:2:"fg";O:10:"SuperClass":0:{}};';
print_r(unserialize($ss));
?>
Which produces the following output:
foo Object
(
[test] => 1
[fg] => SuperClass Object
(
)
)
My question is, how would I recreate the object structure that I could pass to serialize() function that would create the same input string? PHP doesn't allow nested classes, so I'm quite puzzled as to if it is even possible to generate such string with serialize()?
Here is how I would do it, but due to PHP not allowing nested classes, it won't work:
class foo {
public $test = 1;
public $fg = class SuperClass {
}
}
echo serialize(new foo);
If you want to see how PHP will serialize a class structure like that, and that is what I assume you are asking then create your classes like this
<?php
class SuperClass
{
public $name = 'SuperClass';
}
class foo
{
public $name = 'foo';
public $test = 1;
}
$s = new SuperClass();
$f = new foo();
$s->fg = $f;
$ss = serialize($s);
echo $ss . PHP_EOL;
$hydrated = unserialize($ss);
print_r($hydrated);
This produces the following output
O:10:"SuperClass":2:{s:4:"name";s:10:"SuperClass";s:2:"fg";O:3:"foo":2:{s:4:"name";s:3:"foo";s:4:"test";i:1;}}
SuperClass Object
(
[name] => SuperClass
[fg] => foo Object
(
[name] => foo
[test] => 1
)
)
You can play with this to see how to manipulate the serialized string to add whatever content to the string to make the objects contain whatever you want.
Probably the fields needs to be an instance of the class.
class foo {
public $test = 1;
public $fg = new SuperClass();
}
var_dump(serialize(new foo()));
Alternatively to your syntax, PHP7 provides anonymous classes functionality
public $fg = new class() extends SuperClass {
// some additional implementation here
}
So you can add fields which later will be presented in the serialized string. In the current example none are present.

How to return objects or arrays from a chaining interface in PHP OOP?

I am curious in writing a chaining interface in PHP OOP. I modified this sample code from the php.net website, and I want to take it further - how can I return objects or arrays from this kind of interface?
// Declare a simple class
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString()
{
return $this->foo;
}
}
$input = (object)array("title" => "page 1");
$class = new TestClass($input);
echo $class;
error,
Catchable fatal error: Method TestClass::__toString() must return a
string value in C:\wamp\www\test\2013\php\fluent_interface.php on line
2
Should I use different magic method instead of __toString then?
EDIT:
Can I return this as my result,
stdClass Object ( [title] => page 1 )
To get what you want you need to use following syntax:
print_r($class->foo);
The __toString() magic method tries to convert your whole class 'TestClass' to a string, but since the magic method is not returning a string, it is showing you that error. Of course you could also rewrite your __toString() method to do the following:
public function __toString()
{
return print_r($this->foo, true);
}
http://php.net/manual/en/function.print-r.php
http://www.php.net/manual/en/language.oop5.magic.php#object.tostring
I think you are looking for either print_r or var_export functions:
public function __toString()
{
return var_export($this->foo, true);
}
and var_export is better since it also return type of value (and, besides, in valid PHP-code format). Note, that __toString() method have nothing common with fluent interface. It's just different things.

Why is stdClass namespaced?

Argument 1 passed to nms\aclass::__construct() must be an instance
of nms\stdClass, instance of stdClass given
The __construct() function looks like public function __construct(stdClass $aclass)
And I get the error, I am passing in an instance of stdClass from the global namespace, not one from within the same namespace,
I also get the solution: public function __construct(\stdClass $aclass)
But what I don't get is why? I don't think it is even possible to overwrite stdClass is it? so would what is the difference between \stdClass and \nms\stdClass?
Sure it's possible to override stdClass inside of a namespace.
namespace Example;
class stdClass {
public function __construct() {
echo "Overridden!";
}
}
$global = new \stdClass();
$local = new stdClass;
Outputs:
Overridden!
Hence, if you want the original, you'll need to use the /stdClass global namespace. Much like with /Exceptions.

Categories