Calling a changing function name on an object with PHP : how? - php

How would I do something like this :
class Test
{
public function test($methodName) {
$this->$methodName;
}
private function a() {
echo("a");
}
private function b() {
echo("b");
}
}
$testObj = new Test();
$testObj->test("a()");
$testObj->test("b()");
Maybe I should just pass a parameter "TYPE" and use a "IF statement" but I'm just curious! :)
And what if the "dynamic function name" has one or more parameters?
UPDATE : Thanks everyone! :)
UPDATE #2 - Answer :
class Test
{
public function testOut($methodName) {
$this->$methodName();
}
private function a() {
echo("a");
}
private function b() {
echo("b");
}
}
$testObj = new Test();
$testObj->testOut("a");
$testObj->testOut("b");
The problem with the class is that there was a method named "Test" (the same as the class name)... I changed it and it worked.

class Test
{
public function test($methodName) {
$this->$methodName();
}
private function a() {
echo("a");
}
private function b() {
echo("b");
}
}
$testObj = new Test();
$testObj->test("a");
$testObj->test("b");

Check out call_user_func() - it should do what you want.
Documentation here

call_user_func allows you to do things like this.
For example,
funcname = 'a';
call_user_func(array($testObj, $funcname));
The other alternative is to use variable methods
For example,
$funcname = 'a';
$testObj->$funcname();

If you have a "dynamic function name" with more than one parameter, you can use call_user_func_array, like this:
//in class context..
//$parameters can be an array like array(1,2)
call_user_func_array(array($this, $methodName), $parameters);
The reason you would want to use call_user_func_array instead of call_user_func is that you can pass the parameters the function takes as an array, instead of just as additional parameters, thus allowing you to easily have a variable number of arguments.

Following your particular example and use case, the only way to achieve this exactly:
$testObj = new Test();
$testObj->test("a()");
$testObj->test("b()");
would be to use eval():
class Test
{
public function test($methodName) {
eval($methodName);
}
private function a() {
echo("a");
}
private function b() {
echo("b");
}
}
$testObj = new Test();
$testObj->test("a()");
$testObj->test("b()");
Others pointed out a solution using call_user_func() and rightly so. Also evidently, is faster than using eval() for function calls. Read this:
http://nz.php.net/manual/en/function.call-user-func.php#64415

Related

Class variables holding a function in PHP

PHP allows for variables to hold functions like so:
$f = function($a,$b) {
print "$a $b";
};
$f("Hello","World!"); //prints 'Hello World!'
This works just fine for me. I'm trying to pass a function into a class and set an instance variable to hold that function but with little luck:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
Am I doing something wrong here? The error is that the function doesn't exist so my guess currently is that it looks for a class function with that name (which there isn't one) and then gives up rather than looking for variables as well... how can I make it view the $this->distanceFunc as a variable?
EDIT:
So after the advice from the answers below, I found a solution which was the make a function to wrap the invocation. For example my class is now:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
private function distanceFunc($a,$b) {
$holder = $this->distanceFunc;
return $holder($a,$b);
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
and this works great. Php looks for functions first and can only tell if it is a variable by context I guess is the moral of this story.
Your code doesn't work because PHP interprets $this->distanceFunc(1,7) as a class method, but you can do the following:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func_array($this->distanceFunc, array(1, 7));
// print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
http://sandbox.onlinephpfunctions.com/code/cdc1bd6bd50f62d5c88631387ac9543368069310
In PHP, methods and properties of an object occupy separate namespaces. This is different from JavaScript, for example, where foo.bar = function() {} is a perfectly valid way of defining a method.
Consequently, $this->distanceFunc(1,7); looks for a method named distanceFunc on the current class, and the classes it inherits from, but never looks for the property which you happen to have given the same name.
One solution is to force PHP to look up a property, then execute it, e.g. $foo = $this->distanceFunc; $foo(1,7) or call_user_func($this->distanceFunc, 1, 7)
Another would be to define the magic method __call on your class, which gets run whenever a non-existent method is referenced. Something like this ought to work (I don't have an easy way to testright now):
function __call($func, $args) {
if ( property_exists($this, $func) && is_callable($this->$func) ) {
return call_user_func_array($this->$func, $args);
}
}
Note that this still isn't the same as a real method, for instance in terms of access to private properties.
It looks like you're going for a strategy pattern here. IE you want to be able to inject different methods for calculating distance? If so there is a more "sane" way to do it.
You can define an interface to the classes you will use to store the strategy method ensuring that the class will always have the method calculate() for example which would be your distance calculation function. Then in the constructor of your Clusterer class, type check against the interface in the parameter and call calculate() on the object passed in.
Looks like this:
interface Calculateable
{
public function calculate();
}
class MyDistanceCalculator implements Calculateable
{
public function calculate()
{
// Your function here
}
}
class Clusterer
{
protected $calc;
public function __construct(Calculateable $calc)
{
$this->calc = $calc;
$this->calc->calculate();
}
}
$myClusterer = new Clusterer(new MyDistanceCalculator());
Because you defined an interface, any object you pass in will have the calculate() function
In HHVM, you can do this:
<?php
class Foo
{
public function __construct()
{
$this->bar = function() { echo "Here\n"; };
($this->bar)();
}
}
new Foo();
But it's not yet supported in PHP. But, it will be in PHP 7 (there will be no release named PHP 6).
PHP doesn't have first class functions. In JavaScript if you returned a function you could do this: myFunctionThatReturnsAFunction()(1,2), but not in PHP.
<?php
class Clusterer {
private $distanceFunc;
public function __construct(Closure $f) {
$this->distanceFunc = $f;
}
public function getDistFunc()
{
return $this->distanceFunc;
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
$a = $c->getDistFunc();
echo $a(1,2);
Take a look at call_user_func
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func($this->distanceFunc, 1, 7); //works too ;)
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
Don't ask me what is the difference, but it works the way you want (One of the reasons i hate this language)

Call static function from variable

I have the following setup:
class test {
public static function something() {
$somethingElseFunction = "somethingElse";
// How can I call the method with the name saved in variable?
}
public static function somethingElse($a) {
echo 'bla';
}
}
How can I call the function using the variable? (the function name is in variable).
Also I need to do a function_exists() for it.
Tried this:
if (function_exists(self::$somethingElseFunction ())) {
if (!call_user_func(self::$somethingElseFunction , $a)) {
}
}
Didn't work.
In PHP>=5.4 you can use just self:: de-reference:
self::$somethingElseFunction();
-but in earlier versions that will cause error (because it wasn't allowed to use dynamic static methods de-reference). So then you can always use such things as call_user_func_array() :
class test {
public static function something() {
$somethingElseFunction = "somethingElse";
call_user_func_array(array(__CLASS__, $somethingElseFunction), array("bla"));
}
public static function somethingElse($a) {
var_dump($a);
}
}
test::something();
-this will work for PHP>=5.0
About function_exists() call - it expects string as parameter, thus I recommend to use method_exists() - because that function is intended to do the stuff:
public static function something() {
$somethingElseFunction = "somethingElse";
if(method_exists(__CLASS__, $somethingElseFunction))
{
call_user_func_array(array(__CLASS__, $somethingElseFunction), array("bla"));
}
}
You should be able to use the following:
test::$somethingElseFunction();
Use this function:
$classname = 'somethingElse';
call_user_func('test::' . $classname, $params);

Convert static method to lambda in PHP

I want to get static method from class and copy it to variable.
This is non-working example illustrating my question:
class foo
{
public static function bar($argument){ return 2*$argument; }
}
$class = new ReflectionClass('foo');
// here is no ReflectionMethod::getClosure() method in reality
$lambda = $class->getMethod('bar')->getClosure();
echo $lambda(3);
So my question: is this possible by any normal way? I find only one way for now. I can parse source file, get method source from it and convert it using create_function() but it's too perverse.
Just wrap it with closure.
$lamda = function($argument){return foo::bar($argument);};
Or you can try to use something like this
function staticMethodToClosure($class, $method) {
return function($argument)use($class, $method){return $class::$method($argument);};
}
An array in the format array($className, $methodName) is invokable as a static method call so this may work for you.
class foo
{
public static function bar($argument){ return 2*$argument; }
public static function getStaticFunction($arg){
return array("foo", $arg);
}
}
$a = foo::getStaticFunction("bar");
echo $a(5); // echos 10

it is possible to assign a function to a class variable at runtime in php?

it is possible to assign to a class variable a function at runtime to be executed? a kind of "function pointer" like C
something like this: (this won't work because sum is out of the scope of A, but this is the pattern i mean)
class A {
public $function_name;
public functon run($arg1,$arg2){
$function_name($arg1,$arg2);
}
}
function sum($a,$b){
echo $a+$b;
}
$a=new A();
$a->function_name='sum';
$a->run();
[edit]
i know there is "call_user_func" but it need as i understand to have the function in the scope or use a public class method
You could use an anonymous function if you use PHP >5.3.0:
$sum = function($a, $b) {
return $a+$b;
}
$a->function_name = $sum;
Using call_user_func_array:
<?php
class A {
public $function_name;
public function run($arg1,$arg2){
return call_user_func_array( $this->function_name, array($arg1, $arg2 ) );
}
}
function sum($a,$b){
return $a+$b;
}
$a=new A();
$a->function_name= 'sum';
var_dump( $a->run(1,1) ); //2
?>
It works regardless of scope. You just gotta call it using call_user_func. I also fixed a couple of typos in your example.
<?php
class A {
public $function_name;
public function run($arg1, $arg2) {
call_user_func($this->function_name, $arg1, $arg2);
}
}
function sum($a, $b){
echo $a + $b;
}
$a = new A();
$a->function_name = 'sum';
$a->run(2, 3);
?>
Live example
Another way is to make use variable variables (applicable to object method)
public static function sum($arg1, $arg2)
{
..
}
public function run($arg1, $arg2)
{
$func = $this->function_name;
$func( $arg1, $arg2); <-- procedural call
self::$func($arg1, $arg2); <-- static method call
}
Use any variation of the Callback pseudo type.
Use it with call_user_func or call_user_func_array
The manual gives great examples of usage for the above.
Also see the new php 5.4 Closure::bindTO method if you want to be able to easily use the $this keyword in it.

Is it possible to determine whether a method was called from inside or outside of a class?

Example code:
class MyClass {
function echo_msg {
echo // now what...
}
function echo_from_inside {
$this->echo_msg()
}
}
result should be:
$my_instance = new MyClass();
$my_instance->echo_msg(); // I was called from OUTside
$my_instance->echo_from_inside(); // I was called from INside
It might be easier, rather than detecting from whence the function was called, to wrap a private function with a public one. Like so:
class MyClass{
private function myob(){
//do something
}
public function echo_msg(){
$this->myob();
//do other stuff like set a flag since it was a public call
}
private function foo(){ //some other internal function
//do stuff and call myob
$this->myob();
}
}
$obj=new MyClass();
$obj->echo_msg();//get output
$obj->myob(); //throws error because method is private
You can try and get the caller of your method:
$trace = debug_backtrace();
$caller = array_shift($trace);
echo 'called by '.$caller['function']
echo 'called by '.$caller['class']
this should work for you.
You could add an optional parameter like such:
function echo_msg($ins=false) {
if($ins){/*called from inside*/}else{/*called from outside*/}
echo // now what...
}
and leave that last. If you are calling it from inside the class, pass it true, otherwise pass nothing!

Categories