PHP calling a class method with class name as variable string - php

I have a class from which I want to call a function, but the instantiated class variable is taken from a string.
class Myclass
{
public function myFunction($param1,$param2) {
return 'blah';
}
}
I am trying the following but it doesn't work. Errors from log file below.
$myclass='myclass';
${$myclass}=new ucfirst($myclass);
$args=array($stringvar,1);
$retval=call_user_func_array(array(${$myclass}, 'myFunction'), $args);
What am I doing wrong ?
Errors :
PHP Warning: call_user_func_array() expects parameter 1 to be a valid callback, first array member is not a valid class name or object in file.php on line 100
PHP Notice: Undefined variable: myclass in file.php on line 134
PHP Fatal error: Call to a member function myFunction() on a non-object in file.php on line 134

You're overcomplicating it a bit with the extra ${} bracketing on the class reference.
$myclass='myCLaSs';
$myObject = new $myclass();
$retval = $myObject->myFunction($stringvar, 1);
Or if you need to use call_user_func_array:
$args=array($stringvar,1);
$retval=call_user_func_array(array($myObject, 'myFunction'), $args);
When you use the ${} bracketing, you are referencing variables by a variable name. For example:
$myVariable = "A";
$aVariableThatIsHoldingAVariableName = "myVariable";
echo ${$aVariableThatIsHoldingAVariableName}; // outputs "A".
Applying this to your code shows the following logic happening:
Set the variable $myclass equal to the string 'myclass'
$myclass='myclass';
Set the variable ${$myclass}:
This gets the value of the variable $myclass ('myclass') and uses that as the variable name. In other words, the following variable name resolution happens: ${$myclass} => ${'myclass'} => $myclass.
So this line sets $myclass to a new Myclass(); object:
${$myclass}=new ucfirst($myclass);
The first parameter to call_user_func_array is a callable (See http://us2.php.net/manual/en/language.types.callable.php). The callback it looks like you are trying to reference here is Type 3: array($object, $method). But the same variable resolution happens. Now, ${$myclass} is going to resolve differently, because the value of $myclass is a Myclass Object. Variable names have to be strings, not objects (obviously), so ${Myclass Object} is totally invalid.
$args=array($stringvar,1);
$retval=call_user_func_array(array(${$myclass}, 'myFunction'), $args);
But since $myclass at that point is an object already, you can just do the following (as mentioned initially above):
$retval=call_user_func_array(array($myclass, 'myFunction'), $args);

Related

Array to string conversion - Variable function name calling

I need to call some functions inside a class; based on variables, something like this:
$x->$y();
But, I found a strange behavior, consider the following sample code:
$arr = array(
"some_index" => "func_name"
);
$str = "func_name";
class some_class {
public function func_name() {
echo "It works in class!";
}
}
$some_obj = new some_class();
$some_obj->$arr['some_index']();
$some_obj->$str();
Now the line
$some_obj->$arr['some_index']();
gives the errors:
Array to string conversion in ...
Undefined property: some_class::$Array in ...
Uncaught Error: Function name must be a string in...
But, the line
$some_obj->$str();
works perfectly.
Also, both the lines will work, if the function is not defined inside a class.
Anyone knows why this is happening ?
You should call it this way:
$some_obj->{$arr['some_index']}();
here's a living example

Second class changes first class without returning or using a reference PHP OOP

i have 2 classes. I am confused about how the second class is editing the first's properties without being passed the first class as a reference.
first.php
namespace first;
class first {
public $prop = 'first value';
function __construct(){
$second = '\\second\\second';
require 'second.php';
call_user_func_array(array(new $second, 'method'), array($this));
}
}
$first = new first();
echo $first->prop;
second.php
namespace second;
class second {
function method($first){
$first->prop = 'second value';
}
}
i get this output:
second value
can someone please explain how the second class is doing this? it seems to defy everything i've learned.
In PHP by default, objects are passed by reference. In your case you are passing the class object "first" to the second class object "second". So PHP does it for you, and you don't have to specify to pass it by Reference e.g. (&$variable) since $this is an object and not a variable. If you are passing a $variable then you have to specify if you want to pass it by reference ($second->method(&$variable))
call_user_func_array(array(new $second, 'method'), array($this));
In that call you passed ($this) as the argument for method "method" in class second and modify the property "prop".
This function is doing the trick.
call_user_func_array()
This is call back function :- call_user_func_array — Call a callback with an array of parameters
function __construct(){
$second = '\\second\\second';
require 'second.php';
//This is the call back function which
call_user_func_array(array(new $second, 'method'), array($this));
}
call_user_func_array(array(new $second, 'method'), array($this));
This function hast two parameters, array(new $second, 'method'), array($this)
in the first parameter you have an array of two elements, namely new $second, and 'method' which instantiates the class new second and the function method. The second parameter is
array($this) which says this class, meaning or referring to `class first {}`
In nutshell , the reason whey the first class can be modified is because it has got a call back function with parameters of the second class.
you can check out how is function works http://www.php.net/manual/en/function.call-user-func-array.php
As Elin suggested, please read http://www.php.net/manual/en/language.oop5.references.php. It answers everything.
As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.

Is is possible to store a reference to an object method?

Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise

Undefined property of object when using define()

Why can I not do something like the following when I know that the property of myobject is going to be declared already:
define('title','boats');
myobject->title;
but this works:
myobject->boats
Is this even good practice?
You can't use
$myobject->title
as this is attempting to access the title property of your object. If this property does not exist, an error will be triggered.
You can use
$myobject->{title}
but I'd rather see you use a variable instead of a constant, eg
$title = 'boats';
echo $myobject->$title;
Ideally, you will know which property you want to access and use its name appropriately
$myobject->boats
Why can I not do something like the following when I know that the property of myobject is going to be declared already:
Probably because PHP expects you to call it with a method name. There are options, though:
<?php
define( 'METHOD', 'bar' );
class Foo {
public function bar( ) {
echo "Foo->bar( ) called.\n";
}
}
$foo = new Foo;
call_user_func( array( $foo, METHOD ) );
// or
$method = METHOD;
$foo->$method( );
EDIT: Ah, I seem to have misunderstood. My version is for calling methods of which the name is defined in a constant, whereas you were looking for a way of calling properties. Well, I'll leave it in here for future reference anyway.

How to properly call a method when setting a property in PHP (if even possible)

I'm wondering why this doesn't work? I'm setting a property (array) and one value it has to get from a method inside the class, I guess I'm doing something I really shouldn't but I would be very grateful for both an explanation why it doesn't work and how it would work ;)
I'm new to
Class Widget{
public $settings = array('setting1',array(
'subsetting1'=> 1,
'subsetting2' =>$this->WidgetFunction()
));
function WidgetFunction() {
echo 'works';
}
}
Getting following error:
Parse error: syntax error, unexpected T_VARIABLE on line 7
(where WidgetFunction is called)
PHP manual:
Class member variables are called "properties". [...] They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
You should assign this value in your constructor.
Class Widget {
public $settings;
function __construct() {
$this->settings = array(
'setting1' => array(
'subsetting1' => 1,
'subsetting2' => $this->WidgetFunction())
);
}
function WidgetFunction() {
echo 'works';
}
}
(btw, I think you might have wanted to use 'setting1' as an index for the array in the array - as I did it in my code example?)

Categories