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);
}
Related
I have a custom PHP class with few methods in it. Is is possible to call class method this way:
<?php
class someClass{
function someMethod_somename_1(){
echo 'somename1';
}
function someMethod_somename_2(){
echo 'somename2';
}
}
$obj = new someClass();
$methodName = $_GET['method_name'];
$obj->someMethod_{$methodName}(); //calling method
?>
My real world application is more complex, but here I provide just this simple example to get the main idea. Maybe I can use eval function here?
Please don't use eval() because it's evil in most situations.
Simple string concatenation helps you:
$obj->{'someMethod_'.$methodName}();
You should also verify the user input!
$allowedMethodNames = array('someone_2', 'someone_1');
if (!in_array($methodName, $allowedMethodNames)) {
// ERROR!
}
// Unrestricted access but don't call a non-existing method!
$reflClass = new ReflectionClass($obj);
if (!in_array('someMethod_'.$methodName, $reflClass->getMethods())) {
// ERROR!
}
// You can also do this
$reflClass = new ReflectionClass($obj);
try {
$reflClass->getMethod('someMethod_'.$methodName);
}
catch (ReflectionException $e) {
// ERROR!
}
// You can also do this as others have mentioned
call_user_func(array($obj, 'someMethod_'.$methodName));
Of course, take this:
$obj = new someClass();
$_GET['method_name'] = "somename_2";
$methodName = "someMethod_" . $_GET['method_name'];
//syntax 1
$obj->$methodName();
//alternatively, syntax 2
call_user_func(array($obj, $methodName));
Concatenate the whole method name before you call it.
Update:
Directly calling methods based on user input is never a good idea. Consider doing some previous validation of the method name before.
You may also take advantage of php magic methods, namely __call() in combination with call_user_func_array() and method_exists():
class someClass{
public function __call($method, $args) {
$fullMethod = 'someMethod_' . $method;
$callback = array( $this, $fullMethod);
if( method_exists( $this, $fullMethod)){
return call_user_func_array( $callback, $args);
}
throw new Exception('Wrong method');
}
// ...
}
For safety purposes you may want to create a wrapper which would prohibit calling other methods, like this:
class CallWrapper {
protected $_object = null;
public function __construct($object){
$this->_object = $object;
}
public function __call($method, $args) {
$fullMethod = 'someMethod_' . $method;
$callback = array( $this->_object, $fullMethod);
if( method_exists( $this->_object, $fullMethod)){
return call_user_func_array( $callback, $args);
}
throw new Exception('Wrong method');
}
}
And use it as:
$call = new CallWrapper( $obj);
$call->{$_GET['method_name']}(...);
Or maybe create execute method and than add to someClass method GetCallWrapper().
This way you'll get functionality well encapsulated into objects (classes) and won't have to copy it every time (this may come in handy if you'll need to apply some restrictions, i.e. privileges checking).
It is possible to use variable as function.
For example if you have function foo() you can have some variable $func and call it. Here is example:
function foo() {
echo "foo";
}
$func = 'foo';
$func();
So it should work like $obj->$func();
I'm using PHPs create_function($args, $code) function to dynamically load a function definition from a database.
The way I'm attempting to implement it is as follows:
I have a class MyClass which has an instance variable myFunction. The constructor populates that instance variable with the result of a call to create_function. I'm hoping to dynamically create a function for the specific object (once instantiated) of this class, that can be called as $object->myFunction(arg1, arg2);
So my class looks like:
class MyClass {
public $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
}
I'm then trying to call this dynamic function from elsewhere in my program on the instantiated "MyClass" object by doing something like...
$object = new MyClass();
$object->myFunction(args..);
However I keep getting errors such as:
MyClass and its behaviors do not have a method or closure named myFunction.
When I run var_dump($object->myFunction) I get back "lambda_xx", which is a good sign meaning create_function is at least working.
Interesting Update on Works vs. Doesn't Work cases
It turns out that in my "other file" where I am doing the following:
$pm = Yii::app()->user->postMatching; //This is a PostMatching object made elsewhere
$c = $pm->findRelated;
foreach ($posts as $post) {
var_dump($c);
$postIds = $c($post, $limit);
//post to related mapping
$specificRelatedPostIds[$post->postId] = $postIds;
}
exit; // exiting for testing
This doesn't work, but if instead of pulling the object $pm from Yii::app()->user->postMatching I just create a new one:
$pm = new PostMatching();
$c = $pm->findRelated; //the anon function instance variable
$c(); // THIS WORKS NOW!
So naturally I var_dumped $pm and $c in both the "newly created" case and the case where I get it from Yii::app()->user->postMatching, and they are identical. The only thing that is different is the name of the anonymous function (as expected).
Does anyone have any idea why this might be the case? In both cases $pm IS an instantiated PostMatching object with that instance variable, I'm just unable to use the syntax to invoke it!
Just updated the above with newly discovered "Twists", thanks guys!
Maybe something along these lines can be useful:
class MyClass {
private $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
public function myFunction() {
$args = func_get_args();
return call_user_func_array($this->myFunction, $args);
}
}
That's due to parsing-related troubles that PHP has. This version should work:
$object = new MyClass();
$method = $object->myFunction;
$method(args..);
See it in action.
You can call the method like this:
call_user_func($object->myFunction, args..);
php 5.3
Is there a way to do this (viable in java for example)
(new MyClass())->myMethod();
i am receving: Parse error: syntax error, unexpected T_OBJECT_OPERATOR in D.. on line 7
Add
I really need that RFC to be implemented in the next PHP version!
http://wiki.php.net/rfc/instance-method-call
Is there a way we can subscribe to it so it can get more attention?
No, its not possible. There is a RFC for that
http://wiki.php.net/rfc/instance-method-call
But no one knows, when this will come to the userland.
Jacob mentioned the static method. There are other more or less useful methods to achieve the same
function instanciate($className, $arg1 = null) {
$args = func_get_args();
array_shift($args);
$c = new ReflectionClass($className);
return $c->newInstanceArgs($c);
}
instanciate('Classname', 1, 2, 3)->doSomething();
However, I prefer the temporary variable (like in the question).
Update:
I can swear there where an example for the temporary variable stuff in the question in the past. However, I meant this
$x = new Class;
$x->method();
where $x is the temporary variable.
That is not valid syntax. A handy way to achieve what you want is to use a static method to create the object.
In your MyClass:
public static function create() {
return new MyClass();
}
Then you can use:
MyClass::create()->myMethod();
However it is extra code that you have to maintain, if for example the constructor is changed or the class is extended. So you need to weigh up the benefits.
You can do something like this:
function chain_statements($statement1, $statement2) { return $statement2; }
class TClass { public Method() { ...; return $this; } }
$b = chain_statements($a = new TClass(), $a->Method());
... or more generalized:
function chain_statements(array $statements) { return end($statements); }
For example:
function chain_statements($statement1, $statement2) { return $statement2; }
function chain_statements2(array $statements) { return end($statements); }
class TClass
{
public $a = 0;
public function Method1() { $this->a = $this->a + 1; return $this; }
public function Method2() { $this->a = $this->a + 2; return $this; }
}
$b = chain_statements($c = new TClass(), $c->Method1()); echo($b->a);
$b = chain_statements2(array($c = new TClass(), $c->Method1(), $c->Method2())); echo($b->a);
... or even better:
function call_method($object) { return $object; }
$b = call_method(new TClass())->Method2(); echo($b->a);
Not as such. In PHP new is not an expression, but a language construct. The common workaround is to provide a static instantiation method for MyClass::get()->... use.
A more concise alternative is a hybrid factory function:
function MyClass() { return new MyClass; }
class MyClass {
...
}
Which then simplifies the instantiation to MyClass()->doSomething();
You can put it in one statement if you really wanted to. Use eval() ;p
But you probably shouldn't.
I had this same problem a while ago but I found this simple solution which is pretty readable too. I like the fact it uses only the standard PHP functions. There's no need to create any utility functions of your own.
call_user_func(
array(new ClassToInstance(), 'MethodName'),
'Method arguments', 'go here'
);
You can also use call_user_func_array to pass the arguments as an array.
call_user_func_array(
array(new ClassToInstance(), 'MethodName'),
array('Method arguments', 'go here')
);
Take the following code as an example:
class xpto
{
public function __get($key)
{
return $key;
}
}
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new xpto();
}
return $instance;
}
echo xpto()->haha; // returns "haha"
Now, I'm trying to archive the same result but without have to write the xpto class. My guess is I should have to write something like this:
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new stdClass();
}
return $instance;
}
echo xpto()->haha; // doesn't work - obviously
Now, is it possible to add __get() magic functionality to the stdClass object? I guess not, but I'm not sure.
No, it is not possible. You cannot add anything to stdClass. Also, unlike Java, where every object is a direct or indirect subclass of Object, this is not the case in PHP.
class A {};
$a = new A();
var_dump($a instanceof stdClass); // will return false
What are you really trying to achieve? Your question sounds a bit like "I want to close the door of my car, but without having a car" :-).
The OP looks like they are trying to achieve a singleton pattern using a function in the global scope which is probably not the correct way to go, but anyway, regarding Cassy's answer, "You cannot add anything to stdClass" - this is not true.
You can add properties to the stdClass simply by assigning a value to them:
$obj = new stdClass();
$obj->myProp = 'Hello Property'; // Adds the public property 'myProp'
echo $obj->myProp;
However, I think you need PHP 5.3+ in order to add methods (anonymous functions / closures), in which case you might be able to do something like the following. However, I've not tried this. But if this does work, can you do the same with the magic __get() method?
UPDATE: As noted in the comments, you cannot dynamically add methods in this way. Assigning an anonymous function (PHP 5.3+) does just that and simply assigns a function (strictly a closure object) to a public property.
$obj = new stdClass();
$obj->myMethod = function($name) {echo 'Hello '.$name;};
// Fatal error: Call to undefined method stdClass::myMethod()
//$obj->myMethod('World');
$m = $obj->myMethod;
$m('World'); // Output: Hello World
call_user_func($obj->myMethod,'Foo'); // Output: Hello Foo
I have this fetch function:
public static function fetch($class, $key)
{
try
{
$obj = new $class($key);
}
catch(Exception $e)
{
return false;
}
return $obj;
}
It creates a new instance by calling that class's constructor and passing in the key. Now, how would I make it so I can pass in an array of arguments in $key, and have it like:
$obj = new $class($key[0], $key[1]...);
So that it works for one or more keys?
Hopefully that was clear enough.
Using PHP 5
This is an interesting question. If it wasn't a constructor function you were trying to give dynamic arguments to, then normally you could use call_user_func_array(). However, since the new operator is involved, there doesn't seem to be an elegant way to do this.
Reflection seems to be the consensus from what I could find. The following snippet is taken from the user comments on call_user_func_array(), and illustrates the usage quite nicely:
<?php
// arguments you wish to pass to constructor of new object
$args = array('a', 'b');
// class name of new object
$className = 'myCommand';
// make a reflection object
$reflectionObj = new ReflectionClass($className);
// use Reflection to create a new instance, using the $args
$command = $reflectionObj->newInstanceArgs($args);
// this is the same as: new myCommand('a', 'b');
?>
To shorten it up for your case, you can use:
$reflectionObject = new ReflectionClass($class);
$obj = $reflectionObject->newInstanceArgs($key);
Use reflection:
$classReflection = new ReflectionClass($class);
$obj = $classReflection->newInstanceArgs($key);
My library solves this this:
// Returns a new instance of a `$classNameOrObj`.
function fuNew($classNameOrObj, $constructionParams = array()) {
$class = new ReflectionClass($classNameOrObj);
if (empty($constructionParams)) { return $class->newInstance(); }
return $class->newInstanceArgs($constructionParams); }
The empty() test is required because newInstanceArgs() will complain if you give it an empty array, stupidly.
What does the constructor of the class look like? Does it accept an arbitrary number of arguments? It might be better to accept an array of keys instead of a list of key arguments.
call_user_func_array could probably do what you want:
$obj = new $object_class();
call_user_func_array(array($obj, '__construct'), $args);
Note that this calls the constructor twice, which could have negative side effects.