How do I dynamically invoke a class method in PHP? [duplicate] - php

This question already has answers here:
How to use class methods as callbacks
(5 answers)
Closed last year.
How can I dynamically invoke a class method in PHP? The class method is not static. It appears that
call_user_func(...)
only works with static functions?
Thanks.

It works both ways - you need to use the right syntax
// Non static call
call_user_func( array( $obj, 'method' ) );
// Static calls
call_user_func( array( 'ClassName', 'method' ) );
call_user_func( 'ClassName::method' ); // (As of PHP 5.2.3)

Option 1
// invoke an instance method
$instance = new Instance();
$instanceMethod = 'bar';
$instance->$instanceMethod();
// invoke a static method
$class = 'NameOfTheClass';
$staticMethod = 'blah';
$class::$staticMethod();
Option 2
// invoke an instance method
$instance = new Instance();
call_user_func( array( $instance, 'method' ) );
// invoke a static method
$class = 'NameOfTheClass';
call_user_func( array( $class, 'nameOfStaticMethod' ) );
call_user_func( 'NameOfTheClass::nameOfStaticMethod' ); // (As of PHP 5.2.3)
Option 1 is faster than Option 2 so try to use them unless you don't know how many arguments your going to be passing to the method.
Edit: Previous editor did great job of cleaning up my answer but removed mention of call_user_func_array which is different then call_user_func.
PHP has
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
http://php.net/manual/en/function.call-user-func.php
AND
mixed call_user_func_array ( callable $callback , array $param_arr )
http://php.net/manual/en/function.call-user-func-array.php
Using call_user_func_array is orders of magnitude slower then using either option listed above.

You mean like this?
<?php
class A {
function test() {
print 'test';
}
}
$function = 'test';
// method 1
A::$function();
// method 2
$a = new A;
$a->$function();
?>

As of PHP7, you use an array-like way:
// Static call only
[TestClass::class, $methodName](...$args);
// Dynamic call, static or non-static doesn't matter
$instance = new TestClass();
[$instance, $methodName](...$args);
Just replace the class name with TestClass, the method name with $methodName and the method arguments with ...$args. Note that, in the later case, it doesn't matter that the method is static or non-static.
One advantage is you can pass the array as a callable to a function.

call_user_func(array($object, 'methodName'));
For more details, see the php callback documentation.

EDIT: I just worked out what you were trying to ask... ah well.. will leave my comments in anyway. You can substitute names of classes and methods with variables if you like..(but you are crazy) - nick
To call a function from within a class you can do it one of two ways...
Either you can create an instance of the class, and then call it.
e.g.:
$bla = new Blahh_class();
$bla->do_something();
or... you can call the function statically.. i.e. with no instance of the class.
e.g.:
Blahh_class::do_something()
of course you do need to declare that your function is static:
class Blahh_class {
public static function do_something(){
echo 'I am doing something';
}
}
If a class is not defined as static, then you must create an instance of the object.. (so the object needs a constructor)
e.g.:
class Blahh_class {
$some_value;
public function __construct($data) {
$this->$some_value = $data;
}
public function do_something() {
echo $this->some_value;
}
}
The important thing to remember is that static class functions can not use $this as there is no instance of the class. (this is one of the reasons why they go much faster.)

This may be useful as a substitute
class ReferenceContainer {
function __construct(CallbackContainer $callbackContainer) {
//Alternatively you can have no parameters in this constructor and create a new instance of CallbackContainer and invoke the callback in the same manner
//var_dump($this->callbackContainer);
$data = 'This is how you parse a class by reference';
$callbackContainer->myCallback($data);
}
}
class CallbackContainer {
function __construct() {}
function myCallback($data) {
echo $data."\n";
}
}
$callbackContainer = new CallbackContainer();
$doItContainer = new ReferenceContainer($callbackContainer);

Related

call static method on value which is returned from static method [duplicate]

How would I get something like this to work?
$class_name = 'ClassPeer';
$class_name::doSomething();
Depending on version of PHP:
call_user_func(array($class_name, 'doSomething'));
call_user_func($class_name .'::doSomething'); // >5.2.3
To unleash the power of IDE autocomplete and error detection, use this:
$class_name = 'ClassPeer';
$r = new \ReflectionClass($class_name );
// #param ClassPeer $instance
$instance = $r->newInstanceWithoutConstructor();
//$class_name->doSomething();
$instance->doSomething();
Basically here we are calling the static method on an instance of the class.
Use call_user_func. Also read up on PHP callbacks.
call_user_func(array($class_name, 'doSomething'), $arguments);
These answers are all outdated:
<?php
class MyTest{
public static function bippo(){
echo "hello";
}
}
$a = MyTest::class;
$a::bippo();
works fine
After I have almost missed the simplest solution from VolkerK, I have decided to extend and put it in a post. This is how to call the static members on the instance class
// calling class static method
$className = get_class($this);
$result = $className::caluclate($arg1, $arg2);
// using class static member
foreach ($className::$fields as $field) {
:
}
Reflection (PHP 5 supports it) is how you'd do this. Read that page and you should be able to figure out how to invoke the function like that.
$func = new ReflectionFunction('somefunction');
$func->invoke();
Documentation Link
if you need to adjust the namespace
$call = call_user_func(array('\\App\\Models\\'.$class_name, "doSomething"));

Storing a reference to a class function in a variable in PHP

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.

how to get input parameters of any method of a class in PHP?

Let's say I have a class with 10 methods, each method has different parameters.
I want to log input parameters of all methods of said class without having to do edit each method to insert that logging code. Is there away to do that ?
Just wrap it with decorator with magic __call http://ideone.com/n9ZUD
class TargetClass
{
public function A($a, $b) {}
public function B($c, $d) {}
public function C($e, $f) {}
}
class LoggingDecorator
{
private $_target;
public function __construct($target)
{
$this->_target = $target;
}
public function __call($name, $params)
{
$this->_log($name, $params);
return call_user_func_array(array($this->_target, $name), $params);
}
private function _log($name, $params)
{
echo $name . ' has been called with params: ' . implode(', ', $params) . '<br>';
}
}
$target = new TargetClass();
$logger = new LoggingDecorator($target);
$logger->A(1, 2);
$logger->A(3, 4);
The only disadvantage of this approach is that you will lose the type of the decorated class, e.g. you won't be able to satisfy type hints with it. If that is a concern, distill the interface of TargetClass and implement it in the LoggingDecorator.
Not directly, no.
You could rename all your methods to have an underscore suffix, e.g.:
myFunction() -> _myFunction()
Then, write add the magic __call() method to intercept calls to the previous (unprefixed) methods. Then you would log the request and pass on all arguments to the original method.
It's kind of ugly and still requires a change to all your method names.
I might be over simplifying this - but you can retrieve all the arguments of a function using
func_get_args();
Returns an array comprising a function's argument list
http://www.php.net/manual/en/function.func-get-args.php
<?php
function foo() {
$args = func_get_args();
var_export($args);
}
foo('arg1', 'arg1');
?>
That would output something like this -
array (
0 => 'arg1',
1 => 'arg2',
)
There are a few notes to be added here - you should read the documentation link I provided - one "limitation" is -
Note:
This function returns a copy of the passed arguments only, and does not account for default (non-passed) arguments.

Instantiating classes with params in singleton factory

I have a class that produces singleton classes. (Note that this is overly simplified code for the purpose of this question, for example it doesn't check that the filepath exists)
class Singleton
{
public function Load($classname, $params)
{
$filepath = 'classes/'.$classname.'.php';
require_once($filepath);
return $classname();
}
}
Now say that I wanted to pass an array of parameters that can vary in size to the constructor of the class being created. What is the best way to do this? I envision something along the lines of call_user_func_array but for classes?
You can achieve some interesting results with the use of PHP's Reflection library.
function Load( $class, $args )
{
$reflection = new ReflectionClass( $class );
$object = $reflection->newInstanceArgs( $args );
return $object;
}
This is simplified and implies use of the __autoload function, nor does it check for namespaces if you use them, plus it'll create a new instance of the class every time you call it, so you'll need to implement an array of objects to keep track of which ones you've created already, etc...
And for basic documentation: $class is a string with the name of the class you're wishing to instantiate, and $args is an array of arguments that you'll pass to the __construct( ) method.
not tested, but why not just load them into the constructor? ie return $classname($params); with your constructor set up like __construct($params = false). You can then check if params is passed or not, making your constructor parameters optional...
This does mean all your classes need to have the same constructor though.
class Foo {
public function __construct($params = false) {
if($params === false)
echo 'not passed';
else
print_r($params);
}
}
$class = 'Foo';
$foo = new $class(array('one', 'two'));
$foo2 = new $class();
outputs:
Array ( [0] => one [1] => two )
not passed

PHP include class functions with variables

I am trying to use a variable to get a function in a extended class, this is what I want to do but I can't get it to work, Thanks for your help.
class topclass {
function mode() {
$mode = 'function()';
$class = new extendclass;
$class->$mode;
}
}
Don't include the brackets "()" in the $mode variable.
class topclass {
function mode() {
$mode = 'functionx';
$class = new extendclass;
$class->$mode();
}
}
You can also use a callback, which is an array tuple of the instance and a string naming the function. If the intended call is $foo->bar(), then the callback would be:
$callback = array($foo, 'bar');
Regular functions (not a method) and static methods are stored as plain strings:
// Function bar
$callback = 'bar';
// Static method 'bar' in class Foo
$callback = 'Foo::bar';
It is called with call_user_func or call_user_func_array, the second allowing parameters to be passed to the callback function:
// No parameters
call_user_func($callback);
// Parameters 'baz' and 'bat'
call_user_func_array($callback, array('baz', 'bat');
It may seem like this is unnecessary complication, but in many instances, you may want to be able to programmatically build a function call, or you may not know ahead of time how many parameters you will be passing to a function (some functions, such as array_merge and sprintf allow a variable number of parameters).

Categories