How can I instantiate a class using a 'function handling' style function? - php

I am trying to implement a Command Pattern style queue and I do not know how to pass arguments to the constructor of the object.
My 'Command' pattern stores the objects in a database, where I have a table queue_items storing my 'Command' objects, with the fields class, method, constructor_arguments (stored as an indexed array), method_arguments (stored as an indexed array), and object_type (which is enum{'instance','static}).
If object_type is 'instance', I instantiate the object using the 'new' keyword. If object_type is 'static', then I just make the call using forward_static_call_array().
If I have no constructor arguments, I can just use something like this:
$instance = new $class_name(); //NOTE: no arguments in the constructor
$result = call_user_func_array(array($instance, $method_name), $method_arguments);
But if I wish to pass the values from the constructor_arguments into the __construct(), I can not find a function to let me do this.
I wish to keep the indexed array and not to rely on specialised constructors, so that I do not have to rewrite my own and 3rd party classes I use to handle, for example, taking an associative array as the only argument in a constructor.
Does anyone know how to pass an indexed array directly into __construct in the fashion of call_user_func_array()? Or can it simply not be done?
Drew J. Sonne.

You can use the ReflectionClass for this special case:
$rc = new ReflectionClass($className);
$instance = $rc->newInstanceArgs($array_of_parameters);

A more elaborated example using ReflectionClass:
<?php
class MyClass
{
private $arg1;
private $arg2;
public function __construct($arg1, $arg2 = "Hello World")
{
$this->arg1 = $arg1;
$this->arg2 = $arg2;
}
public function print(){
echo $this->arg2 . "," .$this->arg2;
}
}
$class = new ReflectionClass('MyClass');
$args = array(3,"outro");
$instance = $class->newInstanceArgs($args);
$instance->print()
?>

Related

How to programmatically find public properties of a class from inside one of it's methods

I've got a class Foo with public and protected properties. Foo needs to have a non-static method, getPublicVars() that returns a list of all the public properties of Foo (this is just an example, I know from outside the Foo object calling get_object_vars() will accomplish this and there is no need for my getPublicVars() method).
Note: This must also return dynamically declared properties assigned at runtime to the class instance (object) that aren't defined in the class's definition.
Here's the example:
class Foo{
private $bar = '123';
protect $boo = '456';
public $beer = 'yum';
//will return an array or comma seperated list
public function getPublicVars(){
// thar' be magic here...
}
}
$foo = new Foo();
$foo->tricky = 'dynamically added var';
$result = $foo->getPublicVars();
var_dump($result); // array or comma list with 'tricky' and 'beer'
What is the most concise way to get the only the public properties of an object from inside a class's own methods where both public and protected are visible?
I've looked at:
What is the best way to look inside a PHP class instance (object) to see all of its available public properties and methods?
But this doesn't seem to address my question as it points to using get_object_vars() from outside the object.
As you already realized, PHP's build in get_object_vars is scope-sensitive. You want the public object properties only.
So from that function to the public variant is not a large step:
function get_object_public_vars($object) {
return get_object_vars($object);
}
Calling this get_object_public_vars will give you only the public properties then because it is place out of scope of the current object.
If you need more fine-grained control, you can also make use of the ReflectionObject:
(new ReflectionObject($this))->getProperties(ReflectionProperty::IS_PUBLIC);
Which has the benefit that you don't need to introduce another function in the global namespace.
Does not work with php version >=7
As such, I can't really recommend solution any longer.
Use reflection instead
To get the public properties from within the class
$publicProperties = call_user_func('get_object_vars', $this);
the "trick" is that get_object_vars is being called from the scope of call_user_func and not the scope of the object
no need for reflection, stand-alone functions, closures, etc
Another (PHP 7.* compatible) way is to cast $this to an array and filter out any binary keys. This is a little less verbose than using Reflection.
return array_filter(
(array) $this,
static fn(string $key): bool => strpos($key, "\0") !== 0, ARRAY_FILTER_USE_KEY,
);
Using the ReflectionClass, you can do this easily. For example, below, I make an associative array of all the public properties.
$rc = new \ReflectionClass($this);
//get all the public properties.
$props = $rc->getProperties(\ReflectionProperty::IS_PUBLIC);
$ret = [];
foreach($props as $p) {
//get the name and value of each of the public properties.
$ret[$p->getName()] = $p->getValue($this);
}
You can wrap it into a closure and bind it to nowhere, then you'll have an outer scope view to the local public properties.
public function getPublicVars(){
return \Closure::bind(fn($obj) => get_object_vars($obj),null,null)($this);
}
According to this article (written by Vance Lucas), you can create a new call scope inside your "Foo" class definition using an "anonymous" function, and then you can call get_object_vars() from within. This allow you to get only the public properties from inside your class, even if these have been created dynamically later from the outside.
So adapted to your example it would be:
<?php
class Foo {
private $bar = '123';
protected $boo = '456';
public $beer = 'yum';
// return an array of public properties
public function getPublicVars(){
$publicVars = create_function('$obj', 'return get_object_vars($obj);');
return $publicVars($this);
}
}
$foo = new Foo();
$foo->tricky = 'dynamically added var';
$result = $foo->getPublicVars();
print_r($result);
and the output will be:
Array
(
[beer] => yum
[tricky] => dynamically added var
)
There is a second example in the article mentioned above that shows another way to do the same using the so-called "closures" (from php 5.3) but for some reason it doesn't work for me with php v5.4 so the private and protected properties remains included in the resulting array.

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

How to "invoke" a class instance in PHP?

is there any possibility to "invoke" a class instance by a string representation?
In this case i would expect code to look like this:
class MyClass {
public $attribute;
}
$obj = getInstanceOf( "MyClass"); //$obj is now an instance of MyClass
$obj->attribute = "Hello World";
I think this must be possible, as PHP's SoapClient accepts a list of classMappings which is used to map a WSDL element to a PHP Class. But how is the SoapClient "invoking" the class instances?
$class = 'MyClass';
$instance = new $class;
However, if your class' constructor accepts a variable number of arguments, and you hold those arguments in an array (sort of call_user_func_array), you have to use reflection:
$class = new ReflectionClass('MyClass');
$args = array('foo', 'bar');
$instance = $class->newInstanceArgs($args);
There is also ReflectionClass::newInstance, but it does the same thing as the first option above.
Reference:
Object instantiation
ReflectionClass::newInstanceArgs()
ReflectionClass::newInstance()
The other answers will work in PHP <= 5.5, but this task gets a lot easier in PHP 5.6 where you don't even have to use reflection. Just do:
<?php
class MyClass
{
public function __construct($var1, $var2)
{}
}
$class = "MyClass";
$args = ['someValue', 'someOtherValue'];
// Here's the magic
$instance = new $class(...$args);
If the number of arguments needed by the constructor is known and constant, you can (as others have suggested) do this:
$className = 'MyClass';
$obj = new $className($arg1, $arg2, etc.);
$obj->attribute = "Hello World";
As an alternative you could use Reflection. This also means you can provide an array of constructor arguments if you don't know how many you will need.
<?php
$rf = new ReflectionClass('MyClass');
$obj = $rf->newInstanceArgs($arrayOfArguments);
$obj->attribute = "Hello World";

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

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);

Categories