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.
Related
Here is a hypothetical example (the parent class PageState, contains an instance of the class FooterState - the instance may not be created, depending on the conditions. The FooterState needs to call a function which is public and is created in the PageState class):
class PageState {
private $footer_state = null;
function PageState() {
$this->footer_state= new FooterState($this);
}
public function getExpectedPageDimensions() {
// do calculations based on existing body content
return $dimensions;
}
}
class FooterState {
private $get_dimensions_func = null;
function FooterState($page_state) {
// Here, we need to get the reference to the function from the $page_state class
$this->get_dimensions_func = $page_state->getExpectedPageDimensions;
}
public function addLogos($logo_data) {
$page_dimensions = $this->get_dimensions_func();
// use the page dimensions to decide on the size of the content
return Array('width' => $width, 'height' => $height);
}
I am aware of alternative solutions:
Instead of making a copy of the reference to the function, create a refference to the class $this->page_state = $page_state; and then functions in FooterState can call $this->page_state->getExpectedPageDimensions();
Use global $PageStateInstance; and then just call $PageStateInstance->getExpectedPageDimensions();
But I am wondering if it is at all possible to store a reference to a class function in a variable. If the functions were outside of the class, it would be possible to do stuff like $func = 'getExpectedPageDimensions'; $func();.
You can pass on an instance plus a function as a callable: An array with the instance and the function name. There is a similar system for calling static class methods.
# An example callback method
class MyClass {
function myCallbackMethod() {
echo 'Hello World!';
}
}
# create an instance
$obj = new MyClass();
# and later:
call_user_func(array($obj, 'myCallbackMethod'));
From the docs here: http://php.net/manual/en/language.types.callable.php
Instead of making a copy of the reference to the function, create a refference to the class $this->page_state = $page_state; and then functions in FooterState can call $this->page_state->getExpectedPageDimensions();
This is the best generic solution.
But I am wondering if it is at all possible to store a reference to a class function in a variable.
Yes it is, but it really only works for static functions unless you instantiate the class. Example:
class A {
public static function doSomethingStatic() {
// ...
}
public function doSomethingElse() {
// ...
}
}
$somevar = 'A::doSomethingStatic';
$result = call_user_func($somevar); // calls A::doSomethingStatic();
$myA = new A();
$myref = array($myA, 'doSomethingElse');
$result = call_user_func($myref); // calls $myref->doSomethingElse();
Note that in the second example you have to instantiate the class and pass an array as the first parameter to call_user_func().
References: http://php.net/manual/en/function.call-user-func.php and http://php.net/manual/en/language.types.callable.php
is at all possible to store a reference to a class function
I think you mean object instead of class, but yes you can, with closures.
I don't think you need to though. $this->page_state seems like it'll work just fine.
Don't use globals.
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).
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"]);
}
}
ok I do have the code below
<?php
class foo{
public $bar = NULL;
public function boo(){
$this->bar();
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
?>
and you can see it running here http://codepad.org/s1jhi7cv
now what i want here is to store the closure function on the class method.
well closures are possible as i read the documentation about it here http://php.net/manual/en/functions.anonymous.php
is this possible? did i went to something wrong? please correct me
Your example code at codepad.org does not work because codepad.org uses PHP 5.2.5, and closure support was only added in 5.3.
However, your code will also not work in a PHP version that supports closures, although you will get a different error: http://codepad.viper-7.com/Ob0bH5
This is a limitation of PHP at present. $obj->member() looks for a method named member and will not look at properties to see if they are callable. It is, frankly, annoying.
The only way I am aware of to make this work without call_user_func()/call_user_func_array() is:
public function boo() {
$func = $this->bar;
$func();
}
You need to exploit some magic functionality of PHP (__call) to make use of that. Extend from Extendable for example:
class Extendable {
static function import($context) {
$self = new static();
while (is_callable($context)) $context = $context($self);
if (is_array($context) || is_object($context) || is_a($context, 'Traversable')) {
foreach($context as $key => $value)
$self->$key = &$value; # preserve keys if
}
return $self;
}
public function __call($name, $args) {
if (isset($this->$name) && is_callable($this->$name)) {
return call_user_func_array($this->$name, $args);
}
throw new BadFunctionCallException(sprintf('Undefined function %s.', $name));
}
}
And you can do the job. It's not that nice. Background and examples are in one of my blog posts:
PHP: Extending stdClass with Closures (plus Visitor)
You can naturally implement that magic functionality your own, too.
Use call_user_func() function:
<?php
class foo{
public $bar = NULL;
public function boo(){
call_user_func($this->bar);
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
This will display "ahahah"
Hope it helps.
You will not be able to do that.
Take for example this code:
class T {
function foo() {
echo 'T::foo';
}
}
$t = new T;
$t->foo = function() {
echo 'Closure::foo';
};
$t->foo();
It works fine on PHP 5.4.6 and/or PHP 5.3.16, however it will result in T::foo getting printed.
This happens because methods, in PHP, are not modifiable class properties, as they are for example in javascript.
However,
$foo = $t->foo;
$foo();
will print Closure::foo as expected.
PHP is not a prototype based language hence you cannot redefine functions
Use __call to catch all non-defined methods and then look up the closure and invoke it. Take a look at my post on this SitePoint thread.
Starting at php 7, you can put round brackets around the instance and method to call the method like so: ($this->bar)();.
This appears to cause a syntax error on earlier versions however.
here is an example class:
public class example
{
private $foof;
public function __construct()
{
$this->foof = $this->foo;
}
public function foo($val=0)
{
// do something...
}
}
So basically, in the constructer of the sample code, is it possible to assign a class method to a variable?
Ultimately what i want is to have an associative array with all the class methods aliased in it...that possible in php?
In PHP5.3+ (which you should be using anyway!) you can simply create an anonymous function which calls your method:
$this->foof = function() {
$this->foo(1);
};
However, you cannot call it using $this->foof() - you have to assign it to a variable first: $foof = $this->foof; $foof();
In older PHP versions you cannot easily do this - create_function() does not create a closure so $this is not available there.
You don't need to use anonymous functions. Just use the Callable pseudo type.
$this->foof = array($this, 'foo');
...
call_user_func($this->foof);