How to Access Object Properties With Names Like Integers or Invalid Property Names

How to access object properties with names like integers or invalid property names?

Updated for PHP 7.2

PHP 7.2 introduced a behavioral change to converting numeric keys in object and array casts, which fixes this particular inconsistency and makes all the following examples behave as expected.

One less thing to be confused about!


Original answer (applies to versions earlier than 7.2.0)

PHP has its share of dark alleys that you really don't want to find yourself inside. Object properties with names that are numbers is one of them...

What they never told you

Fact #1: You cannot access properties with names that are not legal variable names easily

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->123foo; // error

Fact #2: You can access such properties with curly brace syntax

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!

Fact #3: But not if the property name is all digits!

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!
echo $o->{'123'}; // error!

Live example.

Fact #4: Well, unless the object didn't come from an array in the first place.

$a = array('123' => '123');
$o1 = (object)$a;
$o2 = new stdClass;
$o2->{'123'} = '123'; // setting property is OK

echo $o1->{'123'}; // error!
echo $o2->{'123'}; // works... WTF?

Live example.

Pretty intuitive, don't you agree?

What you can do

Option #1: do it manually

The most practical approach is simply to cast the object you are interested in back into an array, which will allow you to access the properties:

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
$a = (array)$o;
echo $o->{'123'}; // error!
echo $a['123']; // OK!

Unfortunately, this does not work recursively. So in your case you 'd need to do something like:

$highlighting = (array)$myVar->highlighting;
$data = (array)$highlighting['448364']->Data;
$value = $data['0']; // at last!

Option #2: the nuclear option

An alternative approach would be to write a function that converts objects to arrays recursively:

function recursive_cast_to_array($o) {
$a = (array)$o;
foreach ($a as &$value) {
if (is_object($value)) {
$value = recursive_cast_to_array($value);
}
}

return $a;
}

$arr = recursive_cast_to_array($myVar);
$value = $arr['highlighting']['448364']['Data']['0'];

However, I 'm not convinced that this is a better option across the board because it will needlessly cast to arrays all of the properties that you are not interested in as well as those you are.

Option #3: playing it clever

An alternative of the previous option is to use the built-in JSON functions:

$arr = json_decode(json_encode($myVar), true);
$value = $arr['highlighting']['448364']['Data']['0'];

The JSON functions helpfully perform a recursive conversion to array without the need to define any external functions. However desirable this looks, it has the "nuke" disadvantage of option #2 and additionally the disadvantage that if there is any strings inside your object, those strings must be encoded in UTF-8 (this is a requirement of json_encode).

How to access object property with invalid characters

In JavaScript, object properties can be accessed by dot notation or bracket notation. Dot notation is often cleaner, but has restrictions. As you have noticed, your property contains an invalid character and therefore can't be accessed via dot notation. The solution, then, is to access the property using bracket notation like this: total['ga:newVisits'] so that your complete code will be {{total['ga:newVisits']}}. Live demo here (click).

Another nice feature about bracket notation is that it allows you to use a variable name as a property:

var myObj {
bar: '123'
};
var foo = 'bar';

console.log(myObj[foo]); //logs '123'

Object property name as number

You can reference the object's properties as you would an array and use either me[123] or me["123"]

Valid property names, property assignment and access in JavaScript

Short Answer

Object property names can be any valid identifier, numeric literal, or string literal (including the empty string).

With that said, there are some potentially confusing intricacies to keep in mind about JavaScript property names, as described below.

And unless you're working with valid (non-negative integer) array indexes, it's a good idea to explicitly assign all numerical property names as strings.

Negative Numbers

What might look like a negative number is actually an expression — something property names do not support.

// SyntaxError
const obj = { -12: 'nope' };

Fortunately, bracket notation handles expressions for us.

// Successful property assignment.
const obj = {};
obj[-12] = 'yup';

Typecasting

All property names are typecasted into strings before being stored.

const obj = {
12: '12'
};

console.log(typeof Object.keys(obj)[0]); // -> string

Parsing

But even before typecasting occurs, keys are parsed according to the syntax used, and transformed into a decimal literal.

const obj = {
// Valid string literal
'022': '022',

// Interpreted as decimal
6: '6',

// Interpreted as floating-point
.345: '0.345',

// Interpreted as floating-point
1.000: '1',

// Interpreted as floating-point
8.9890: '8.989',

// Interpreted as decimal
000888: '888',

// Interpreted as octal
0777: '511',

// Interpreted as hexadecimal
0x00111: '273',

// Interpreted as binary
0b0011: '3',
};


/* Quoted property name */
console.log(obj['022']); // "022"; as expected
console.log(obj[022]); // undefined; 022 is an octal literal that evaluates to 18 before our lookup ever occurs

/* Valid (non-negative integer) array index */
console.log(obj[6]); // "6"; as expected
console.log(obj['6']); // "6"; as expected

/* Non-valid array index */
console.log(obj[0x00111]); // "273"; we're accessing the property name as it was assigned (before it was parsed and typecasted)
console.log(obj['0x00111']); // undefined; after parsing and typecasting, our property name seems to have disappeared
console.log(obj['273']); // "273"; there it is, we found it using the evaluation of our original assignment

How can I access an object attribute that starts with a number?

What about this :

$Beeblebrox->{'2ndhead'}



Actually, you can do this for pretty much any kind of variable -- even for ones that are not class properties.

For example, you could think about a variable's name that contains spaces ; the following syntax will work :

${"My test var"} = 10;
echo ${"My test var"};

Even if, obviously, you would not be able to do anything like this :

$My test var = 10;
echo $My test var;



No idea how it's working internally, though... And after a bit of searching, I cannot find anything about this in the PHP manual.

Only thing I can find about {} and variables is in here : Variable parsing -- but not quite related to the current subject...



But here's an article that shows a couple of other possiblities, and goes farther than the examples I posted here : PHP Variable Names: Curly Brace Madness

And here's another one that gives some additionnal informations about the way those are parsed : PHP grammar notes

How do you access a dynamic property in an object?

This won't work in PHP < 7.2.0 and the issue is that the string-integer array keys are actually converted to integer property names, not strings. An alternate way to get an object from an array that will work:

$var = json_decode(json_encode(array('1' => 'Object one','2' => 'Object two')));
$num = "2";
var_dump( $var->$num );

See the Demo, in PHP < 7.2.0 the (object) cast converts to integer properties but json_decode creates string properties.

How to create an object with number properties?

Er... Yes, you can. It's the very same syntax you are already using:

$obj = (object)nuLL;
$obj->{1} = 'Hi!';
var_dump($obj);

(Demo)

need help understanding php object property names versus array keys

I 'm not sure if xdebug has any problems with non-standard property names, but you definitely do not want to use property names like these on purpose. It doesn't make sense.

If you intend to use objects for OO purposes then you will declare their properties formally. In this case it is flat out impossible to use "strange" property names and there is nothing more to say.

If you don't intend to use objects for OO then you simply end up having arrays into which you can index $like->this instead of $like['this']. While the first form is shorter and I do prefer it myself, it becomes much uglier when using properties $with->{'strange-names'} -- worse than the array access in fact. So again, there is nothing to be gained from doing this.

In some really exceptional situations there might be a case for property names like that -- for example if an object uses the names of its properties as column names in a database table. But even in such cases it is usually much more convenient to add a translation layer between PHP property names and database column names instead of fighting with the syntax every step of the way.

Bonus argument: If one look at this question is not enough to change your mind, nothing will. It's just a specific corner case, but we are talking about PHP. Corner cases should be... avoided.

How do I get the 'Status' element out of this object?

You can use curly brace syntax to access properties with invalid names

<?php
$myObject = json_decode('{"8772622": [{"status": "RENEWED"}]}');

var_dump($myObject);
var_dump($myObject->{'8772622'}[0]->status);

Output:

object(stdClass)#2 (1) {
["8772622"]=>
array(1) {
[0]=>
object(stdClass)#1 (1) {
["status"]=>
string(7) "RENEWED"
}
}
}
string(7) "RENEWED"

Alternativley, you could cast the object to an array and access it by index number

$status = ((array)$myObject)[8772622][0]->status;

Probably, you got this object from using json_decode() on the API response. Set it's second argument to true to get an array instead of an object in the first place.



Related Topics



Leave a reply



Submit