Know if an object has sub properties in PHP - php

How do I know if an arbitrary object has any properties in PHP?
I need it for a recursive search on JSON objects as a break condition
i.e break search when the object has no more sub-objects.
I thought of property_exists but it checks a particular property, while I want to know if any property exists.

You can use a foreach loop:
foreach (new object as $prop => $value) {
echo "property \$$prop is $value\n";
}
Also You can do it conveniently with get_object_vars:
$propertyName = key(get_object_vars($object));

The function get_object_vars() will return a list of all accessible properties on an object.

You can try to use this function:
http://php.net/manual/en/function.get-object-vars.php
From the docs:
Returns an associative array of defined object accessible non-static properties for the specified object in scope. If a property has not been assigned a value, it will be returned with a NULL value.
You can also use the ReflectionClass to get the object properties like this:
$obj = new YourObjectClass;
$reflect = new ReflectionClass($obj);
$props = $reflect->getProperties();
foreach ($props as $prop) {
print $prop->getName() . "\n";

Casting the object to an array and performing a count on the resulting array will tell you if an object has properties.
$foo = new Bah();
$propertiesAsArray = (array) $foo;
if(count($propertiesAsArray)) {
//this object has properties
} else {
//this object does not have properties
}

You could use :
if(isset($yourobject)){
//YourCode
}
in a loop to see if the object have anything already set.

Related

Dynamically loop through and set PHP class variables

I have a class with several variables like:
class ABC
{
$var1=0;
$var2=0;
...
}
Instead of setting variables one by one like;
$ABC=new ABC();
$ABC->var1=1;
$ABC->var2=1;
...
How to loop through all class' (instance) variables and dynamically set them all to some value.
You could use get_object_vars to get the non static properties of the object, and then loop through that.
$object_vars = get_object_vars($ABC);
foreach ($object_vars as $name => $value) {
$ABC->{$name} = $newVal;
}
See more information here: http://php.net/manual/en/function.get-object-vars.php

Accessing an Object Property from a string

I would like to be able to access the value of a property from a single string...
$obj->Test->FGH = "Well Done!";
I have tried
var_dump($obj->{'Test->FGH'});
And
var_dump( eval( '$obj->Test->FGH' ) );
I know, the following will work, but it has to be defined from a string
var_dump ($obj->Test->FGH);
I also know the following will work, but it doesnt access the FGH property;
var_dump ($obj->{'Test'});
So how is it possible to return the value of $obj->Test->FGH, from a string?
You need to iterate through the object structure recursively until you find the property.
Here is a recursive function that does the job.
It only works if the searched value is not an object. You will have to modify it if the property you are looking for is an object, relying on wether the $props array is empty or not.
The $props argument needs to be ordered in the same way the object properties are nested.
You could also modify it to have a string as second argument, for example Test/FGH
function search_property($obj, $props) {
$prop = array_shift($props);
// If this is an object, go one level down
if (is_object($obj->$prop)) {
return search_prop($obj->$prop, $props);
}
if (!isset($obj->$prop)) {
return false;
}
return $obj->$prop;
}
$val = search_property($obj, array('Test', 'FGH'));

Global check for valid properties in a PHP stdClass?

Is it possible to check properties from a PHP stdClass? I have some models which are being generated as an stdClass. When using them I would like to check if the properties I'm calling exist in some kind of Core-class. I've noticed __get is ignored by the stdClass...
How can properties from a stdClass be checked if they exist in the object?
StdClass objects contain only porperties, not code. So you can't code anything from "within" them. So you need to work around this "shortcomming". Depending on what generates these classes this can be done by "overloading" the data (e.g. with a Decorator) providing the functionality you've looking for:
class MyClass
{
private $subject;
public function __construct(object $stdClass)
{
$this->subject = $stdClass;
}
public function __get($name)
{
$exists = isset($this->subject->$name);
#...
}
}
$myModel = new MyClass($model);
Use get_object_vars() to iterate through the stdClass object, then use the property_exists() function to see if the current property exists in the parent class.
Just cast it to an array
$x = (array) $myStdClassObject;
Then you can use all the common array functions

Can't write new elements to IteratorAggregate?

Am I correct in thinking that IteratorAggregate only provides array-like read access to an object? If I need to write to the object-as-array, then I need to use Iterator?
Demonstration of an IteratorAggregate object causing a fatal error when trying to add a new element follows:
<?
class foo implements IteratorAggregate {
public $_array = array('foo'=>'bar', 'baz'=>'quux');
public function getIterator() {
return new ArrayIterator($this->_array);
}
} // end class declaration
$objFoo = & new foo();
foreach ( $objFoo as $key => $value ) {
echo "key: $key, value: $value\n";
}
$objFoo['reeb'] = "roob";
foreach ( $objFoo as $key => $value ) {
echo "key: $key, value: $value\n";
}
$ php test.php
key: foo, value: bar
key: baz, value: quux
Fatal error: Cannot use object of type foo as array in /var/www/test.php on line 20
Call Stack:
0.0009 57956 1. {main}() /var/www/test.php:0
For what it's worth, given your sample class, you could have just made use of the ArrayObject (which implements IteratorAggregate and ArrayAccess like we want).
class Foo extends ArrayObject
{
public function __construct()
{
parent::__construct(array('foo' => 'bar', 'baz' => 'quux'));
}
}
$objFoo = new Foo();
foreach ( $objFoo as $key => $value ) {
echo "$key => $value\n";
}
echo PHP_EOL;
$objFoo['foo'] = 'badger';
$objFoo['baz'] = 'badger';
$objFoo['bar'] = 'badger';
foreach ( $objFoo as $key => $value ) {
echo "$key => $value\n";
}
With the output being, as expected:
foo => bar
baz => quux
foo => badger
baz => badger
bar => badger
Ok, now that I better understand what you mean, let me try to explain here.
Implementing an iterator (either via the Iterator or IteratorAggregate interface) will only add functionality when iterating. What that means, is it only affects the ability to use foreach. It doesn't change or alter any other use of the object. Neither of which directly support "writing" to the iterator. I say directly, since you can still write to the base object while iterating, but not inside of the foreach.
That means that:
foreach ($it as $value) {
$it->foo = $value;
}
Will work fine (since it's writing to the original object).
While
foreach ($it as &$value) {
$value = 'bar';
}
Most likely won't work since it's trying to write through the iterator (which isn't directly supported. It may be able to be done, but it's not the core design).
To see why, let's look at what's going on behind the scenes with that foreach ($obj as $value). It basically is identical to:
For Iterator classes:
$obj->rewind();
while ($obj->valid()) {
$value = $obj->current();
// Inside of the loop here
$obj->next();
}
For IteratorAggregate classes:
$it = $obj->getIterator();
$it->rewind();
while ($it->valid()) {
$value = $it->current();
// Inside of the loop here
$it->next();
}
Now, do you see why writing isn't supported? $iterator->current() does not return a reference. So you can't write to it directly using foreach ($it as &$value).
Now, if you wanted to do that, you'd need to alter the iterator to return a reference from current(). However, that would break the interface (since it would change the methods signature). So that's not possible. So that means that writing to the iterator is not possible.
However, realize that it only affects the iteration itself. It has nothing to do with accessing the object from any other context or in any other manor. $obj->bar will be exactly the same for an iterator object as it is for a non iterator object.
Now, there comes a difference between the Iterator and IteratorAggregate. Look back at the while equivalencies, and you may be able to see the difference. Suppose we did this:
foreach ($objFoo as $value) {
$objFoo->_array = array();
print $value . ' - ';
}
What would happen? With an Iterator, it would only print the first value. That's because the iterator operates directly on the object for each iteration. And since you changed what the object iterates upon (the internal array), the iteration changes.
Now, if $objFoo is an IteratorAggregate, it would print all the values that existed at the start of iteration. That's because you made a copy of the array when you returned the new ArrayIterator($this->_array);. So you can continue to iterate over the entire object as it was at the start of iteration.
Notice that key difference. Each iteration of a Iterator will be dependent upon the state of the object at that point in time. Each iteration of a IteratorAggregate will be dependent upon the state of the object at the start of iteration.* Does that make sense?
Now, as for your specific error. It has nothing to do with iterators at all. You're trying to access (write actually) a variable outside of the iteration. So it doesn't involve the iterator at all (with the exception that you're trying to check the results by iterating).
You're trying to treat the object as an array ($objFoo['reeb'] = 'roob';). Now, for normal objects, that's not possible. If you want to do that, you need to implement the ArrayAccess interface. Note that you don't have to have an iterator defined in order to use that interface. All it does is provide the ability to access specific elements of an object using an array like syntax.
Note I said array like. You can't treat it like an array in general. sort functions will never work. However, there are a few other interfaces that you can implement to make an object more array like. One is the Countable interface which enables the ability to use the count() function on an object (count($obj)).
For an example in the core of combining all of these into one class, check out the ArrayObject class. Each interface in that list provides the ability to use a specific feature of the core. The IteratorAggregate provides the ability to use foreach. The ArrayAccess provides the ability to access the object with an array-like syntax. The Serializable interface provides the ability to serialize and deserialize the data in a specific manor. The Countable interface allows for counting the object just like an array.
So those interfaces don't affect the core functionality of the object in any way. You can still do anything with it that you could do to a normal object (such as $obj->property or $obj->method()). What they do however is provide the ability to use the object like an array in certain places in the core. Each provides the functionality in a strict scope (Meaning that you can use any combination of them that you would like. You don't need to be able to access the object like an array to be able to count() it). That's the true power here.
Oh, and assigning the return value of new by reference is deprecated, so there's no need to do that...
So, as for your specific problem, here's one way around it. I simply implemented the ArrayAccess interface into your class so that $objFoo['reeb'] = 'roob'; will work.
class foo implements ArrayAccess, IteratorAggregate {
public $_array = array('foo'=>'bar', 'baz'=>'quux');
public function getIterator() {
return new ArrayIterator($this->_array);
}
public function offsetExists($offset) {
return isset($this->_array[$offset]);
}
public function offsetGet($offset) {
return isset($this->_array[$offset]) ? $this->_array[$offset] : null;
}
public function offsetSet($offset, $value) {
$this->_array[$offset] = $value;
}
public function offsetUnset($offset) {
if (isset($this->_array[$offset]) {
unset($this->_array[$offset]);
}
}
}
Then, you can try your existing code:
$objFoo = & new foo();
foreach ( $objFoo as $key => $value ) {
echo "key: $key, value: $value\n";
}
$objFoo['reeb'] = "roob";
foreach ( $objFoo as $key => $value ) {
echo "key: $key, value: $value\n";
}
And it should work fine:
key: foo, value: bar
key: baz, value: quux
key: foo, value: bar
key: baz, value: quux
key: reeb, value: roob
You could also "fix" it by changing the $objFoo->_array property directly (just like regular OOP). Simply replace the line $objFoo['reeb'] = 'roob'; with $objFoo->_array['reeb'] = 'roob';. That'll accomplish the same thing....
Note that this is the default behavior. You could hack together an inner iterator (the iterator returned by IteratorAggreagate::getIterator) that does depend upon the original objects state. But that's not how it's typically done

php Array Initialization

I need to initialize an array of objects in PHP.
Presently I have the following code:
$comment = array();
And when i am adding an element to the array
public function addComment($c){
array_push($this->comment,$c);
}
Here, $c is an object of class Comment.
But when I try to access an functions of that class using $comment, I get the following error:
Fatal error: Call to a member function
getCommentString() on a non-object
Can anyone tell me how to initialize an array of objects in php?
Thanks
Sharmi
$this->comment = array();
Looks like a scope problem to me.
If $comments is a member of a class, calling $comments inside a function of that class will not actually use the member, but rather use an instance of $comments belonging to the scope of the function.
If other words, if you are trying to use a class member, do $this->comments, not just $comments.
class foo
{
private $bar;
function add_to_bar($param)
{
// Adds to a $bar that exists solely inside this
// add_to_bar() function.
$bar[] = $param;
// Adds to a $bar variable that belongs to the
// class, not just the add_to_bar() function.
$this->bar[] = $param;
}
}
This code might help you:
$comments = array();
$comments[] = new ObjectName(); // adds first object to the array
$comments[] = new ObjectName(); // adds second object to the array
// To access the objects you need to use the index of the array
// So you can do this:
echo $comments[0]->getCommentString(); // first object
echo $comments[1]->getCommentString(); // second object
// or loop through them
foreach ($comments as $comment) {
echo $comment->getCommentString();
}
I think your problem is either how you are adding the objects to the array (what is $this->comment referencing to?) or you may be trying to call ->getCommentString() on the array and not on the actual objects in the array.
You can see what's in the array by passing it to print_r():
print_r($comment);
Presuming you have Comment objects in there, you should be able to reference them with $comment[0]->getCommentString().

Categories