Dynamically calling a method in a class - php

I've recently worked on some code in which I had to dynamically call a method inside a class.
The solution I ended up using was 2 lines, because the "dynamic" part was only a small section of the actual method name that I needed to call.
This is the solution I ended up using:
$pull = "pull_{$type}_day";
$day = $download->$pull();
Originally, I tried to make this a single line, but it did not work. In a technical sense, why does the above code work but the below code does not?
$day = $download->"pull_{$type}_day"();

If you use a string or part of a string as a method (or property) you need to surround it in {} like this:
#property
echo $foo->{"bar"};
#method call
echo $foo->{"bar"}();
So if you need a variable in the string part, this follows the same rules as any normal string.
echo $foo->{"bar".$bar}();
echo $foo->{'bar'.$bar.'bar'}();
echo $foo->{"bar{$bar}bar"}();
And so on. Here is a full example
class foo{
function pull_1_day(){
echo "bar";
}
}
$a = 1;
(new foo)->{"pull_{$a}_day"}();
Outputs
bar
Sandbox
This follows in the same way PHP allows you to use a string as a variable such as this:
$foo = 'bar';
echo ${"foo"};
Outputs
bar
Sandbox
Same kind of syntax.

Why not use call_user_func()?
I find it easier to read and parse.
Assuming you have:
class pull {
public function pull_foo_day () {
echo "hello";
}
}
$pull = new pull();
$type = "foo";
You can simply do:
call_user_func([$pull, "pull_{$type}_day"]);
Outputs:
hello
See it working here.

Related

Weird PHP Introspection: Get original variable NAMES from function call?

I have a strange question that's probably not possible, but it's worth asking in case there are any PHP internals nerds who know a way to do it. Is there any way to get the variable name from a function call within PHP? It'd be easier to give an example:
function fn($argument) {
echo SOME_MAGIC_FUNCTION();
}
$var1 = "foo";
$var2 = "bar";
fn($var1); // outputs "$var1", not "foo"
fn($var2); // outputs "$var2", not "bar"
Before you say it - yes, I know this would be a terrible idea with no use in production code. However, I'm migrating some old code to new code, and this would allow me to very easily auto-generate the replacement code. Thanks!
debug_backtrace() returns information about the current call stack, including the file and line number of the call to the current function. You could read the current script and parse the line containing the call to find out the variable names of the arguments.
A test script with debug_backtrace:
<?php
function getFirstArgName() {
$calls=debug_backtrace();
$nearest_call=$calls[1];
$lines=explode("\n", file_get_contents($nearest_call["file"]));
$calling_code=$lines[$nearest_call["line"]-1];
$regex="/".$nearest_call["function"]."\\(([^\\)]+)\\)/";
preg_match_all($regex, $calling_code, $matches);
$args=preg_split("/\\s*,\\s*/", $matches[1][0]);
return $args[0];
}
function fn($argument) {
echo getFirstArgName();
}
$var1 = "foo";
$var2 = "bar";
fn($var1);
fn($var2);
?>
Output:
$var1$var2

Pass parameters to router's controller method

It was kind of hard for me to form this as a title. Basically what I have is a simple laravel-like router that saves allowed locations in an array. My question is if I could supply some arguements dynamically through that array's elements. I think I did even worse in the body of the question so let me just give a simple example:
class Example(){
public function display($bool){
if($bool){
echo 'hey';
}else{
echo 'bye';
}
}
}
Now if you wanna call this class' method from somewhere else with a variable
$var = 'display';
$example = new Example();
$example->$var(true);
And you get 'hey', but what if I can only control the $var = 'display' part, and the code below is beyond my control. Can I still pass a value to that method? Something like
$var = 'display(true)';
$example = new Example();
$example->$var;
Not without some epic string parsing.
More importantly, if you need this, something that you're doing is horribly wrong.

Call function from an object?

<?php
$ar = (object) array('a'=>function(){
echo 'TEST';
});
$ar->a();
?>
I get this error Call to undefined method
Update:
If you are using PHP 5.3 or greater, take a look at other answers please :)
I don't think that's correct syntax, it would give you:
Parse error: syntax error, unexpected T_FUNCTION in....
You need to create a class, add method to it, use new keyword to instantiate it and then you will be able to do:
$ar->a();
class myclass
{
public function a()
{
echo 'TEST';
}
}
$ar = new myclass;
$ar->a(); // TEST
See Classes and Objects for more information.
Anonymous or not, you have a callback function, thus you need to handle it as such. E.g.:
<?php
$ar = (object) array(
'a' => function(){
echo 'TEST';
}
);
call_user_func($ar->a);
?>
For some reason it doesn't seem possibly to run the closure the way you do.
If you modify your code and set another variable to the function, it can be called:
$ar = (object) array('a'=>function(){
echo 'TEST';
});
$a = $ar->a;
$a();
This is no solution. But from what I can see, this seems like a bug or limitation in PHP 5.3.
I am using 5.3.5 when trying this.
There is no function a() but the property a, so you should call it by $ar->a.
Anyway I don't think it's going to work the way you expect it to.
EDIT:
As suggested by Álvaro G. Vicario you should use call_user_func, not echo to call the function and it will work correctly.
Or, just for the fun of it, you can do something like this -
<?php
$ar = new stdClass;
$ar->a = function($to_echo){ echo $to_echo; };
$temp = $ar->a;
//[Edit] - $ar->a("hello"); // doesn't work! php tries to match an instance method called "func" that is not defined in the original class' signature
$temp("Hey there");
call_user_func($ar->a("You still there?"));
?>

PHP Basics: Can't deal with scope within classes

i got some trouble to understand scope in OOP. What i want is that $foo->test_item() prints "teststring"...Now it just fails with:
Warning: Missing argument 1 for testing::test_item()
Thanks a lot!
<?php
class testing {
public $vari = "teststring";
function test_item($vari){ //$this->vari doesn't work either
print $vari;
}
}
$foo = new testing();
$foo->test_item();
?>
test_item() should be:
function test_item() {
print $this->vari;
}
There is no need to pass $vari as a parameter.
Well, you've declared a method which expects an argument, which is missing. You should do:
$foo->test_item("Something");
As for the $this->, that goes inside of the class methods.
function test_item(){
print $this->vari;
}
function parameters can not be as "$this->var",
change your class like
class testing {
public $vari = "teststring";
function test_item(){ //$this->vari doesn't work either
print $this->vari;
}
}
$foo = new testing();
$foo->test_item();
And read this Object-Oriented PHP for Beginners
What's happening there is that $foo->test_item() is expecting something passed as an argument, so for example
$foo->test_item("Hello");
Would be correct in this case. This would print Hello
But, you may be wondering why it doesn't print teststring. This is because by calling
print $vari;
you are only printing the variable that has been passed to $foo->test_item()
However, if instead you do
function test_item(){ //notice I've removed the argument passed to test_item here...
print $this->vari;
}
You will instead be printing the value of the class property $vari. Use $this->... to call functions or variables within the scope of the class. If you try it without $this-> then PHP will look for that variable within the function's local scope

Return a method with variable list of arguments as reference in PHP

I'm trying to return a value from a method as a reference in PHP5.3. I may be going at this the completely wrong way, but I am bringing an older project up to speed with some of the newer 5.3+ features.
Below is an example I whipped up to explain what is happening:
class Foo
{
static $foobar = 5;
function &bar()
{
return self::$foobar;
}
}
// Doesn't work
//$test1 = &call_user_func_array(array("Foo","bar"),array());
// Doesn't work
//$test1 = &call_user_func_array("Foo::bar",array());
// Doesn't work
//$f = new Foo; $test1 = &call_user_func_array(array($f,"bar"),array());
// WORKS
//$test1 = &Foo::bar();
//Doesn't work
//$function = "Foo::bar";
//$test1 = &$function();
// WORKS
$f = new Foo; $test1 = &$f->bar();
$test2 = Foo::bar();
var_dump($test1);
var_dump($test2);
$test1 = 10;
echo "----------<br />";
var_dump($test1);
var_dump($test2);
var_dump(Foo::bar()); //returns 10 when working, 5 when not working
The very last Foo::bar() should return a 10, since $test1 should be a reference to Foo::$foobar when everything works.
I realize that this example also uses some funky legacy PHP calling Foo::bar and the method bar() not being specified as static, but still being able to be invoked via ::
Any help would be greatly appreciated as the only fix I have so far is to just setup a switch on the argument list, and call the method directly based upon how many arguments exist.
This is just assigning $test1 to the value of $foobar (which is 5)
$test1 = &$f->bar();
This is just overwriting the value contained in $test1 with 10
$test1 = 10;
If you want to update the value within Foo, use
$f->foobar = 10;
Doesn't it already work in PHP 5.2.5: http://codepad.org/uMEIK210 (note the 10 as final result)?
I suppose, you would like to see the 10 three times.
For that (that $test2 is also a reference to the class field) you need to specify the & on both sides:
function &bar() and $test2 =& Foo::bar();
See the docs:
Note: Unlike parameter passing, here you have to use & in both places - to indicate that you want to return by reference, not a copy, and to indicate that reference binding, rather than usual assignment, should be done for $myValue.
So you just need to edit one line to get the (probably) desired 3 x 10:
$test2 =& Foo::bar();
Final hint
Do not use PHP references
First of all, try declaring the function static. Also the call should be a normal call. prefixed by ampersand as already answered.
class Foo
{
static $foobar = 5;
public static function &bar()
{
return self::$foobar;
}
}
The call:
$test1 =& Foo::bar();
Also, I can't see a valid reason for referencing a static variable. A static variable is a variable that doesn't change value between calls. It basically is a "global" var enclosed in a namespace. You only need read access from outside the class, the write should be done internally, as per the encapsulation principle. No need for the reference, really..

Categories