This article has the following method:
/**
* Call protected/private method of a class.
*
* #param object &$object Instantiated object that we will run method on.
* #param string $methodName Method name to call
* #param array $parameters Array of parameters to pass into method.
*
* #return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = array())
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
My question is... is there a particular reason why $object has an ampersand before it in the function declaration? Normally that'd mean you were passing by reference but doens't PHP pass objects by reference by default?
My question is... is there a particular reason why $object has an
ampersand before it in the function declaration?
In this piece of code, no. $object is only used for its value inside this function. The only two places where it is used are to be passed to get_class() (which takes its argument by value), and to be passed to ReflectionMethod::invokeArgs() (which also takes its arguments by value).
Therefore, there was no point to take $object by reference.
As you can see in the Function arguments section of the PHP documentation:
PHP supports passing arguments by value (the default), passing by reference, and default argument values. Variable-length argument lists are also supported.
By default, arguments are passed by value.
As for objects, it may seem that they are passed by reference but that is not totally true. See Objects and references which states:
A PHP reference is an alias, which allows two different variables to write to the same value. 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.
So, to avoid confusion, I would always assume that the parameter is NOT passed by value, even for an object. If you want it to be passed by reference, add the & to make sure you are really passing it by reference.
Here is an example with an object:
<?php
// Passed by value... won't be affected
function byValue($arg) {
$arg = null;
}
// Passed by reference... will be affected
function byReference(&$arg) {
$arg = null;
}
$obj = new StdClass;
var_dump($obj); // Untouched object created
byValue($obj);
var_dump($obj); // After 'trying' to set it to null
byReference($obj);
var_dump($obj); // After setting it to null for real
Run demo
Related
I have created two code snippets at
http://codepad.viper-7.com/o5MxgF
and http://codepad.viper-7.com/qUpTag
In the second snippet, I was trying to use array as a call back because I found at
http://www.php.net/manual/en/language.types.array.php
that
Note: Both square brackets and curly braces can be used
interchangeably for accessing array elements (e.g. $array[42] and
$array{42} will both do the same thing in the example above).
So, I thought I could use array as a function so that I don't need the declare
$get_new_key = function ($k) use($fa) {
return $fa[$k];
};
But as you can see, I was getting a
: array_map() expects parameter 1 to be a valid callback, first array
member is not a valid class name or object in /code/qUpTag on line 8
error.
Are there anything to make array a callable without creating a companion function to access its values?
Thanks.
Having a callable array doesn't mean that it returns the values of the array. Instead, it's an array that points to a method on either a class or object.
For instance, array('MyClass', 'my_method') is a callable for the static method my_method on MyClass and array($object, 'method') is a callable for the instance method method on the object in $object.
You can read more about the callable type in the PHP documentation
Say we have the object $teams (an associative array) containing an object that provides the method getMembers() which returns an Array as follows:
$membersArray = $teams['blueteam']->getMembers();
If I want to then access individual members, I may do so as follows:
$membersArray[1];
Why can't I perform the access in-line as follows, and is there a proper way to do so in PHP?
$membersArray = $teams['blueteam']->getMembers()[1];
Support for this added in PHP 5.4.0:
$membersArray = $teams['blueteam']->getMembers()[1];
Rather than try to access it like that, why not make an alternative method called getMember() which accepts a parameter for the array index. For example:
function getMember( $index )
{
return $this->members[$index];
}
This makes your code a little more self-documenting by indicating getMembers will return an array of members, where getMember() will only return a single array element.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
In PHP can someone explain cloning vs pointer reference?
According to http://php.net/manual/en/language.oop5.references.php
One of the key-points of PHP 5 OOP that is often mentioned is that "objects are passed by references by default". This is not completely true. This section rectifies that general thought using some examples.
A PHP reference is an alias, which allows two different variables to write to the same value. 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.
If objects are passed by alias or handler then in what situation would you actually want to pass by reference?
myFunc($obj); // Pass by alias/handler
myFunc(& $obj); // Pass by reference (when would you do this?)
myFunc(clone $obj); // Create a new object
What are the different use-cases for each of these?
What are the different use-cases for each of these?
You already named the three different use-cases:
Passing an object as parameter
Passing an alias as parameter
Passing an object clone as parameter
Naturally you do each of these three things when you need to do the specific case. You normally only need 1. in 99.9%. You do the other 0.1% when you need to.
An examlary use-case (as asked for in comments) for a variable alias as parameter as a little example (Demo):
class VarFreezer
{
private $alias;
private $value;
public function __construct(&$object) {
$this->alias = &$object;
$this->value = $object;
}
public function restore()
{
$this->alias = $this->value;
}
}
$var = new stdClass();
$var->value = 'I am an object now.';
$freezer = new VarFreezer($var);
$var = 'I am a string now.';
$freezer->restore();
var_dump($var);
When you give an object to a method the reference to the existing objects is passed so that you have the variable in the calling scope and the method parameter variable in the method's scope both referencing to the same object.
You can explicitly use & to give an reference to a primitive like an array, so that an invoked method can change the data of the original array. For objects this is the default nowadays as you mentioned.
A clone seems obvious.
With &$obj you create an alias to $obj not a reference to the object $obj refers to. Quoting php.net:
$c = new A;
$d = &$c;
$c->foo = 2;
$c = NULL;
echo $d->foo."\n"; // Notice: Trying to get property of non-object...
myfunc($obj) //pass in identifier
For when your function needs to get access to the object and manipulate/ read from it somehow.
myfunc(& $obj) //pass in reference to the identifier.
The key here is that your passing a reference to the identifier. Which means that if the outer variable ($obj) is set to null, then the inner object also becomes null because you've removed the identifier from $obj and so the reference to it has also been removed. Unlike when you pass in the identifier because what your actually doing it passing in a copy of the identifier and so if $obj was set to null then the value inside the function would still point to the object.
example:
var x = new Object();
var y = x //y and x both contain **different** identifiers to the same object in memory.
function a($_a){} // a function for examples
a($y) // passes in a copy of $y so $_a (inside the function) is a third identifier pointing to th in memory object
a(& $y) // now $_a contains the **same** identifier as $y so if either is emptied then the other is also emptied.
a(clone $y) //creates a new object identical to the first and passes in an identifier of the new object.
hope thats clear enough.
you would pass an identifier by reference if you want to be able to modify or delete the identifier from inside the function and have it affect the external identifier variable whilst being able to return an independently calculated value. e.g serialize an object, save it to a file return the file string but also clear the identifier so the object can be cleared from memory.
I was browsing SO and found this hosted code as a recommended way of cutting down on PHP code.
https://github.com/jamierumbelow/codeigniter-base-model
So far, from the methods that I have figured out how to use, I love what it does and how simple it makes things.
However, in the following code:
/**
* Get a single record by creating a WHERE clause by passing
* through a CI AR where() call
*
* #param string $key The key to search by
* #param string $val The value of that key
* #return object
*/
public function get_by() {
$where =& func_get_args();
$this->_set_where($where);
$this->_run_before_get();
$row = $this->db->get($this->_table)
->row();
$this->_run_after_get($row);
return $row;
}
I'm not exactly sure how to make a call to this function.
The description of what it does is exactly what I want to do.
The #params say it takes in a key and value pair for the WHERE block but I don't see any function inputs in the method signature.
Help, please?
As I'm noticing with a lot of CI code, it's strange and maintenance un-friendly.
PHP functions can accept n or more arguments (where n is the number of arguments defined in the signature)
The code makes use of func_get_args() which returns an array of arguments.
The array of arguments is then passed to the _set_where() method which passes either one or two items to the db->where() method.
A more descriptive method signature would have been
public function get_by($key, $val = null)
For future reference, and like Phil mentioned, the *_by methods pass the value through to the db->where method. This means you can use it in a variety of methods:
$row = $this->model->get_by('key', $value);
Or using an array for multiple WHERE conditions:
$row = $this->model->get_by(array('key' => $value, 'other_key !=' => $value));
Or just the string (don't forget to escape your values!):
$row = $this->model->get_by('some_column = ' . $this->db->escape($value));
Since this question was asked, I've thoroughly updated the documentation so now it should all be a little clearer. Hope this helps.
This question already has answers here:
unpacking an array of arguments in php
(5 answers)
Closed 6 months ago.
In Python, you can pack and unpack arguments like so:
def myfunction(arg1, arg2, arg3):
pass
mydict = dict(arg1='foo', arg2='bar', arg3='baz')
myfunction(**mydict)
mylist = ['foo', 'bar', 'baz']
myfunction(*mylist)
Now, is there something similar in PHP? I have a class that is passed to a function, along with an associative array extracted from a database record. I want to create an instance of the class, which accepts arguments that are named the same as the keys in the array. Can it be done?
EDIT
Ok, so I definitely didn't formulate my question correctly. I wanted to instantiate a new class dynamically when I know neither the class name, nor the number of arguments beforehand.
I've found the Answer though:
http://php.net/manual/en/book.reflection.php
Here's the solution:
class MyClass {
public function __construct($var1, $var2, $var3) {
echo "$var1\n";
echo "$var2\n";
echo "$var3\n";
}
}
$reflector = new ReflectionClass('MyClass');
$args = array('var1'=>'foo', 'var2'=>'baz', 'var3'=>'bar');
$instance = $reflector->newInstanceArgs($args);
No you can't do it directly, you'll have to manually extract the values from the array and pass them to the constructor, or change it so that it accepts an array as argument.
You might take a look at call_user_func_array but I don't think you can use it with a constructor as you need an instance of the object to call one of its methods.
Note that the param list unpacking myfunc(*args) can be achieved with call_user_func_array():
$args = array("foo", "bar", "baz");
call_user_func_array("myfunc", $args);
For object methods you can use call_user_func_array(array($obj, "__construct"), $args). The deficiency here is the __constructor thus would be invoked twice, so should not do any funny things. Otherwise a specific init method is necessary.
Due to lack of named parameters an equivalent to myfunc(**args) does not exist in PHP. A common idiom is therefore to just pass the dict/array in, and let the function handle named params.