PHP: Illegal string offset because [] binds tighter than -> - php

I am fairly new to PHP and just had a learning experience that I am sharing here to help others who, like me, may need help to find the cause of this error and also because I still don't know what the solution is and am sure there is simply a syntax that I just haven't found yet to do what I need to do.
So, the problem can be demonstrated with something like this:
class Sample {
protected $foo = array();
public function magicSampleSetFunc($property, $key, $value) {
$this->$property[$key] = $value;
}
}
...
$s = new Sample();
$s->magicSampleSetFunc('foo', 'someKey', 'someVal');
Obviously, this isn't my real code (nor have I run it) this is just a minimal example to explain my situation. Here we have a member variable foo that is clearly an array and a generic function that is going to try to set a key and value into it. I can var_dump $this->$property and see that it is an array, but on the $this->$property[$key] line I get the error message: "Warning: Illegal string offset 'someKey' in ...".
At first I though it was saying that 'someKey' was an illegal string to use as an array offset, which didn't make sense. And, even if I wrap it in an isset it complains. The first thing I learned is that if you have a string in php, you can use the array access operator to get a character out of that string. So the warning message is actually complaining that 'someKey' is not a valid offset into a string (because it is not an integer offset). Okay, but I just var_dumped $this->$property and see that it is an array, so what gives? The second lesson was one of operator precedence. The array operator "[]" binds tighter than the indirection operator "->". So, the binding of that statement is actually something like: ( $this-> ( $property[$key] ) ). So, it is illegally trying to offset the string in $property by the index in $key. What I wanted was to offset the array in $this->$property by the index in $key.
So, now we come to my question. The third lesson I need to learn, and haven't figured out yet is how do I override this operator precedence issue? I tried ($this->$property)[$key] but that appears to be a sytax error. Is there some other syntax I can use to get the interpreter to understand what I meant to do? Or, do I have to assign $this->$property to a temporary variable first? If I do, wouldn't that mean that my actual member variable array is not updated? Do I need a temp reference or something? What's the right syntax for my situation here? Thanks!

this is the way to do it:
Your variable name is basically {$property} so when you do $this->$property[$key] I think PHP parser gets confused. I usually make sure that to explicitly state it to the parser that my variable name is $property which is done by using curly braces around variable.
Curly braces are used to explicitly specify the end of a variable name
class Sample {
protected $foo = array();
public function magicSampleSetFunc($property, $key, $value) {
$this->{$property}[$key] = $value;
}
}
...
$s = new Sample();
$s->magicSampleSetFunc('foo', 'someKey', 'someVal');

Related

Fatal error: Uncaught Error: Function name must be a string in... when upgrading to php 7.0 [duplicate]

I have object properties in my code that look like this:
$obj ->field_name_cars[0];
$obj ->field_name_clothes[0];
The problem is I have 100s of field names and need to write the property name dynamically. Otherwise, the object name and the keys for the property will always be the same. So I tried:
$obj -> $field[0];
Hoping that the name of the property would dynamically be changed and access the correct values. But, I keep getting 'undefined property $field in stdClass::$field;
More or less I am trying dynamically write the php before it executes so that it can output the proper values. Thoughts on how to approach this?
Update for PHP 7.0
PHP 7 introduced changes to how indirect variables and properties are handled at the parser level (see the corresponding RFC for more details). This brings actual behavior closer to expected, and means that in this case $obj->$field[0] will produce the expected result.
In cases where the (now improved) default behavior is undesired, curly braces can still be used to override it as shown below.
Original answer
Write the access like this:
$obj->{$field}[0]
This "enclose with braces" trick is useful in PHP whenever there is ambiguity due to variable variables.
Consider the initial code $obj->$field[0] -- does this mean "access the property whose name is given in $field[0]", or "access the element with key 0 of the property whose name is given in $field"? The braces allow you to be explicit.
I think you are looking for variable-variable type notation which, when accessing values from other arrays/objects, is best achieved using curly bracket syntax like this:
$obj->{field[0]}
The magic method __get is you friend:
class MyClass
{
private $field = array();
public function __get($name)
{
if(isset($this->field[$name]))
return $this->field[$name];
else
throw new Exception("$name dow not exists");
}
}
Usage:
$myobj = new MyClass();
echo $myobj->myprop;
Explanation: All your field data is stored in a array. As you access $myobj->myprop that property obviously does not exists in the class. That is where __get is called. __get looks up the name in the field array and returns the correct value.
today i face that challenge. I ended up with that style of development
$countTickets = new \stdClass;
foreach ($tickets as $key => $value) {
if(!isset($countTickets->total)){
$countTickets->total = 0;
}
if(!isset($countTickets->{$value['categoryname']})){
$countTickets->{$value['categoryname']} = 0;
}
$countTickets->total += $value['number'];
$countTickets->{$value['categoryname']} += $value['number'];
}
I worked on some code that used dynamically created object properties. I thought that using dynamically created object properties was pretty cool (true, in my opinion). However, my program took 7 seconds to run. I removed the dynamic object properties and replaced them object properties declared as part of each class (public in this case). CPU time went from over 7 seconds to 0.177 seconds. That's pretty substantial.
It is possible that I was doing something wrong in the way I was using dynamic object properties. It is also possible that my configuration is broken in some way. Of course, I should say that I have a very plain vanilla PHP configuration on my machine.

PHP $$var['index'] = something; fails, although I thought it would work just like $$var = something; does

I was trying to use the $$ syntax in PHP for accessing arrays where we can put name of a variable inside another variable and access that variable.
I have used this syntax many times before in different ways, but to my surprise this didn't work for me and lost lot of time over it.
Here is sample code to replicate my problem:
$test=array(
'a'=>array(array(1,2,3),array(4,5,6),array(7,8,9))
);
$var = 'test';
var_dump($$var);
var_dump($$var['a']);
The line var_dump($$var) works as expected, but I'm getting a Warning: Illegal string offset 'a' at line var_dump($$var['a']); and the var_dump prints just null
Why doesn't this work? What am I doing wrong here?
Is there any work around if the syntax is not supported for arrays?
Your $$var['a'] is equivalent to ${$var['a']}. Not ${$var}['a']. The latter being the workaround syntax you are looking for.
Quoting the PHP Manual on Variable Variables:
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.
See http://codepad.org/lR7QJygX

500 - Internal Server Error with extra pair of brackets

I have been looking for an error in my code since an hour. This was the error:
Writing:
if(isset(($_POST['to'])))
instead of
if(isset($_POST['to']))
I don't get why is this extra pair of brackets causing an Internal Server Error.
I don't think putting brackets around a variable never changes its value. I mean,
$a = $b;
$c = ($b);
$a==$c; //True
I am curious as to know why is it an error?
Thank you.
EDIT:
The above error was occurring for normal variable also.
This is because isset is not a function but a language construct; as such, its definition can be found in the language parser.
T_ISSET '(' isset_variables ')' { $$ = $3; }
It only expects one pair of braces; passing another pair will cause a parse error.
Im pretty sure it has something to do with the fact that isset can not take a function in parameter. You have to pass it a value. Your extra pair of parenthesis may be evaluated as a 'function' or something that need to be evaluated.
Normally, when you try to pass a function to isset, you get this error :
Can't use method return value in write context
isset:
Warning
isset() only works with variables as passing anything else will result in a parse error. For checking if constants are set use the defined() function.
Information can be passed to functions through arguments. An argument is just like a variable.
Arguments are specified after the function name, inside the parentheses. You can add as many arguments as you want, just seperate them with a comma.
The following example has a function with one argument ($fname). When the familyName() function is called, we also pass along a name (e.g. Jani), and the name is used inside the function, which outputs several different first names, but an equal last name:
<?php
function familyName($fname)
{
echo "$fname Refsnes.<br>";
}
familyName("Jani");
familyName("Hege");
familyName("Stale");
familyName("Kai Jim");
familyName("Borge");
?>

In what way is my array index an 'Illegal string offset'?

When "future-proofing" code by testing it on PHP 5.4, I get a warning I don't understand.
function __clone() {
$this->changed = TRUE;
foreach ($this->conditions as $key => $condition) {
if (
$condition['field']
instanceOf QueryConditionInterface) {
$this->conditions[$key]['field'] = clone($condition['field']);
}
}
}
I broke out $condition['field'] into its own row to reduce the amount of code to focus on. About that specific line, PHP has this to say
Warning: Illegal string offset 'field' in DatabaseCondition->__clone()
And I just can't see how 'field', is an illegal string offset. I'm guessing that I'm just missing something obvious, but if the community can't find a problem, I'll file a bug report.
I interpret the warning as "Under no circumstances is 'field' a valid key". This error would have made sense if I had tried to us for example an array as a key.
Without more knowledge about the creation of the conditions array/iterator, I can only assume that you should first check if the offset exists.
if(isset($condition['field']) && $condition['field'] instanceOf QueryConditionInterface)
Using isset in this situation is enough and faster than array_key_exists, the only difference is, if $condition['field'] is NULL isset will return falls, array_key_exists will return true, cause the key exists. But because you only want to work on fields that are an instance of QueryConditionInterface, you will running fine with isset.
The warning looks like its saying that $condition is a string. Without any knowledge of the code I don't whether that makes any sense.

PHP variable variables in {} symbols

I get the basics of variable variables, but I saw a syntax just know, which bogles my mind a bit.
$this->{$toShow}();
I don't really see what those {} symbols are doing there. Do they have any special meaning?
PHP's variable parser isn't greedy. The {} are used to indicate what should be considered part of a variable reference and what isn't. Consider this:
$arr = array();
$arr[3] = array();
$arr[3][4] = 'Hi there';
echo "$arr[3][4]";
Notice the double quotes. You'd expect this to output Hi there, but you actually end up seeing Array[4]. This is due to the non-greediness of the parser. It will check for only ONE level of array indexing while interpolating variables into the string, so what it really saw was this:
echo $arr[3], "[4]";
But, doing
echo "{$arr[3][4]}";
forces PHP to treat everything inside the braces as a variable reference, and you end up with the expected Hi there.
They tell the parser, where a variable name starts and ends. In this particular case it might not be needed, but consider this example:
$this->$toShow[0]
What should the parser do? Is $toShow an array or $this->$toShow ? In this case, the variable is resolved first and the array index is applied to the resulting property.
So if you actually want to access $toShow[0], you have to write:
$this->{$toShow[0]}
These curly braces can be used to use expressions to specify the variable identifier instead of just a variable’s value:
$var = 'foo';
echo ${$var.'bar'}; // echoes the value of $foobar
echo $$var.'bar'; // echoes the value of $foo concatenated with "bar"
$this->{$toShow}();
Break it down as below:
First this is a object oriented programming style as you got to see $this and ->. Second, {$toShow}() is a method(function) as you can see the () brackets.
So now {$toShow}() should somehow be parsed to a name like 'compute()'. And, $toShow is just a variable which might hold a possible function name. But the question remains, why is {} used around.
The reason is {} brackets substitues the value in the place. To clarify,
$toShow="compute";
so,
{$toShow}(); //is equivalent to compute();
but this is not true:
$toShow(); //this is wrong as a variablename is not a legal function name

Categories