Determining if a closure is static in PHP - php

A closure defined in PHP can also carry the static modifier.
$f = function () { };
$g = static function () { };
The static closure cannot be bound via Closure::bind or Closure::bindTo, and will issue a warning.
$g = Closure::bind(static function () { }, new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
This is also the case of closures created by reflecting a static method with ReflectionMethod::getClosure.
class MyClass
{
public static function myStaticMethod() { }
}
// reflect MyClass::myStaticMethod, create an unbound closure, and try to bind it
$f = (new ReflectionMethod(MyClass::class, 'myStaticMethod'))
->getClosure()
->bindTo(new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
While annoying, this is acceptable; however, how is one to test between a static and non-static closure?
ReflectionMethod::isStatic seemed like it might work, but sensibly doesn't as Closure::__invoke is instance-level, not static.
$f = static function () { };
// reflect Closure::__invoke because I think I'm tricky
$r = new ReflectionMethod($f, '__invoke');
// and it's not static anyway
var_dump($r->isStatic()); // bool(false)
Further, checking ReflectionMethod::getClosureThis can generally work, as a static method must be unbound, however that doesn't cover closures defined outside an instance method, or the corner-case of instance methods that have been unbound.
class MyClass
{
public function myInstanceMethod() { }
}
$o = new MyClass();
// reflect MyClass::myInstanceMethod, create a bound closure, and then unbind it
$f = (new ReflectionMethod($o, 'myInstanceMethod'))
->getClosure($o)
->bindTo(null);
// then reflect the closure
$r = new ReflectionFunction($f);
// and see it's bound to nothing, as would be the case of a static closure
var_dump($r->getClosureThis()); // NULL
So, to restate, how do you determine whether a closure is static (or more specifically, bindable) or not?
It really seems as though we should have a ReflectionFunctionAbstract::isBindable, or that ReflectionMethod::isStatic be moved up to ReflectionFunctionAbstract.

If binding works, the Closure will have a $this bound to it. So, just bind it and then check for the $this. If it's null, well, then it's a static Closure.
function isBindable(\Closure $closure) {
$boundClosure = #\Closure::bind($closure, new stdClass);
return $boundClosure && (new ReflectionFunction($boundClosure))->getClosureThis() != null;
}

It seems impossible now.
You can find some debates here: https://bugs.php.net/bug.php?id=64761
The only real workaround I use for myself now is adding ->isBindable property manually.
Here's some code I found here https://github.com/atoum/atoum/blob/master/classes/test/adapter/invoker.php
Maybe will give you a few ideas
protected static function isBindable(\closure $closure)
{
$isBindable = (version_compare(PHP_VERSION, '5.4.0') >= 0);
if ($isBindable === true)
{
$reflectedClosure = new \reflectionFunction($closure);
$isBindable = ($reflectedClosure->getClosureThis() !== null || $reflectedClosure->getClosureScopeClass() === null);
}
return $isBindable;
}

Very old question but still shows up in google search. The mentioned isStatic nowadays exists in ReflectionFunction,as of php 8.1 (I tested it out)
$fn = static fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns true
$fn = fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns false
It does not show up in documentation as of writing.
See: https://www.php.net/manual/en/class.reflectionfunction.php
I also created a missing doc report here: https://github.com/php/doc-en/issues/2136

Related

Is there any cleaner way to call a clouse property in PHP [duplicate]

I would like to be able to call a closure that I assign to an object's property directly without reassigning the closure to a variable and then calling it. Is this possible?
The code below doesn't work and causes Fatal error: Call to undefined method stdClass::callback().
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback();
As of PHP7, you can do
$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');
or use Closure::call(), though that doesn't work on a StdClass.
Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)
class Foo
{
public function __call($method, $args)
{
if(is_callable(array($this, $method))) {
return call_user_func_array($this->$method, $args);
}
// else throw exception
}
}
$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');
Note that you cannot do
return call_user_func_array(array($this, $method), $args);
in the __call body, because this would trigger __call in an infinite loop.
You can do this by calling __invoke on the closure, since that's the magic method that objects use to behave like functions:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback->__invoke();
Of course that won't work if the callback is an array or a string (which can also be valid callbacks in PHP) - just for closures and other objects with __invoke behavior.
As of PHP 7 you can do the following:
($obj->callback)();
Since PHP 7 a closure can be called using the call() method:
$obj->callback->call($obj);
Since PHP 7 is possible to execute operations on arbitrary (...) expressions too (as explained by Korikulum):
($obj->callback)();
Other common PHP 5 approaches are:
using the magic method __invoke() (as explained by Brilliand)
$obj->callback->__invoke();
using the call_user_func() function
call_user_func($obj->callback);
using an intermediate variable in an expression
($_ = $obj->callback) && $_();
Each way has its own pros and cons, but the most radical and definitive solution still remains the one presented by Gordon.
class stdKlass
{
public function __call($method, $arguments)
{
// is_callable([$this, $method])
// returns always true when __call() is defined.
// is_callable($this->$method)
// triggers a "PHP Notice: Undefined property" in case of missing property.
if (isset($this->$method) && is_callable($this->$method)) {
return call_user_func($this->$method, ...$arguments);
}
// throw exception
}
}
$obj = new stdKlass();
$obj->callback = function() { print "HelloWorld!"; };
$obj->callback();
It seems to be possible using call_user_func().
call_user_func($obj->callback);
not elegant, though.... What #Gordon says is probably the only way to go.
Well, if you really insist. Another workaround would be:
$obj = new ArrayObject(array(),2);
$obj->callback = function() {
print "HelloWorld!";
};
$obj['callback']();
But that's not the nicest syntax.
However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, ( as method call. There seems to be no workaround for making -> bypass the method table and access the attributes instead.
I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+
First, create a trait that makes properties callable:
trait CallableProperty {
public function __call($method, $args) {
if (property_exists($this, $method) && is_callable($this->$method)) {
return call_user_func_array($this->$method, $args);
}
}
}
Then, you can use that trait in your classes:
class CallableStdClass extends stdClass {
use CallableProperty;
}
Now, you can define properties via anonymous functions and call them directly:
$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
well, it should be emphisized that storing the closure in a variable, and call the varible is actually (wierdly) faster, depending on the call amount, it becomes quite a lot, with xdebug (so very precise measuring), we are talking about 1,5 (the factor, by using a varible, instead of directly calling the __invoke. so instead , just store the closure in a varible and call it.
Here's another alternative based on the accepted answer but extending stdClass directly:
class stdClassExt extends stdClass {
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
Usage example:
$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();
You are probably better off using call_user_func or __invoke though.
Updated:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
PHP >= 7 :
($obj->callback)();
PHP >= 5.4 :
$callback = $obj->callback;
$callback();
If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..
function run_method($object, Closure $method)
{
$prop = uniqid();
$object->$prop = \Closure::bind($method, $object, $object);
$object->$prop->__invoke();
unset($object->$prop);
}
And you were operating on a class like so..
class Foo
{
private $value;
public function getValue()
{
return $this->value;
}
}
You could run your own logic as if you were operating from within the scope of your object
$foo = new Foo();
run_method($foo, function(){
$this->value = 'something else';
});
echo $foo->getValue(); // prints "something else"
I note that this works in PHP5.5
$a = array();
$a['callback'] = function() {
print "HelloWorld!";
};
$a['callback']();
Allows one to create a psuedo-object collection of closures.

php Slim framework - passing parameter to closure

I am trying to study the code for Slim framework. In the constructor for Slim class, $c is passed to the closure (for instance, when storing/setting the request/response object in the container):
public function __construct(array $userSettings = array())
{
// Setup IoC container
$this->container = new \Slim\Helper\Set();
$this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings);
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
// Default response
$this->container->singleton('response', function ($c) {
return new \Slim\Http\Response();
});
But $c is not defined/declared anywhere prior to this statement, so how does it work? I started to trace everything from the beginning and I can't find $c anywhere prior to it being used in this manner.
$c is a parameter of the closure function. Imagine you had a function by itself:
function myFunction($c) {
echo $c;
}
In the case of a closure, you can store an anonymous function in a variable:
$someFunction = function ($c) {
echo $c;
}
$someFunction("hello world");
So instead of directly storing the closure into the variable, the code above is passing the anonymous function as a parameter to $this->container->singleton(). So $c is not created until the closure is called. The singleton method stores this in a variable called $value, so if that function ran:
$value(array('environment'=>'test'));
$c would now contain array('environment'=>'test')
Slim also uses the __get() __set() magic methods quite a bit, so from the example code you set, within the Slim class, one could call:
$request = $this->container->request(array('environment'=>'test'));
The container is of class Slim\Helper\Set. Since this doesn't have a request method, this would call the container's __get() method. It would look up the stored method configured above for 'request' and pass the array in as $c
I think khartnett gave a perfect answer. To make it clearer for you, an example.
When you define a function, you are only describing how it works. You are not setting any specific values. For example, when I write:
function sum($a, $b) {
return $a + $b;
}
I am not saying what the values of $a and $b are here. I am just describing what I am doing with these variables to calculate a result. It is not until I call this function that I'm working with actual values:
sum(3, 4); // returns 7
In your question, the $c variable is like the $a and $b variable.
Like khartnett showed in his answer, it works like this:
// Definition time
$someFunction = function ($c) {
echo $c;
}
// Calling time
$someFunction("hello world");
It is not until calling time that $c gets its value (in this example, the value is "hello world").
The $c is a reference to the container itself - so that any dependencies will be automatically resolved when invoked.
So using for example the request object:
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
Looking at the Request constructor you will see that it expects an instance of Environment, which we just told the container should be available already using the key 'environment'.
The answer will lay in $app->run():
$this->container->get("service_name") then in get() method offsetGet($id)
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this); // THIS LINE CALLS THE CONTAINER ITSELF
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
Pimple Container functioning it's a bit more complicated due to the fact:
Allowing any PHP callable leads to difficult to debug problems
as function names (strings) are callable (creating a function with
the same name as an existing parameter would break your container)
So unique identifiers are introduced.
Here is a simple implementation of ServiceContainer get() method inside the ServiceContainer class:
public function get($serviceName)
{
if (!array_key_exists($serviceName, $this->container)) {
throw new \http\Exception\InvalidArgumentException("Service not found!");
}
$service = $this->container[$serviceName];
if (is_callable($service)) {
$this->container[$serviceName] = $service = $service($this);
// $this (aka ServiceContainer) will be passed as parameter to Closures
}
return $service;
}
Hope it clarifies the question, have a nice day!

static variable in php closure defined within a private class method

So I re-wrote this to hopefully be clearer about what I'm attempting. During some parsing of data someValue(s) associated with someCode(s) are received. The intent is to capture a particularValue of a someCode having the lowest precedence, irrespective of the order the someCode/someValue (s) received. The issue keeping this from working is that the closure is created every time the private function is called and the value of $precedenceOfCodeCaptured therefore is always reset to null. If I could keep the closure around then things might work as intended.
private function Foo($particularValue, $someValue, $someCode) {
switch ($someCode) {
case:
CODE1:
case:
CODE2:
$c = function () use ($someCode, $someValue) {
static $precedenceOfCodeCaptured = null;
$precedenceArray = array(
CODE2 => 1,
CODE1 => 2
);
if ((is_null($someValue))) {//first time the case statement matched because $someValue==null
$precedenceOfCodeCaptured = $precedenceArray[$someCode];
$particularValue = someValue;
} else if ($precedenceArray[$someCode] <= $precedenceOfCodeCaptured) {
$particularValue = someValue;
}
};
$c();
break;
...
}
}//end of private method
Every time Foo is called, you create a new function/closure, since you evaluate a function(), and that's what evaluating function() does!
If you'd like to have Foo() return the same function/closure each time, only evaluate it once. Perhaps use a singleton pattern, where you'll check if some higher-scoped variable that holds the function has been initialized, and initialize it if not.
For example:
$c = null;
private function Foo(){
if ($c == null) {
$c = function() use ( $whatever){
static $x = 0;
echo "x= $x";
$x++;
...
};
}
$c();
}//end of private method
(but please don't use globals in your actual code!)
Try this:
static $x = 0;
private function Foo() {
$c = function() use ( $whatever){
echo "x= $x";
self::$x++;
...
};
$c();
}
<?php
class example{
private function foo(){
static $_closure;
if( ! $_closure ){
$_closure = function(){
static $_state;
/* . . . */
};
}
$_closure();
}
}
Note that the closure is static (it is created on first use, and not again), as is the variable that holds state inside the closure.
However, as stated in other answers, this is "pointless" at best. $_closure should be another class method; $_state should be a class property (there is no need for static anything in the closure, in foo(), nor in the class). It is not "cleaner" or more readable or performant (in fact, the opposite is likely true) to use a closure like this.
It might make sense if you were doing something like:
returning the closure for use elsewhere
dynamically creating the function at runtime (i.e., based on foo() args)
something else dynamic and interesting
…but as it stands, this is simply the wrong approach. The tools to do this already exist.

php closures: why the 'static' in the anonymous function declaration when binding to static class?

The example in the php documentation on Closure::bind include static on the anonymous function declaration. why? I can't find the difference if it is removed.
with:
class A {
private static $sfoo = 1;
}
$cl1 = static function() { // notice the "static"
return self::$sfoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
echo $bcl1(); // output: 1
without:
class A {
private static $sfoo = 1;
}
$cl1 = function() {
return self::$sfoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
echo $bcl1(); // output: 1
I beg to differ. It's true that often it won't matter. But sometimes it matters a whole lot.
The closure holding a reference to $this might prevent garbage collection of that object, which in turn may also impact performance significantly. Here's an example where it really makes a huge difference:
class LargeObject {
protected $array;
public function __construct() {
$this->array = array_fill(0, 2000, 17);
}
public function getItemProcessor(): Closure {
// Try with and without 'static' here
return static function () {
// do some processing unrelated to $this
};
}
}
$start = microtime(true);
$processors = [];
for ($i = 0; $i < 2000; $i++) {
$lo = new LargeObject();
$processors[] = $lo->getItemProcessor();
}
$memory = memory_get_usage() >> 20;
$time = (microtime(true) - $start) * 1000;
printf("This took %dms and %dMB of memory\n", $time, $memory);
Here's the output with a normal closure:
This took 55ms and 134MB of memory
Here's the output with a static closure:
This took 22ms and 1MB of memory
I tested this with PHP 7.3.19 on Debian Buster, so YMMV. Obviously this is a specially constructed example to demonstrate the difference. But things like this may happen in real applications as well. I started using Slevomat's SlevomatCodingStandard.Functions.StaticClosure sniff to remind me to always use static closures.
found the difference: you can't bind static closures to object, only change the object scope.
class foo { }
$cl = static function() { };
Closure::bind($cl, new foo); // PHP Warning: Cannot bind an instance to a static closure
Closure::bind($cl, null, 'foo') // you can change the closure scope
Static Closures, like any other static method, cannot access $this.
Like any other method, a non-static Closure that does not access $this will generally work in a static context.
As you've noticed, it doesn't really matter.
It's like using the static keyword on a class method. You don't necessarily need it if you don't reference $this within the method (though this does violate strict standards).
I suppose PHP can work out you mean the Closure to access A statically due to the null 2nd argument to bind()

Returning a reference to an object in PHP

I'm using PHP 5.2.14 and PearLog 1.12.3.
The latest documentation from the singleton method in Log.php (PEARLog) states:
You MUST call this method with the
$var = &Log::singleton()syntax.
Without the ampersand (&) in front of
the method name, you will not get a
reference; you will get a copy.
However, doing so generates the following warning:
STRICT NOTICE: Only variables should
be assigned by reference
The source for this function is:
public static function singleton($handler, $name = '', $ident = '',
$conf = array(), $level = PEAR_LOG_DEBUG)
{
static $instances;
if (!isset($instances)) $instances = array();
$signature = serialize(array($handler, $name, $ident, $conf, $level));
if (!isset($instances[$signature])) {
$instances[$signature] = Log::factory($handler, $name, $ident,
$conf, $level);
}
return $instances[$signature];
}
If I remove the & and use just:
$var = Log::singleton()
then I no longer get the warning. Also, if I do
$var = Log::singleton();
$var2 = Log::singleton();
then $var === var2 evaluates to true.
Question: Which is correct: the API documentation or the warning? (If the function returns an object, isn't it a reference anyway? Why would I need the ampersand ?
The way that objects are passed fundamentally changed in PHP5. In PHP4 they were always passed by value, meaning that a function or method that returned an object was actually passing a copy of the object back. This led to the use of the '&' operator, forcing the function to return an object by reference. In PHP5 objects are always passed by reference. To create a copy of an object you have to use the clone operator.
From having a very quick look at the source for the log package it appears that it maintains compatibility with PHP4. I don't think that you need the ampersand. PHP5 will return a reference to the object. Your test of '$var === $var2' has proved that the method returns an object and that the object is a reference to one object. If they were copies of an object the identity comparison would evaluate to false.
The warning is correct and the API documentation is outdated, objects are returned by reference since PHP5.
The way to deal with references changed a bit in PHP in PHP 5. Now they want the called function to decide to return by reference or by value, not the caller.
But usually PHP can sort this out by itself - like in your case it does detect that these two are the same object.
For more information about your E_STRICT check out the manual: http://www.php.net/manual/en/language.references.whatdo.php and how the pear function should be implemented: http://www.php.net/manual/en/language.references.return.php . (In my oppinion most parts of pear are outdated, the zend framework covers most of pear now.)
Edit: Large example of references:
error_reporting(E_STRICT);
ini_set('display_errors', 1);
class Settings
{
private static $_instance;
public static function getInstance()
{
if(self::$_instance == null)
{
self::$_instance = new Settings();
}
return self::$_instance;
}
public static function &getInstanceByRef()
{
if(self::$_instance == null)
{
self::$_instance = new Settings();
}
return self::$_instance;
}
private $counter = 0;
private function Settings()
{
}
public function getCounter()
{
return $this->counter;
}
public function setCounter($value)
{
$this->counter = $value;
}
}
$settings1 = Settings::getInstance();
$settings2 = Settings::getInstance();
echo $settings1->getCounter(); // 0
echo $settings2->getCounter(); // 0
$settings1->setCounter(42);
echo $settings1->getCounter(); // 42
echo $settings2->getCounter(); // 42
$settings3 = &Settings::getInstanceByRef(); // ref to private static $_instance !
$settings4 = &Settings::getInstanceByRef(); // ref to private static $_instance !
echo $settings3->getCounter(); // 42
echo $settings4->getCounter(); // 42
$settings3 = 5;
$settings5 = Settings::getInstance();
echo $settings5; // 5
As you can see even without refence getInstance is handled as reference. If you want to use references, both the caller and the called function must be marked as reference.
As a warning: return by reference can cause hard to find bugs: Returning by reference allows me to overwrite the private instance holding var. The expected behaviour in PHP would be that $settings3 is 5 but not the private static $_instance; This can result in very unpredictable code.

Categories