Instantiating classes with params in singleton factory - php

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

Related

Creating new object instance with arguments array

I am trying to create a new instance of a class like this:
$obj = new $class;
I am doing this in a way that a common set of functions will do this for a number of classes, but now I am implementing some arguments. Now although the handler function could look like this:
function newInst($argA = null, $argB = null, $argC = null)
This would have to have all the arguments included beforehand and would have an upper limit. So, I am trying to do something like this:
function newInst() {
$obj = new $class(func_get_args());
...
}
but instead of just the first argument being applied, I would like it to apply the array as a set of arguments. I have tried
function newInst() {
$obj = new $class;
call_user_func_array(array($obj, '__construct'), func_get_args());
...
}
but that calls the __construct function twice. So, is there any way to use the arguments of a called function to create a new instance that would go through the __construct or classname function during instantiation?
If you're not opposed to using reflection: ReflectionClass::newInstanceArgs
function createInstance($class, array $arguments) {
$reflection = new ReflectionClass($class);
return $reflection->newInstanceArgs($arguments);
}
Reflection offers alot, and despite the common claim that it's "slow", it's very inoften that reflection will be a true bottleneck in your application; any possibility can be mitigated with caching anyhow.
Based on discussion, I'm just amending a hypothetical solution with count() checks and naive caching; it would still (definitely) need profiling.
function createInstance($class, array $arguments) {
static $cache = [];
switch (count($arguments)) {
case 0: return new $class();
case 1: return new $class($arguments[0]);
case 2: return new $class($arguments[0], $arguments[1]);
case 3: return new $class($arguments[0], $arguments[1], $arguments[2]);
}
if (!isset($cache[$class])) {
$cache[$class] = new ReflectionClass($class);
}
return $cache[$class]->newInstanceArgs($arguments);
}

How to reuse an object if it existed

I'm trying to determine whether or not a given object has been created. I see there are methods for class_exists and method_exists but what I'm trying to figure out is if new Foo() has been called (and hopefully figure out what variable it was assigned to, but that is not as important).
If I understand you correctly you are trying to initialize object only once. If this is the case why not to use singleton pattern? This will free you from checking of existence of object:
class MyClass {
private static $instance;
private function __construct() {}
public static function getInstance() {
if (empty(self::$instance)) {
self::$instance = new __CLASS__();
}
return self::$instance;
}
}
You can use this code like this:
$obj = MyClass::getInstance();
With similar approach you can define additional helper static methods which will check whether object was instantiated or not. You just need to keep instance statically inside your class.
Edit: After seeing the reason you are needing this in the comments above, this is definitely not the way to go about it.
Here ya go. It could be optimized a little, but should work fine.
Also, passing get_defined_vars() to the function every time is necessary because that function only retrieves the vars within the scope it's called. Calling it inside the function would only give the vars within the scope of that function.
<?php
function isClassDeclared($class_name, $vars, $return_var_name = FALSE) {
foreach ($vars AS $name => $val) {
if (is_object($val) && $val instanceof $class_name)
return $return_var_name ? $name : TRUE;
}
return FALSE;
}
class Foo {}
$foo = new Foo;
echo '<pre>';
var_dump(isClassDeclared('foo', get_defined_vars(), TRUE));
var_dump(isClassDeclared('bar', get_defined_vars(), TRUE));
echo '</pre>';

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

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()
?>

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

How do you copy a PHP object into a different object type

New class is a subclass of the original object
It needs to be php4 compatible
You could have your classes instantiated empty and then loaded by any number of methods. One of these methods could accept an instance of the parent class as an argument, and then copy its data from there
class childClass extends parentClass
{
function childClass()
{
//do nothing
}
function loadFromParentObj( $parentObj )
{
$this->a = $parentObj->a;
$this->b = $parentObj->b;
$this->c = $parentObj->c;
}
};
$myParent = new parentClass();
$myChild = new childClass();
$myChild->loadFromParentObj( $myParent );
You can do it with some black magic, although I would seriously question why you have this requirement in the first place. It suggests that there is something severely wrong with your design.
Nonetheless:
function change_class($object, $new_class) {
preg_match('~^O:[0-9]+:"[^"]+":(.+)$~', serialize($object), $matches);
return unserialize(sprintf('O:%s:"%s":%s', strlen($new_class), $new_class, $matches[1]));
}
This is subject to the same limitations as serialize in general, which means that references to other objects or resources are lost.
A php object isn't a whole lot different to an array, and since all PHP 4 object variables are public, you can do some messy stuff like this:
function clone($object, $class)
{
$new = new $class();
foreach ($object as $key => $value)
{
$new->$key = $value;
}
return $new;
}
$mySubclassObject = clone($myObject, 'mySubclass');
Its not pretty, and its certianly not what I'd consider to be good practice, but it is reusable, and it is pretty neat.
The best method would be to create a clone method on the Subclass so that you could do:
$myvar = $subclass->clone($originalObject)
Alternatively it sounds like you could look into the decorator pattern php example
I would imagine you would have to invent some sort of a "copy constructor". Then you would just create a new subclass object whilst passing in the original object.

Categories