I would like to assign a static function to a variable so that I can send it around as a parameter. For example:
class Foo{
private static function privateStaticFunction($arg1,$arg2){
//compute stuff on the args
}
public static function publicStaticFunction($foo,$bar){
//works
$var = function(){
//do stuff
};
//also works
$var = function($someArg,$someArg2){
//do stuff
};
//Fatal error: Undefined class constant 'privateStaticFunction'
$var = self::privateStaticMethod;
//same error
$var = Foo::privateStaticFunction;
//compiles, but errors when I try to run $var() somewhere else, as expected
//Fatal error: Call to private method Foo::privateStaticMethod() from context ''
$var = function(){
return Foo::privateStaticMethod();
};
}
}
I've tried a few more variations but none of them worked.
I don't even expect this sort of functional hacking to work with PHP but hey, who knows?
Is it possible to do that in PHP or will I need to come up with some hack using eval?
P.S.: LawnGnome on ##php mentioned something about it being possible to do what I want using array('Foo','privateStaticMethod') but I didn't understand what he meant and I didn't press him further as he looked busy.
You want to use call_user_func with an array argument.
$var = array('Foo','privateStaticMethod');
call_user_func($var);
Note that the "private" specifier on that method will make it only callable this way within the Foo class.
call_user_func takes a callable as the first argument. Note that as of PHP 5.2.3, it is possible to avoid the array notation in your case.
Static class methods can also be passed without instantiating an
object of that class by passing the class name instead of an object at
index 0. As of PHP 5.2.3, it is also possible to pass
'ClassName::methodName'.
As a note, you should set your static variable as so:
static $privateStaticMethod;
$var = self::$privateStaticMethod;
Leaving off a $ when using self:: will try to access a class CONSTANT and not a variable.
In actuality, I think you meant this line to be:
//Fatal error: Undefined class constant 'privateStaticFunction'
$var = self::privateStaticFunction("arga", "argb");
If you are on PHP 5.4 you can do the following within your publicStaticFunction:
$var = function(){
return self::privateStaticFunction();
};
$var();
Within PHP 5.4, PHP allows you to access class scope from lambda functions.
Also, have you looked into ReflectionClass?
The following would be used to replace call_user_func_array() in a more OOP style:
<?php
$rc = new ReflectionClass('SomeClass');
$class = $rc->newInstanceArgs(array('foo', 'bar'));
echo $class->doSomething();
You could write your own class to use ReflectionMethod which implements ReflectionClass and use setAccessible() to allow access to protected and private methods.
Here is what I did to solve my issue
class test{
static function Unix($arr){return implode("/",$arr);}
static function Windows($arr){return implode("\\",$arr);}
function test(){
$slash = "Unix";
echo self::$slash(["path","to","a","folder"]);
}
}
Related
In PHP, I have a class that takes in a function object and calls that function in a thread: see the answer to this question.
It works well with anonymous functions, however, I want to use this functionality with a static function of a different class:
$thThread = new FunctionThreader(OtherClass::static_function, $aParams);
This throws an error Undefined class constant static_function on line x
I have tried:
$fFunction = OtherClass::static_function;
$thThread = new FunctionThreader($fFunction, $aParams);
And I get the same error.
So is there anyway to store this static function into $fFunctionor to simply reference it as a function object?
In PHP you would usually use a callback for this:
$callback = array('ClassName', 'methodName');
$thThread = new FunctionThreader($callback, $aParams);
If FunctionThreader::__construct() does only accept a Closure and you have no influence on it's implementation, then you can wrap the static function call in a Closure:
$closure = function() { return ClassName::methodName(); };
$thThread = new FunctionThreader($closure, $aParams);
You could define a lambda function which calls your static function. Or simply store function name as a string in that variable. You would also have to implement a handler code for the class that calls a function from variable.
Resources:
http://php.net/manual/en/function.forward-static-call-array.php
http://ee1.php.net/manual/en/function.func-get-args.php
Example for anonymous function:
<?php
$function = new function(){
return forward_static_call_array(['class', 'function'], func_get_args());
};
?>
Testes this, works flawlessly.
Say I'm here:
class Foo
{
public function test()
{
// I'm here!
}
private function pow() {}
}
I want to declare a local variable that references the method $this->pow. I try:
$pow = $this->pow;
This doesn't work:
Notice: Undefined property: Foo::$pow
How do I reference a class instance method in PHP?
I need this because I need to pass the function to an anonymous function inside test. I can't pass $this to the anonymous function because I'm using PHP 5.3, and that only became possible in PHP 5.4. Also, the function pow is not public, so I can't assign $this to an intermediary variable and pass that to the anonymous function.
If pow is public as it is in your example, then you can simply do this:
$self = $this;
$invokePow = function() use ($self) { $self->pow(); };
// ... some time later ...
$invokePow(); // calls pow()
You can also do the same by passing around array($this, 'pow') as a callable and invoking it with call_user_func.
If pow is not public, you are simply out of luck.
However, if you are able to upgrade to 5.4 then it all becomes much easier:
$invokePow = function() { $this->pow(); };
You don't need to capture $this explicitly, and you can invoke $invokePow from any context (even outside the class).
The following PHP code results in an error: unexpected T_FUNCTION on the line starting with "say". I'm using PHP version 5.3.6 so lambdas should be supported, but it's not working. I don't know if the "use" clause is valid in this context either, but removing it does not resolve the problem. Is something wrong with my syntax? Note: $this->backend is defined in the constructor as $this->backend = fopen("bar.txt","w+");
class Foo
{
private $backend;
private $commands = array(
0 => array(
"say" => function($msg) use($this->backend) { fwrite($this->backend,$msg); }
)
);
}
Specific to PHP 5.3.x
First off, it's not possible to assign lambdas as default property values (lambdas are not considered a constant expression). So it is not possible to make that assignment directly; you would need to do it inside the constructor.
Secondly, in PHP 5.3.x you cannot use $this inside a lambda. The typical workaround is to make a copy of $this and capture that, as in:
$that = $this;
$func = function() use($that) { ... };
However this way it's not possible to access non-public members of $that from inside the lambda at all, so the technique cannot be used directly in your case.
What you should probably do is store $this->backend in a local variable inside the constructor and use that variable inside the lambda. Both the store and the capture can be done by value or by reference, depending on if you want any modifications to propagate outside the lambda and the possibility that the value of $this->backend may change before the lambda is invoked:
public function __construct() {
$backend = $this->backend;
$this->commands = array(
'say' => function($msg) use($backend) { fwrite($backend, $msg); }
);
}
Later PHP versions
Starting from PHP 5.4.0 you can implicitly use $this inside a lambda defined within a class method:
public function __construct() {
$this->commands = array(
'say' => function($msg) { fwrite($this->backend, $msg); }
);
}
The restriction that lambdas cannot be assigned as default property values still stands.
The problem is
use($this->backend)
$this doesn't exist yet in your class blueprint, so you absolutely can't specify it as this point.
What you're trying to do may be impossible to do inside an anonymous function, as it's not a member of the class as such, and hence doesn't get the $this variable.
You may have to do this in a proper member function of Foo, or pass $this as a parameter.
You cannot define properties in classes directly from variables or as lambda;
// these are incorrect
private $foo = $bar
private $callback = function() ...
// correct
define("BAR", "The bar!");
class A {
private $foo = BAR;
private $commands = array();
...
public function __construct() {
$this->commands[0] = function() ...
Consider the following code, which is a scheme of storing a callback function as a member, and then using it:
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return $this->callback();
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass(myFunc);
echo $o->makeCall();
I would expect myFunc was here to be echoed, but instead I get:
Call to undefined method MyClass::callback()
Can anyone explain what's wrong here, and what I can do in order to get the desired behaviour?
In case it matters, I am using PHP 5.3.13.
You can change your makeCall method to this:
function makeCall() {
$func = $this->callback;
return $func();
}
Pass it as a string and call it by call_user_func.
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return call_user_func($this->callback);
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass("myFunc");
echo $o->makeCall();
One important thing about PHP is that it recognises the type of a symbol with the syntax rather than the contents of it, so you need to state explicitly what you refer to.
In many languages you just write:
myVariable
myFunction
myConstant
myClass
myClass.myStaticMethod
myObject.myMethod
And the parser/compiler knows what each of the symbols means, because it's aware of what they refer to simply by knowing what's assigned to them.
In PHP, however, you need to use the syntax to let the parser know what "symbol namespace" you refer to, so normally you write:
$myVariable
myFunction()
myConstant
new myClass
myClass::myStaticMethod()
$myObject->method()
However, as you can see these are calls rather than references. To pass a reference to a function, class or method in PHP, combined string and array syntax is used:
'myFunction'
array('myClass', 'myStaticMethod')
array($myObject, 'myMethod')
In your case, you need to use 'myFunc' in place of myFunc to let PHP know that you're passing a reference to a function and not retrieving the value the myFunc constant.
Another ramification is that when you write $myObject->callback(), PHP assumes callback is a method because of the parentheses and it does not attempt to loop up a property.
To achieve the expected result, you need to either store a copy of/reference to the property callback in a local variable and use the following syntax:
$callback = $this->callback;
return $callback();
which identifies it as a closure, because of the dollar sign and the parentheses; or call it with the call_user_func function:
call_user_func($this->callback);
which, on the other hand, is a built-in function that expects callback.
Just wondering why something like this doesn't work:
public function address($name){
if(!isset($this->addresses[$name])){
$address = new stdClass();
$address->city = function($class = '', $style = ''){
return $class;
};
$this->addresses[$name] = $address;
}
return $this->addresses[$name];
}
Calling it like echo $class->address('name')->city('Class') should just echo Class, however I get Fatal error: Call to undefined method stdClass::city()
I can find a better way to do this, because this will get messy, but I'm wondering what I might be doing wrong there, or if PHP doesn't support this and why.
PHP is right when invoke fatal error Call to undefined method stdClass::city() because object $class->address('name') has no method city.
Intead, this object has property city which is instance of Closure Class (http://www.php.net/manual/en/class.closure.php)
You can verify this: var_dump($class->address('name')->city)
I found the way to call this anonymous function is:
$closure = $class->address('name')->city;
$closure('class');
Hope this helps!
Sadly it is not possible within stdClass, but there is a workaround -- PHP Anonymous Object.
// define by passing in constructor
$anonim_obj = new AnObj(array(
"foo" => function() { echo "foo"; },
"bar" => function($bar) { echo $bar; }
));
$anonim_obj->foo(); // prints "foo"
$anonim_obj->bar("hello, world"); // prints "hello, world"
AFAIK, this is not supported by PHP, and you must use the call_user_func() or call_user_func_array() functions to call closures assigned to class properties (usually you can use __call() to do this, but in your case, the class is stdClass, so this isn't possible).