PHP Variable object properties - php

I understand the concept of variable variables in PHP.
According to my understanding of variable variables in PHP the following code:
$foo = 'hello';
$$foo = 'world';
echo $foo . ' ' . $hello;
Will generate the output as:
Hello World
But I am finding it difficult to understand the case of variable object properties
Say I have a class called foo with a single property as follows:
class foo {
var $r = 'I am r.';
}
Now, creating the instance of the class foo and using the concept of variable variables the following code:
$foo = new foo();
$bar = 'r';
echo $foo->$bar;
will output:
I am r.
Up till everything is fine, but when I include a property having an array value it gets messed up for me.
Say for example I add another property with array value to the class foo and now the class looks like:
class foo {
var $arr = array('I am A.', 'I am B.', 'I am C.');
var $r = 'I am r.';
}
Now When I create the instance of the class foo and try to read the property $arr[1] as follows:
$arr = 'arr';
echo $foo->$arr[1];
Which outputs:
I am r.
and the output looks weird to me. I mean how does $foo->$arr[1] resolves to the propery $r in the class foo ?
Shouldn't doing $foo->$arr resolve into $foo->arr[1] (Note: There is no dollar sign) giving the output:
I am B.
How does this happen?
I am aware that doing $foor{$arr}[1] outputs "I am B."
My question is why does $foo->$arr doesn't resolve into $foo->arr ? Given that the variable $arr is holding the value 'arr' ?
A note to admins/supervisors: This question is not a duplicate; I have tried searching for the similar questions but none of it answered what I need to know.

class foo {
var $arr = array('I am A.', 'I am B.', 'I am C.');
var $r = 'I am r.';
}
$foo = new foo;
$arr = 'arr';
echo $foo->$arr[1];
It's order of precedence that's biting you here. The last line could be re-written as
echo $foo->{$arr[1]};
Since strings are arrays starting with index 0, $arr[1] references the second character in 'arr', i.e. 'r', and results in printing the string 'I am r.'.
To get the results you're expecting, you need to explicitly tell PHP which part of the expression is meant as the variable name, because by default it greedily takes pretty much everything. Replacing the last line with this will get the output you expect:
echo $foo->{$arr}[1];
This will evaluate $arr to its full string 'arr', and then use that as the label to the class member $arr in foo; the index will then grab the second entry in that array, 'I am B.'.

Related

Instantiate a class from a string returned from function call [duplicate]

I've been looking into substantiating a new class instance from a string in PHP. This is seems to be an acceptable procedure, however I am curious why it can't be done with the returns of a function call. I posted a short test below, and my results indicate it works if there is a variable intermediary (i.e. $bar = new $foo->callBar(); does not work, while $x = $foo->callBar(); $bar = new $x; does).
class Foo {
function callBar() {
return 'Bar';
}
}
class Bar {
function sayHi() {
echo 'Hi';
}
}
$foo = new Foo();
$bar = new $foo->callBar();
Warning: Uncaught Error: Class name must be a valid object or a string
in php shell code:1 Stack trace:
#0 {main} thrown in php shell code on line 1
$x = $foo->callBar();
$bar = new $x;
$bar->sayHi();
//Output: Hi
I'd love to know why that is, or if I'm wrong, what is the correct why of going about this?
As stated in the PHP Manual the new keyword accepts a string or an object as a class name. Optionally you can provide arguments to the constructor in parentheses at the end.
What this means is that the syntax roughly expects:
new [string|object] ()
// ^^ keyword ^^ name ^^ optional parentheses
Another important fact is that the keyword new has the highest precedence of all operators.
Combining the two facts together means that we cannot use parentheses to increase the precedence of the operation with new keyword. If we use parentheses PHP will treat them as the optional part of new syntax. Something like this won't work.
$bar = new ($foo->callBar());
// ^ missing class name
There is just no unambiguous way of telling PHP parser to treat this otherwise.
There is also another caveat worth remembering, which Edward Surov has partially mentioned in his answer. The class name can come from any variable which is a string or an instance.
The following code will create an object of class Bar:
$obj = ['a'=>'Bar'];
$bar = new $obj['a'];
// or
$obj = (object) ['a'=>'Bar'];
$bar = new $obj->a;
So, let's explain what your code does.
new $foo->callBar ()
// ^^ keyword ^^ name ^^ optional parentheses
Because your Foo class doesn't have a property callBar it will trigger Notice message, but PHP will check if it can be used as a class name anyway. And because it doesn't exist it can't be a string or an instance, which is why you see the error.
This has been fixed in PHP 8.
The Variable Syntax Tweaks RFC addressed this issue. Now you can use expressions when creating an instance of a class. The only thing to pay attention to is the operator precedence. When calling function/method you should use () to denote precedence. For example:
$bar = new ( $foo->callBar() ) ();
// ^^ expression ^^ optional parentheses
There are exactly four options of creating a class instance using new that I know:
Name class directly: $bar = new Bar;
Use variable directly: $barName = 'Bar'; $bar = new $barName;
Use object property: $obj = (object) ['barName' => 'Bar']; $bar = new $obj->barName;
Use existing instance: $bar1 = new Bar; $bar2 = new $bar1;
First half of an answer to your question is PHP parser: it prohibits many things like new (Foo) and new "Foo" that could used to build a hack.
Second half may hide in PHP sources: here's the C function that throws that exception.

Why does this example print twice?

When reading php documentation on variable passing, it seems this function is supposed to print
i am bar
i am bar
The documentation says; "When a string is specified in double quotes or with heredoc, variables are parsed within it."
function
<?php
class foo {
var $bar = 'I am bar.';
}
$foo = new foo();
$bar = 'bar';
$baz = array('foo', 'bar', 'baz', 'quux');
echo "{$foo->$bar}\n";
echo "{$foo->{$baz[1]}}\n";
?>
Can someone help me understand
echo "{$foo->{$baz[1]}}\n";
Why does this also print 'i am bar'? And why is $bar = 'bar' declared and what's its significance? When it's deleted the file only prints i am bar once but I am not sure why since the second echo statement seems like it should be printing the $baz variable array?
https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing
you declare
$foo = new foo();
and then the $foo->bar is 'I am bar.'
So
the first time 'I am bar.' is obvious.
the second time 'I am bar.' is a trick of php variable.
PHP will translate the following to
echo "{$foo->{$baz[1]}}\n";
==>
echo "{$foo->{$bar}}\n";
==>
echo "{$foo->bar}\n";
==>
echo "I am bar.";
That's why you got two line [I am bar.]

What's the difference in printing a value contained in an array key and printing a value contained in an array key of object property?

Consider below code snippets and their respective outputs :
Code snippet 1 :
<?php
$juices = array("apple", "orange", "koolaid1" => "purple");
echo "He drank some $juices[0] juice.".PHP_EOL;
echo "He drank some $juices[1] juice.".PHP_EOL;
class people {
public $john = "John Smith";
}
$people = new people();
echo "$people->john drank some $juices[0] juice.".PHP_EOL;
?>
Output of Code snippet 1 :
He drank some apple juice.
He drank some orange juice.
John Smith drank some apple juice.
Code snippet 2 :
<?php
class foo {
public $foo;
public $bar;
function __construct() {
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'MyName';
echo "My name is \"$name\". I am printing some $foo->foo.
Now, I am printing some $foo->bar[1].";
?>
Output of Code Snippet 2 :
Notice: Array to string conversion in hello.php on line 16
My name is "MyName". I am printing some Foo. Now, I am printing some Array[1].
If you look closely you can see that in first program I'm able to print a values contained in keys of an array viz. $juices[0], $juices[1] but in the second program I'm getting a Notice when I try to print a value contained in an array key and the array is an object property.
I'm not understanding why I'm getting this Array to String conversion notice for second program.
Someone, please clear the differences to me with good explanation and help me in printing the array key value in second program.
PHP can correctly determine the intended element and object that you are referencing in the first code block. However, in the second, PHP wants to be crystal clear on how to access the element of the object. PHP bends over backward in other cases to make coding simple (type-jugging and other such features), but in this case, it needs to reign us in and demand clarity in the script.
You only need to improve your syntax.
Use curly brackets to fix it up. (Demo)
<?php
class foo {
public $foo;
public $bar;
function __construct() {
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'MyName';
echo "My name is \"$name\". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.";
?>
A similar demo from the manual (though this one is using nowdoc, not double quotes -- still it is wrapping in {}):

How to insert variable inside variable

I Have the php code :
$rand = rand(1,5);
and I want to define var that has the name of the rand function like :
$$rand // if $rand= 1 then the var will be $1
and then do
switch($rand){
case(1):
$$rand = 'How many legs dog has ?';
$ans= '4'; }
The code is for defining security questions.
Hope someone got my idea. How can I do it ?
Sometimes it is convenient to be able to have variable variable names. That is, a variable name which can be set and used dynamically. A normal variable is set with a statement such as:
<?php
$a = 'hello';
?>
A variable variable takes the value of a variable and treats that as the name of a variable. In the above example, hello, can be used as the name of a variable by using two dollar signs. i.e.
<?php
$$a = 'world';
?>
At this point two variables have been defined and stored in the PHP symbol tree: $a with contents "hello" and $hello with contents "world". Therefore, this statement:
<?php
echo "$a ${$a}";
?>
produces the exact same output as:
<?php
echo "$a $hello";
?>
i.e. they both produce: hello world.
In order to use variable variables with arrays, you have to resolve an ambiguity problem. That is, if you write $$a[1] then the parser needs to know if you meant to use $a[1] as a variable, or if you wanted $$a as the variable and then the [1] index from that variable. The syntax for resolving this ambiguity is: ${$a[1]} for the first case and ${$a}[1] for the second.
Class properties may also be accessed using variable property names. The variable property name will be resolved within the scope from which the call is made. For instance, if you have an expression such as $foo->$bar, then the local scope will be examined for $bar and its value will be used as the name of the property of $foo. This is also true if $bar is an array access.
// Sanitize the arrays
$questions = array();
$answers = array();
// Build some questions and assign to the questions array
$questions[0] = 'How many legs does a dog have?';
$questions[1] = 'How many eyes does a human have?';
$questions[2] = 'How many legs does a spider have?';
// Add the answers, making sure the array index is the same as the questions array
$answers[0] = 4;
$answers[1] = 2;
$answers[2] = 8;
// Select a question to use
$questionId = rand(0, count($questions));
// Output the question and answer
echo 'questions: ' . $questions[$questionId];
echo 'answer: ' . $answers[$questionId];
Variables in PHP cannot start with a number.
${$rand} is the right way. Do note, however, that your variable name cannot start with a number.
Quoting the php manual:
Variable names follow the same rules as other labels in PHP. A valid variable name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thus: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

PHP Syntax ${"{$type}_method"}

I've been reading an PHP5 book, and the author commonly used this syntax
${"{$something}_somethingelse"};
I have no idea what that means. Does it dynamically generate a variable name?
Someone help me out?
It is a language feature called Variable variables.
Consider the following piece of code:
$a = 'hello';
This is pretty straight forward. It creates the variable $a and sets its value to 'hello'.
Let's move on with:
$$a = 'world';
${$a} = 'world';
Basically, since $a = 'hello', those two statement are the equivalent of doing:
$hello = 'world';
So the following:
echo "$a ${$a}";
Is the equivalent of doing:
echo "$a $hello";
Braces { }
The braces are used to prevent ambiguity problems from occurring. Consider the following:
$$a[1] = 'hello world';
Do you want to assign a variable named after the value of $a[1] or do you want to assign the index 1 of the variable named after $a?
For the first choice, you would write it as such:
${$a[1]} = 'hello world';
For the second choice:
${$a}[1] = 'hello world';
Your example
Now, for your example.
Let's consider that:
$something = 'hello';
Using your example as such:
${"{$something}_somethingelse"} = 'php rocks';
Would essentially be equivalent of doing:
$hello_somethingelse = 'php rocks';
They are 'variable variables'. See this.
Brackets allow you to make more advanced variable names. It your Case if $something was equal to test it would be:
${"test_somethingelse"};
Which is just an advanced variable name.
Here is an example.
$test = "test";
${"test_test"} = "test2";
echo $test; // prints test
echo ${"test_test"}; // prints test2
Using Variable Varaibles, as everyone else mentioned, you can create variables based on other variables. So in your case, he was making a variable based on $something's value
$something = "test";
${"{$something}_somethingelse"};
turns into
${"test_somethingelse"};
That will replace the {$something} with the value of $something.
I think the inner curly braces are just for readability and to help when doing $object->property etc.
Because it seems to be also in a variable, that is called a variable variable.
For example,
$foo = 'bar';
$$foo = 7;
echo $bar;
// produces 7;

Categories