In C++ when you want a function to be able to read from an object, but not modify it, you pass a const reference to the function. What is the equivalent way of doing this in php?
I know objects in php5 are passed by reference by default, but for readability I think I will continue to use the ampersand before the variable name, like this:
function foo(&$obj)
{
}
If you want to pass an object but not by reference you can clone the object beforehand.
<?php
function changeObject($obj) {
$obj->name = 'Mike';
}
$obj = new StdClass;
$obj->name = 'John';
changeObject(clone $obj);
echo $obj->name; // John
changeObject($obj);
echo $obj->name; // Mike
I know objects in php5 are passed by reference by default, but for readability I think I will continue to use the ampersand before the variable name
That's your call but I find that would simply make it read more like C++. Alternativly, you can show that an object is being passed in by using type-hinting in your function definition:
function foo(StdClass $obj)
{
}
Once it's clear that $obj is an object it can be assumed that it's being passed by reference.
Related
Since objects are passed by reference by default now, is there maybe some special case when &$obj would make sense?
Objects use a different reference mechanism. &$object is more a reference of a reference. You can't really compare them both.
See Objects and references:
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.
&$object is something else than $object. I'll give you an example:
foreach ($objects as $object) {
if ($cond) {
$object = new Object(); // This won't affect $objects
}
}
foreach ($objects as &$object) {
if ($cond) {
$object = new Object(); // This will affect $objects
}
}
I won't answer the question if it makes sense, or if there is a need. These are opinion based questions. You can definitely live without the & reference on objects, as you could without objects at all. The existence of two mechanisms is a consequence of PHP's backward compatibility.
There are situations where you add & in front of function name, to return any value as a reference.
To call those function we need to add & in front of object.
If we add & in front of object, then it will return value as reference otherwise it will only return a copy of that variable.
class Fruit() {
protected $intOrderNum = 10;
public function &getOrderNum() {
return $this->intOrderNum;
}
}
class Fruitbox() {
public function TestFruit() {
$objFruit = new Fruit();
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 10
$intOrderNumber = $objFruit->getOrderNum();
$intOrderNumber++;
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 10
$intOrderNumber = &$objFruit->getOrderNum();
$intOrderNumber++;
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 11
}
}
Can I do this, maybe using ReflectionClass ?
myprintr($some_object);
function myprintr(){
foreach(func_get_args() as $key => $arg){
// here I want to get the name of the passed variable, "some_object"
// $key seems to be numeric...
}
}
You cannot get the name of the "variable", as there is no variable.
eg:
myprintr("test");
myprintr(myotherfun());
Note: I'm not sure what you are trying to do, but I just feels terrifyingly wrong.. the whole point of functions and objects is to create barriers, and it shouldn't matter what is in the caller's context..
If the user passes an object to myprintr(), then you can use
if (is_object($arg)) {
$className = get_class($arg);
}
to get the name of the object type that has been passed, which you can then feed to reflection
but the reflection constructor will accept either a class name or an object as an argument, so you don't even need the class name to instantiate a reflection class
EDIT
Just for the sake of playing a bit with this concept (and not creating any dependency on globals), or on whether the arguments are variables, values returned from functions, strings, etc:
class Test{};
function myTest() {
$some_object = new Test();
myprintr($some_object);
}
function myprintr(){
$callStack = debug_backtrace();
$calledAt = $callStack[0];
$callingFile = file($calledAt['file'],FILE_IGNORE_NEW_LINES);
$callingLine = $callingFile[$calledAt['line']-1];
$callingLine = substr($callingLine,strpos($callingLine,__METHOD__));
$calledWithArgNames = trim(substr($matches[0],1,-1));
var_dump($calledWithArgNames);
$args = func_get_args();
foreach($args as $arg) {
var_dump($arg);
}
}
myTest();
$some_object = new Test();
$some_other_object = &$some_object;
$t = 2;
$gazebo = "summer house";
$visigoth = pi() / 2; myprintr($some_other_object,pi(), atan2(pi(),$t), $visigoth, "Hello $t World", $gazebo); $t = log($t/$visigoth);
This retrieves all the arguments passed by the calling function in $calledWithArgNames, so for the first call you have:
'$some_object'
and for the second call:
'$some_other_object,pi(), atan2(pi(),$t), $visigoth, "Hello $t World", $gazebo'
This still requires splitting down into the individual arguments (a preg_split on commas, except where they're inside braces), but is certainly a step closer to what you're actually asking for.
You can't access argument names that don't exist: myprintr doesn't specify any variable names, and func_get_args() will only ever return a numerically indexed array.
I suppose you could add docblock comments and access them with reflection, but this seems like an extraordinary amount of overhead for functionality that you most likely don't need anyway. Using reflection on the function's arguments itself won't do anything for you because, again, you didn't specify any arguments in the function's argument signature.
PHP function arguments are ordered. They aren't something you can reference like an associative array. If you want access to "associative" type key names for a function or method's arguments, you'll have to specify an array argument and pass a value with the associative keys you want, like this:
myfunc(array $args=[])
{
$key1 = isset($args['key1']) ? $args['key1'] : NULL;
$key2 = isset($args['key2']) ? $args['key2'] : NULL;
}
If it is an object you can use func_get_args() and spl_object_hash() to identify the object and then search it in $GLOBALS. There you find the name.
class Test{};
$some_object = new Test();
myprintr($some_object);
function myprintr(){
$args = func_get_args();
$id = spl_object_hash($args[0]);
foreach($GLOBALS as $name => $value)
{
if (is_object($value) && spl_object_hash($value) == $id) echo $name;
}
}
http://codepad.org/gLAmI511
In PHP 5, are you required to use the & modifier to pass by reference? For example,
class People() { }
$p = new People();
function one($a) { $a = null; }
function two(&$a) { $a = null; )
In PHP4 you needed the & modifier to maintain reference after a change had been made, but I'm confused on the topics I have read regarding PHP5's automatic use of pass-by-reference, except when explicity cloning the object.
In PHP5, is the & modifier required to pass by reference for all types of objects (variables, classes, arrays, ...)?
are you required to use the & modifier to pass-by-reference?
Technically/semantically, the answer is yes, even with objects. This is because there are two ways to pass/assign an object: by reference or by identifier. When a function declaration contains an &, as in:
function func(&$obj) {}
The argument will be passed by reference, no matter what. If you declare without the &
function func($obj) {}
Everything will be passed by value, with the exception of objects and resources, which will then be passed via identifier. What's an identifier? Well, you can think of it as a reference to a reference. Take the following example:
class A
{
public $v = 1;
}
function change($obj)
{
$obj->v = 2;
}
function makezero($obj)
{
$obj = 0;
}
$a = new A();
change($a);
var_dump($a);
/*
output:
object(A)#1 (1) {
["v"]=>
int(2)
}
*/
makezero($a);
var_dump($a);
/*
output (same as before):
object(A)#1 (1) {
["v"]=>
int(2)
}
*/
So why doesn't $a suddenly become an integer after passing it to makezero? It's because we only overwrote the identifier. If we had passed by reference:
function makezero(&$obj)
{
$obj = 0;
}
makezero($a);
var_dump($a);
/*
output:
int(0)
*/
Now $a is an integer. So, there is a difference between passing via identifier and passing via reference.
Objects will pass-by-reference. Built in types will be pass-by-value (copied);
What is happening behind the scenes is that when you pass in a variable that holds an object, it's a reference to the object. So the variable itself is copied, but it still references the same object. So, essentially there are two variable, but both are pointing to the same object. Changes made to objects inside a function will persist.
In the case of the code that you have there (first you need $ even with &):
$original = new Object();
one($original); //$original unaffected
two($original); //$original will now be null
function one($a) { $a = null; } //This one has no impact on your original variable, it will still point to the object
function two(&$a) { $a = null; ) //This one will set your original variable to null, you'll lose the reference to the object.
You're using it wrong. The $ sign is compulsory for any variable. It should be:
http://php.net/manual/en/language.references.pass.php
function foo(&$a)
{
$a=null;
}
foo($a);
To return a reference, use
function &bar($a){
$a=5;
return $a
}
In objects and arrays, a reference to the object is copied as the formal parameter, any equality operations on two objects is a reference exchange.
$a=new People();
$b=$a;//equivalent to &$b=&$a roughly. That is the address of $b is the same as that of $a
function goo($obj){
//$obj=$e(below) which essentially passes a reference of $e to $obj. For a basic datatype such as string, integer, bool, this would copy the value, but since equality between objects is anyways by references, this results in $obj as a reference to $e
}
$e=new People();
goo($e);
I understand that by using "=" objects are assigned by reference and for other variable and for datatypes like strings and integers you need to use "=&" to assign by reference. When you assign an object by reference explicitly using "=&", it seems to not affect assignment. However when you assign the object to a global it does.
Consider the following:
<?php
$global_obj = null;
class my_class {
var $value;
public function __construct() {
global $global_obj;
$global_obj =& $this;
$GLOBALS['some_var'] = $this;
}
}
$a = new my_class();
$a->my_value = 5;
$global_obj->my_value = 10;
echo 'A: ' . $a->my_value; //5
echo '<br />';
echo 'Global Object: ' . $global_obj->my_value; //10
echo '<br />';
echo 'Globals Array Value: ' . $some_var->my_value; //5
?>
If you remove the ampersand in the code above $this is assigned to $global_obj by reference. My question is why does having the ampersand in there seemingly stop this from happening?
Thanks
What is happening here is that $global_obj is a reference to the variable $this. $this is a pseudo-variable that exists inside methods to be a reference to the current object. But who knows what happens to $this when it goes outside of the method scope. It is probably a bad idea to have a reference to $this.
In fact, if you investigate it a bit further, after your constructor returns, if you examine $global_obj, its value is null. The PHP engine probably sets $this to null after the method exits (but this is not documented anywhere), and your $global_obj, since it is a reference to the variable $this, follows it. When you set an attribute on null, it instantiates a new object of class stdClass automatically, so it seems to succeed. But of course this is a completely different object than the one in $a.
I have this:
one string variable which holds the class name ($classname)
one string variable with holds the property name ($propertyname)
I want to get that property from that class, the problem is, the property is static and I don't know how to do that.
If the property weren't static, it would have been:
$classname->$propertyname;
if the property were a method, I could have used call_user_function
call_user_func(array($classname, $propertyname));
But in my case, am I just lost. I am however hoping that it is possible. With the thousands of functions that PHP has, he'd better have something for this as well. Maybe I'm missing something?
Thanks!
Edit:
for those with eval() solutions: thanks, but it is out of the question
for those with get _class _vars() solutions: thanks, but it seems it returns "the default properties of the given class" (php.net), and yes, I would like that value to be changable (even though it does help me in some of the cases)
If you are using PHP 5.3.0 or greater, you can use the following:
$classname::$$propertyname;
Unfortunately, if you are using a version lower than 5.3.0, you are stuck using eval() (get_class_vars() will not work if the value is dynamic).
$value = eval($classname.'::$'.$propertyname.';');
EDIT: I've just said get_class_vars() wouldn't work if the value is dynamic, but apparently, variable static members are part of "the default properties of a class". You could use the following wrapper:
function get_user_prop($className, $property) {
if(!class_exists($className)) return null;
if(!property_exists($className, $property)) return null;
$vars = get_class_vars($className);
return $vars[$property];
}
class Foo { static $bar = 'Fizz'; }
echo get_user_prop('Foo', 'bar'); // echoes Fizz
Foo::$bar = 'Buzz';
echo get_user_prop('Foo', 'bar'); // echoes Buzz
Unfortunately, if you want to set the value of the variable, you will still need to use eval(), but with some validation in place, it's not so evil.
function set_user_prop($className, $property,$value) {
if(!class_exists($className)) return false;
if(!property_exists($className, $property)) return false;
/* Since I cannot trust the value of $value
* I am putting it in single quotes (I don't
* want its value to be evaled. Now it will
* just be parsed as a variable reference).
*/
eval($className.'::$'.$property.'=$value;');
return true;
}
class Foo { static $bar = 'Fizz'; }
echo get_user_prop('Foo', 'bar'); // echoes Fizz
set_user_prop('Foo', 'bar', 'Buzz');
echo get_user_prop('Foo', 'bar'); // echoes Buzz
set_user_prop() with this validation should be secure. If people start putting random things as $className and $property, it will exit out of the function as it won't be an existing class or property. As of $value, it is never actually parsed as code so whatever they put in there won't affect the script.
I think this is the simplest:
$foo = new ReflectionProperty('myClassName', 'myPropertyName');
print $foo->getValue();
To return a variable value that is set by a Static Variable you need to call:
$static_value = constant($classname.'::'.$propertyname);
Check out the documentation :: PHP Constant Documentation
You should be able to do something like:
eval("echo $classname::$propertyname;");
I just did a quick test and got this to work for me. Not sure if there's a better way or not, but I wasn't able to find one.
'eval' looks so close to 'evil', and I hate using it and/or seeing it in code. With a few ideas from other answers, here's a way to avoid it even if your php isn't 5.3 or higher.
Changed to reflect testing based on a comment.
class A {
static $foo = 'bar';
}
A::$foo = 'baz';
$a = new A;
$class = get_class($a);
$vars = get_class_vars($class);
echo $vars['foo'];
Outputs 'baz'.
One thing I noticed is that you can't set variables which are protected in static classes as the eval() command runs in a scope outside the class. The only thing to get around this would be to implement a static method inside the/every class which runs the eval(). This method could be protected as the call_user_func() [to call the setter method] also runs from inside the class.
Potentially relevant: discussion on late static binding in PHP - When would you need to use late static binding?.
get_class_vars is not same as get_object_vars.
I think get_clas_vars should return the original property values.
Even if for you said eval is out of the question, prior PHP 5.3 the easiest solution is still by using eval:
eval("\$propertyval = $classname::\$propertyname;");
echo $propertyval;
Getting and setting both static and non static properties without using Reflection
Using Reflection works but it is costly
Here is what I use for this purpose,
It works for PHP 5 >= 5.1.0 because I'm using property_exist
function getObjectProperty($object, $property)
{
if (property_exists(get_class($object), $property)) {
return array_key_exists($property, get_object_vars($object))
? $object->{$property}
: $object::$$property;
}
}
function setObjectProperty($object, $property, $value)
{
if (property_exists(get_class($object), $property)) {
array_key_exists($property, get_object_vars($object))
? $object->{$property} = $value
: $object::$$property = $value;
}
}
You can use ReflectionClass:
class foo
{
private static $bar = "something";
}
$class = "foo";
$reflector = new ReflectionClass($class);
$static_vars = $reflector->getStaticProperties();
var_dump($static_vars["bar"]);