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).
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.
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() ...
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"]);
}
}
The code:
public function couts_complets($chantier,$ponderation=100){
function ponderation($n)
{
return($n*$ponderation/100); //this is line 86
}
...
}
What I'm trying to do: to declare a function B inside a function A in order to use it as a parameter in
array_map().
My problem: I get an error:
Undefined variable: ponderation [APP\Model\Application.php, line 86]
Try this:
public function couts_complets($chantier,$ponderation=100){
$ponderationfunc = function($n) use ($ponderation)
{
return($n*$ponderation/100);
}
...
$ponderationfunc(123);
}
As of php 5.3 you can use anonymous functions. Your code would look like this (untested code warning):
public function couts_complets($chantier,$ponderation=100) {
array_map($chantier, function ($n) use ($ponderation) {
return($n*$ponderation/100); //this is line 86
}
}
In your current code, $ponderation is not covered by the scope of the function, hence the "undefined" error.
To pass a variable to an "internal" function, use the use statement.
function ponderation($n) use($ponderation) {
Using a callback function:
In order to use a function as a parameter in PHP it is enough to pass the function's name as a string as such:
array_map('my_function_name', $my_array);
If the function is actually a static method in a class you can pass it as a parameter as such:
array_map(array('my_class_name', 'my_method_name'), $my_array);
If the function is actually a non-static method in a class you can pass it as a parameter as such:
array_map(array($my_object, 'my_method_name'), $my_array);
Declaring a callback function:
If you declare in the global space all is good and clear in the world - for everybody.
If you declare it inside another function it will be global but it won't be defined until the parent function runs for the first time and it will trigger an error Cannot redefine function my_callback_function if you run the parent function again.
If you declare it as a lambda function / anonymous function you will need to specify which of the upper level scope variables it is allowed to see/use.
Calling a callback:
function my_api_function($callback_function) {
// PHP 5.4:
$callback_function($parameter1, $parameter2);
// PHP < 5.3:
if(is_string($callback_function)) {
$callback_function($parameter1, $parameter2);
}
if(is_array($callback_function)) {
call_user_func_array($callback_function, array($parameter1, $parameter2));
}
}
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);