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.
Related
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.
This question already has answers here:
Why can't I access DateTime->date in PHP's DateTime class?
(5 answers)
Closed 5 years ago.
Consider the following code sample:
$m_oDate = new DateTime('2013-06-12 15:54:25');
print_r($m_oDate);
echo $m_oDate->date;
Since PHP 5.3, this produces (something like) the following output:
DateTime Object
(
[date] => 2013-06-12 15:54:25
[timezone_type] => 3
[timezone] => Europe/Amsterdam
)
2013-06-12 15:54:25
However the following code:
$m_oDate = new DateTime('2013-06-12 15:54:25');
echo $m_oDate->date;
...simply emits an error:
Notice: Undefined property: DateTime::$date in ...
Why does print_r() "add" these properties to the object? Note that they are not defined as part of the DateTime class on the manual page.
This has been reported as Bug #49382 in PHP.
In PHP 5.3, internal functionality was added to allow print_r() to show details of the underlying timestamp value held by an instance of DateTime, to assist with debugging. A side effect of this change is that when the object is dumped to text, these phantom public properties are added to the instance.
The same effect can be achieved by using reflection to access these properties, and if you need to access the properties then using reflection would be the way to go, so you don't provoke the error.
However, it should be noted that you shouldn't really use these properties - since they are not defined as members of the object, there is no guarantee they will continue to carry the same data (or even exist) in future PHP versions. If you need to access the information, use the following methods, defined as part of the API, instead:
// $obj->date
$obj->format('Y-m-d H:i:s');
// $obj->timezone
$obj->getTimezone()->getName();
// or...
$obj->getTimezone()->getOffset();
// or...
$obj->getTimezone()->listAbbreviations(); // returns an array, so may need
// further processing to be of use
NB: The timezone_type property is not accessible through the PHP API. It is an internal value and not useful in userland, because it describes the type of string that timezone holds when the object is dumped - i.e. one of the three methods for obtaining timezone information in the code sample above. For completeness, its possible values are defined in the following way:
Value | Type | Userland equivalent
------+-----------------------+----------------------------------
1 | time offset | DateTimeZone::getOffset()
2 | TimeZone abbreviation | DateTimeZone::listAbbreviations()
3 | TimeZone identifier | DateTimeZone::getName()
There is some magic occurring but it's pretty simple.
The class DateTime doesn't have a public variable 'date' that you're meant to access. However, as a side effect of how PHP works, there is a variable created when you call print_r or var_dump on that class.
After that magic happens 'date' is available, but it shouldn't be. You should just use the getTimestamp function to make your code work reliably.
There's no date property in DateTime; that's why you're getting (Undefined property: DateTime::$date).
print_r() performs some introspection on the object to display its contents; this causes the object to magically create the ::date property. This is not documented though, so using this may break your code in the future.
You need something like $m_oDate->format('m-d-Y'); instead.
The problem lies here:
static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
{
// ...
zend_hash_update(props, "date", 5, &zv, sizeof(zval), NULL);
// ...
The function date_object_get_properties is called when any data dumping is made (print_r, var_dump, var_export). The hash table is updated for data representing, unfortunately this is made public.
Consider the following code-example:
$m_oDate = new DateTime('2013-06-12 15:54:25');
some_func($m_oDate);
echo $m_oDate->{'ROXXOR_IS_BACK!!'};
The most obvious difference to yours is that instead of the print_r function a different one some_func is called. The expectations might differ because you know print_r but you don't know some_func but that is only to sharpen your senses. Let's wait a moment until I show the definition of that function.
The second difference is the name of the property that is getting echoed. Here I've choosen a name that is really exceptional: {'ROXXOR_IS_BACK!!'}, again to sharpen your senses.
This name is that crazy that is must be obvious not being part of DateTime albeit when the example above is executed, it's totally clear that this roxxor property must exists. Program Output:
PHP never lets you down.
So how come? Yes, you sure already have an idea how that works. The some_func() function must have added it. So let's take a look into the functions definition:
function some_func($m_oDate) {
$m_oDate->{'ROXXOR_IS_BACK!!'} = 'PHP never lets you down.';
}
Yes, it's now clearly visible that this function has added a property to the object. And it also shows that this is totally easily possible with any object in PHP.
Compare with an array in PHP, you can also add new keys when you want to.
This example has not been chosen out of nothing, because this is where objects in PHP come from: They are just syntactic sugar around arrays and this has to with the time when objects in PHP were introduced back in PHP 3:
At the time classes were introduced to the source tree of what was to become PHP 3.0, they were added as syntactic sugar for accessing collections. PHP already had the notion of associative array collections, and the new critters were nothing but a neat new way of accessing them. However, as time has shown, this new syntax proved to have a much more far-reaching effect on PHP than was originally intended.
-- Zeev Suraski about the standard object since PHP 3 (archived copy) - via Why return object instead of array?
This is also the simple explanation why it's totally common in PHP that functions can add member variables which have not been defined earlier in the class. Those are always public.
So when you do assumptions about some object having a property or not, take a look where it comes from. It must not be part of the class but might have been added later.
And keep the following in mind:
Do not use print_r and var_dump in production code.
For the fun of it, this is how you can make it work, using Reflection:
$m_oDate = new DateTime('NOW');
$o = new ReflectionObject($m_oDate);
$p = $o->getProperty('date');
echo $p->getValue($m_oDate);
Source
You should use DateTime::format or DateTimeImmutable::format as the case may be instead
$m_oDate = new DateTime('NOW');
echo $m_oDate->format("r");
This question already has answers here:
Why can't I access DateTime->date in PHP's DateTime class?
(5 answers)
Closed 5 years ago.
Consider the following code sample:
$m_oDate = new DateTime('2013-06-12 15:54:25');
print_r($m_oDate);
echo $m_oDate->date;
Since PHP 5.3, this produces (something like) the following output:
DateTime Object
(
[date] => 2013-06-12 15:54:25
[timezone_type] => 3
[timezone] => Europe/Amsterdam
)
2013-06-12 15:54:25
However the following code:
$m_oDate = new DateTime('2013-06-12 15:54:25');
echo $m_oDate->date;
...simply emits an error:
Notice: Undefined property: DateTime::$date in ...
Why does print_r() "add" these properties to the object? Note that they are not defined as part of the DateTime class on the manual page.
This has been reported as Bug #49382 in PHP.
In PHP 5.3, internal functionality was added to allow print_r() to show details of the underlying timestamp value held by an instance of DateTime, to assist with debugging. A side effect of this change is that when the object is dumped to text, these phantom public properties are added to the instance.
The same effect can be achieved by using reflection to access these properties, and if you need to access the properties then using reflection would be the way to go, so you don't provoke the error.
However, it should be noted that you shouldn't really use these properties - since they are not defined as members of the object, there is no guarantee they will continue to carry the same data (or even exist) in future PHP versions. If you need to access the information, use the following methods, defined as part of the API, instead:
// $obj->date
$obj->format('Y-m-d H:i:s');
// $obj->timezone
$obj->getTimezone()->getName();
// or...
$obj->getTimezone()->getOffset();
// or...
$obj->getTimezone()->listAbbreviations(); // returns an array, so may need
// further processing to be of use
NB: The timezone_type property is not accessible through the PHP API. It is an internal value and not useful in userland, because it describes the type of string that timezone holds when the object is dumped - i.e. one of the three methods for obtaining timezone information in the code sample above. For completeness, its possible values are defined in the following way:
Value | Type | Userland equivalent
------+-----------------------+----------------------------------
1 | time offset | DateTimeZone::getOffset()
2 | TimeZone abbreviation | DateTimeZone::listAbbreviations()
3 | TimeZone identifier | DateTimeZone::getName()
There is some magic occurring but it's pretty simple.
The class DateTime doesn't have a public variable 'date' that you're meant to access. However, as a side effect of how PHP works, there is a variable created when you call print_r or var_dump on that class.
After that magic happens 'date' is available, but it shouldn't be. You should just use the getTimestamp function to make your code work reliably.
There's no date property in DateTime; that's why you're getting (Undefined property: DateTime::$date).
print_r() performs some introspection on the object to display its contents; this causes the object to magically create the ::date property. This is not documented though, so using this may break your code in the future.
You need something like $m_oDate->format('m-d-Y'); instead.
The problem lies here:
static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
{
// ...
zend_hash_update(props, "date", 5, &zv, sizeof(zval), NULL);
// ...
The function date_object_get_properties is called when any data dumping is made (print_r, var_dump, var_export). The hash table is updated for data representing, unfortunately this is made public.
Consider the following code-example:
$m_oDate = new DateTime('2013-06-12 15:54:25');
some_func($m_oDate);
echo $m_oDate->{'ROXXOR_IS_BACK!!'};
The most obvious difference to yours is that instead of the print_r function a different one some_func is called. The expectations might differ because you know print_r but you don't know some_func but that is only to sharpen your senses. Let's wait a moment until I show the definition of that function.
The second difference is the name of the property that is getting echoed. Here I've choosen a name that is really exceptional: {'ROXXOR_IS_BACK!!'}, again to sharpen your senses.
This name is that crazy that is must be obvious not being part of DateTime albeit when the example above is executed, it's totally clear that this roxxor property must exists. Program Output:
PHP never lets you down.
So how come? Yes, you sure already have an idea how that works. The some_func() function must have added it. So let's take a look into the functions definition:
function some_func($m_oDate) {
$m_oDate->{'ROXXOR_IS_BACK!!'} = 'PHP never lets you down.';
}
Yes, it's now clearly visible that this function has added a property to the object. And it also shows that this is totally easily possible with any object in PHP.
Compare with an array in PHP, you can also add new keys when you want to.
This example has not been chosen out of nothing, because this is where objects in PHP come from: They are just syntactic sugar around arrays and this has to with the time when objects in PHP were introduced back in PHP 3:
At the time classes were introduced to the source tree of what was to become PHP 3.0, they were added as syntactic sugar for accessing collections. PHP already had the notion of associative array collections, and the new critters were nothing but a neat new way of accessing them. However, as time has shown, this new syntax proved to have a much more far-reaching effect on PHP than was originally intended.
-- Zeev Suraski about the standard object since PHP 3 (archived copy) - via Why return object instead of array?
This is also the simple explanation why it's totally common in PHP that functions can add member variables which have not been defined earlier in the class. Those are always public.
So when you do assumptions about some object having a property or not, take a look where it comes from. It must not be part of the class but might have been added later.
And keep the following in mind:
Do not use print_r and var_dump in production code.
For the fun of it, this is how you can make it work, using Reflection:
$m_oDate = new DateTime('NOW');
$o = new ReflectionObject($m_oDate);
$p = $o->getProperty('date');
echo $p->getValue($m_oDate);
Source
You should use DateTime::format or DateTimeImmutable::format as the case may be instead
$m_oDate = new DateTime('NOW');
echo $m_oDate->format("r");
I've read about this interesting syntax in PHP:
$value = (new MyClass)->attribute1;
Is it ok to use it? I've never seen anything like this in any code I've analyzed. Any pros and cons?
Why can't I set the attribute using this syntax? Structures like this:
(new MyClass)->attribute1 = 'value1';
throw errors at '=' sign, no matter if the attribute exists in the class already.
Well i don't see the point of using it since you loose your reference to the object, you cannot use it anymore, and it breaks the OO concept.
I think (new MyClass)->attribute1 is resolved first, so it is the same as writing something like 42 = 12
This may have a sense, if the class MyClass supports internal static list (or hashmap) of all existing instances. This way you can create new object and get its unique ID or index in the list for future references (for example, by sending it via cookies to a client).
As for assignment, you'd post exact error message for this case. I can guess, that the error is about assigning something to a temporary value which is about to be destroyed.
How can I dynamically pass "items" to class function?
For example here it is a piece of some class and its function where I declare an element of object (items) as $b:
//..........
public function __add2SomeObj($b) {
$namespc = $this -> __someObj(); // __someObj() returns object
$namespc -> cats = $b;
}
//...................
Can I pass any other name instead cats dynamically so it won't be declared as a string?
i.e. something like:
//..........
public function __add2SomeObj($a,$b) {
$namespc = $this -> __someObj(); // __someObj() returns object
$namespc -> $a = $b;
}
//...................
} //end of class started above
$t=new aboveClass()
$t->__add2SomeObj("cats", array(1=>"PussyCat",2=>"Scratchy"));
$t->__add2SomeObj("dogs", array(1=>"Waffy",2=>"Sharik")); // once again but dogs...
Should I define a constant or what to make this or should i declare this protected varibale as object like (object) $vaaar?
Sorry I'm a little bit infamiliar with PHP OOP...
Yes you can do this. Read about variable variables:
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.
However you must be carful when dealing with arrays:
$namespc->$a[0]
will get the first element from the array that gets returned by $namespc->$a.
Whereas
$namespc->{$a[0]}
will first resolve $a[0], i.e. get the first value of the array $a, and use this as property name.
What you're asking is whether you can decide at runtime which property to change inside __add2SomeObj as you in the second listing. You can, and you did is correct.
However, properties must be strings __add2SomeObj, so you should ensure that the $a parameter is a string (it will be automatically converted to a string, but this may give unexpected results if $a is an object or an array).
Second, you're allowing the caller too change an arbitrary property. This may or may not violate the encapsulation of your class depending on the class __someObj returns and on the class of __add2SomeObj. It will also create a dynamic property on the object $namespce (i.e., one that does not exist in all the objects of that class), which you may not want.
Finally, and has a consequence of the point before, __add2SomeObj may generate a fatal error. So I'd say you'd better validate the $a paramater against a set of permitted property names.
You syntax is correct:
$obj->cats = $b;
and
$a = 'cats';
$obj->$a = $b;
will do the same thing.
As for whether or not to make "cats" a constant, that's up to you. I would suggest putting the error reporting up:
error_reporting(E_ALL | E_STRICT);
That way if you accidentally put in "cast" and that's not a valid member you'll get an error thrown.
Lastly, PHP is a dynamic language. I get the feeling your background might be with more static languages. This is just something you need to get comfortable with and you need to find a balance between readability and verbosity. But whatever you do, don't try and recreate non-PHP idioms in PHP, which is a common mistake for people coming from one language to another.