What is the iterator_to_array function in PHP? - php

I'm having a hard time understanding PHP's iterator_to_array function.
I tried reading the manual but that didn't help.
What is it? How can I use it? What are the appropriate use cases?

In a nutshell, iterator_to_array() function takes an iterator of type Traversable and convert it to an associative/non-associative array, depending upon the argument provided. From the documentation,
array iterator_to_array ( Traversable $iterator [, bool $use_keys = true ] )
The function takes the following two arguments,
The first argument is of type Traversal, which is an interface. Both IteratorAggregate and Iterator class extends this interface. You can implement these two classes in your custom class, like this:
class myIterator implements IteratorAggregate {
private $array = array('key1'=>'value1', 'value2', 'value3', 'value4');
public function getIterator(){
return new ArrayIterator($this->array);
}
}
$obj = new myIterator;
$array = iterator_to_array($obj->getIterator(), true);
var_dump($array);
Or,
class myIterator implements Iterator {
private $key;
private $array = array('key1'=>'value1', 'value2', 'value3', 'value4');
public function __construct(){
$this->key = key($this->array);
}
public function rewind(){
reset($this->array);
$this->key = key($this->array);
}
public function current(){
return $this->array[$this->key];
}
public function key(){
return $this->key;
}
public function next(){
next($this->array);
$this->key = key($this->array);
}
public function valid(){
return isset($this->array[$this->key]);
}
}
$obj = new myIterator;
$array = iterator_to_array($obj, true);
var_dump($array);
The most important point to note here is that argument 1 passed to iterator_to_array() function must implement interface Traversable, so you cannot directly pass an array or object of any other type to this function. See the following example,
$array = array('key1'=>'value1', 'value2', 'value3', 'value4');
$array = iterator_to_array($array, true); // wrong
The second argument is a boolean value, to indicate whether to use the iterator element keys as index or not. See Example #1 here.

Iterators are useful because they allow you to use a custom defined order of data within a foreach loop. Let's take this (slightly pared down) example from the PHP manual:
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
function rewind() {
$this->position = 0;
}
function current() {
return $this->array[$this->position];
}
function key() {
return $this->position;
}
function next() {
++$this->position;
}
function valid() {
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
To iterate over it, we can use a foreach loop just like an array:
foreach($it as $ele){
echo $ele;
}
Unlike an array, however, you cannot access a single element without iterating to it first. Attempting to do so will give you a fatal error:
/*
* Fatal error: Uncaught Error: Cannot use object of type myIterator as array
* in /home/hpierce/sandbox.php
*/
echo $it[1];
//Iterating to the second element.
$it->next();
// "secondelement"
echo $it->current();
To access a single element, you could use iterator_to_array() to cast the iterator as an array and then access the element without the extra step:
$array = iterator_to_array($it);
// "secondelement"
echo $array[1];
If you know ahead of time that you will need to access a single element within an iterator, you should consider using ArrayIterator instead.

Related

PHP object comparison and private properties

I am wondering how PHP determines the equality of instances of a class with private properties:
class Example {
private $x;
public $y;
public __construct($x,$y) {
$this->x = $x; $this->y = $y;
}
}
and something like
$needle = new Example(1,2);
$haystack = [new Example(2,2), new Example(1,2)];
$index = array_search($needle, $haystack); // result is 1
The result is indeed 1, so the private member is compared. Is there a possibility to only match public properties?
I know I could overwrite the __toString method and cast all arrays and needles to string, but that leads to ugly code.
I am hoping to find a solution that is elegant enough to work with in_array, array_search, array_unique, etc.
A possible solution could be the PHP Reflection API. With that in mind you can read the public properties of a class and compare them to other public properties of another instance of the same class.
The following code is a simple comparison of public class properties. The base for the comparison is a simple value object.
declare(strict_types=1);
namespace Marcel\Test;
use ReflectionClass;
use ReflectionProperty;
class Example
{
private string $propertyA;
public string $propertyB;
public string $propertyC;
public function getPropertyA(): string
{
return $this->propertyA;
}
public function setPropertyA(string $propertyA): self
{
$this->propertyA = $propertyA;
return $this;
}
public function getPropertyB(): string
{
return $this->propertyB;
}
public function setPropertyB($propertyB): self
{
$this->propertyB = $propertyB;
return $this;
}
public function getPropertyC(): string
{
return $this->propertyC;
}
public function setPropertyC($propertyC): self
{
$this->propertyC = $propertyC;
return $this;
}
public function __compare(Example $b, $filter = ReflectionProperty::IS_PUBLIC): bool
{
$reflection = new ReflectionClass($b);
$properties = $reflection->getProperties($filter);
$same = true;
foreach ($properties as $property) {
if (!property_exists($this, $property->getName())) {
$same = false;
}
if ($this->{$property->getName()} !== $property->getValue($b)) {
$same = false;
}
}
return $same;
}
}
The __compare method of the Example class uses the PHP Reflection API. First we build a reflection instance of the class to which we want to compare to the current instance. Then we request all public properties of the class we want to compare to. If a public property does not exist in the instance or the value of the property is not the same as in the object we want to compare to, the method returns false, otherwise true.
Some examples.
$objectA = (new Example())
->setPropertyA('bla')
->setPropertyB('yadda')
->setPropertyC('bar');
$objectB = (new Example())
->setPropertyA('foo')
->setPropertyB('yadda')
->setPropertyC('bar');
$result = $objectA->__compare($objectB);
var_dump($result); // true
In this example the comparison results into true because the public properties PropertyB and PropertyC exist in both instances and have the same values. Keep in mind, that this comparison works only, if the second instance is the same class. One could spin this solution further and compare all possible objects based on their characteristics.
In Array Filter Example
It is a kind of rebuild of the in_array function based on the shown __compare method.
declare(strict_types=1);
namespace Marcel\Test;
class InArrayFilter
{
protected ArrayObject $data;
public function __construct(ArrayObject $data)
{
$this->data = $data;
}
public function contains(object $b)
{
foreach ($this->data as $object) {
if ($b->__compare($object)) {
return true;
}
}
return false;
}
}
This filter class acts like the in_array function. It takes a collection of objects and checks, if an object with the same public properties is in the collection.
Conclusion
If you want this solution to act like array_unique, array_search or ìn_array you have to code your own callback functions which execute the __compare method in the way you want to get the result.
It depends on the amount of data to be handled and the performance of the callback methods. The application could consume much more memory and therefore become slower.

Using object implementing ArrayAccess and Iterator as variadic parameter

I have a class that implements both ArrayAccess and Iterator.
I'm trying to figure out how to pass this object variadic parameter to a native function like array_merge:
array_merge(...$object);
To my disappointment I'm getting an error saying that $object is not an array.
array_merge(): Argument #1 is not an array
I've looked at these other interfaces but non of them seem obvious: IteratorAggregate, Serializable, Countable. Also ArrayObject turned out to be a dead end.
I do have a getter for to convert to array. But I was kind of hopping to discover my $object transform into an array just by implementing either ArrayAccess or Iterator, since it is about unfolding the array.
Is there another interface I can implement to make my class be more array-like?
This is a new language feature as documented in the migration guide from 5.5.x to 5.6.x in the manual (section Argument unpacking via ...), you must be on a pre-5.6.x runtime.
If you cannot upgrade your runtime, you must make use of your getter for converting it to an array (similar to ArrayObject's getArrayCopy):
call_user_func_array('array_merge', $arr->getArrayCopy());
Tests
The code below (based on PHP's documentation examples for ArrayAccess and Iterator) executed successfully on PHP 5.6.2, 5.6.17 and 7.0.1. It fails indeed on older versions (5.5.31 and older).
$arr = new MyArray();
$arr[0] = array(1, 2);
$arr[1] = array(3, 4);
// MyArray
print(get_class($arr));
// Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
print_r(array_merge(...$arr));
Implementation of MyArray:
class MyArray implements ArrayAccess, Iterator
{
private $container = array();
private $position = 0;
public function getArrayCopy() {
return $this->container;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
function rewind() {
$this->position = 0;
}
function current() {
return $this->container[$this->position];
}
function key() {
return $this->position;
}
function next() {
++$this->position;
}
function valid() {
return isset($this->container[$this->position]);
}
}

A faster way of doing objectToArray

Ive got this snippet of code below which works perfectly fine. I have been profiling it and the bit of code gets used alot of times, so I want to try figure out how to write it in a way that will perform better than the current way its written.
Is there a more efficient way to write this?
function objectToArray($d) {
if (is_object($d)) {
// Gets the properties of the given object
// with get_object_vars function
$d = get_object_vars($d);
}
if (is_array($d)) {
// Return array converted to object Using __FUNCTION__ (Magic constant) for recursive call
return array_map(__FUNCTION__, $d);
}
else {
// Return array
return $d;
}
}
You could implement a toArray() method to the class that needs to be converted:
e.g.
class foo
{
protected $property1;
protected $property2;
public function __toArray()
{
return array(
'property1' => $this->property1,
'property2' => $this->property2
);
}
}
Having access to the protected properties and having the whole conversion encapsulated in the class is in my opinion the best way.
Update
One thing to note is that the get_object_vars() function will only return the publically accessible properties - Probably not what you are after.
If the above is too manual of a task the accurate way from outside the class would be to use PHP (SPL) built in ReflectionClass:
$values = array();
$reflectionClass = new \ReflectionClass($object);
foreach($reflectionClass->getProperties() as $property) {
$values[$property->getName()] = $property->getValue($object);
}
var_dump($values);
depends what kind of object it is, many standard php objects have methods built in to convert them
for example MySQLi results can be converted like this
$resultArray = $result->fetch_array(MYSQLI_ASSOC);
if its a custom class object you might consider implementing a method in that class for that purpose as AlexP sugested
Ended up going with:
function objectToArray($d) {
$d = (object) $d;
return $d;
}
function arrayToObject($d) {
$d = (array) $d;
return $d;
}
As AlexP said you can implement a method __toArray(). Alternatively to ReflexionClass (which is complex and expensive), making use of object iteration properties, you can iterate $this as follow
class Foo
{
protected $var1;
protected $var2;
public function __toArray()
{
$result = array();
foreach ($this as $key => $value) {
$result[$key] = $value;
}
return $result;
}
}
This will also iterate object attributes not defined in the class: E.g.
$foo = new Foo;
$foo->var3 = 'asdf';
var_dump($foo->__toArray());)
See example http://3v4l.org/OnVkf
This is the fastest way I have found to convert object to array. Works with Capsule as well.
function objectToArray ($object) {
return json_decode(json_encode($object, JSON_FORCE_OBJECT), true);
}

How to unset nested array with ArrayObject?

ideone
Sample Code:
<?php
$a = new ArrayObject();
$a['b'] = array('c'=>array('d'));
print_r($a);
unset($a['b']['c']);
print_r($a);
Output
ArrayObject Object
(
[b] => Array
(
[c] => Array
(
[0] => d
)
)
)
ArrayObject Object
(
[b] => Array
(
[c] => Array
(
[0] => d
)
)
)
You notice that $a['b']['c'] is still there, even after unsetting. I would expect $a to have just the one value left (b).
In my actual app, I get the following warning:
Indirect modification of overloaded element of MyClass has no effect
Where MyClass extends ArrayObject. I have a lot of code that depends on being able to unset nested elements like this, so how can I get this to work?
One way to do it
<?php
$a = new ArrayObject();
$a['b'] = array('c' => array('d'));
$d =& $a['b'];
unset($d['c']);
print_r($a['b']);
prints:
Array
(
)
Would have to think a bit longer for an explanation as to why the syntax you've originally used doesn't remove the element.
EDIT: Explanation of behavior
What's happening is the call to unset($a['b']['c']); is translated into:
$temp = $a->offsetGet('b');
unset($temp['c']);
since $temp is a copy of $a instead of a reference to it, PHP uses copy-on-write internally and creates a second array where $temp doesn't have ['b']['c'], but $a still does.
ANOTHER EDIT: Reusable Code
So, no matter which way you slice it, seems like trying to overload function offsetGet($index) to be function &offsetGet($index) leads to trouble; so here's the shortest helper method I came up w/ could add it as a static or instance method in a subclass of ArrayObject, whatever floats your boat:
function unsetNested(ArrayObject $oArrayObject, $sIndex, $sNestedIndex)
{
if(!$oArrayObject->offSetExists($sIndex))
return;
$aValue =& $oArrayObject[$sIndex];
if(!array_key_exists($sNestedIndex, $aValue))
return;
unset($aValue[$sNestedIndex]);
}
So the original code would become
$a = new ArrayObject();
$a['b'] = array('c' => array('d'));
// instead of unset($a['b']['c']);
unsetNested($a, 'b', 'c');
print_r($a['b']);
YET ANOTHER EDIT: OO Solution
OK - So I must have been scrambling this morning b/c I found an error in my code, and when revised, we can implement a solution, based on OO.
Just so you know I tried it, extension segfaults..:
/// XXX This does not work, posted for illustration only
class BadMoxuneArrayObject extends ArrayObject
{
public function &offsetGet($index)
{
$var =& $this[$index];
return $var;
}
}
Implementing a Decorator on the other hand works like a charm:
class MoxuneArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable
{
private $_oArrayObject; // Decorated ArrayObject instance
public function __construct($mInput=null, $iFlags=0, $sIteratorClass='')
{
if($mInput === null)
$mInput = array();
if($sIteratorClass === '')
$this->_oArrayObject = new ArrayObject($mInput, $iFlags);
else
$this->_oArrayObject = new ArrayObject($mInput, $iFlags, $sIteratorClass);
}
// -----------------------------------------
// override offsetGet to return by reference
// -----------------------------------------
public function &offsetGet($index)
{
$var =& $this->_oArrayObject[$index];
return $var;
}
// ------------------------------------------------------------
// everything else is passed through to the wrapped ArrayObject
// ------------------------------------------------------------
public function append($value)
{
return $this->_oArrayObject->append($value);
}
public function asort()
{
return $this->_oArrayObject->asort();
}
public function count()
{
return $this->_oArrayObject->count();
}
public function exchangeArray($mInput)
{
return $this->_oArrayObject->exchangeArray($mInput);
}
public function getArrayCopy()
{
return $this->_oArrayObject->getArrayCopy();
}
public function getFlags()
{
return $this->_oArrayObject->getFlags();
}
public function getIterator()
{
return $this->_oArrayObject->getIterator();
}
public function getIteratorClass()
{
return $this->_oArrayObject->getIteratorClass();
}
public function ksort()
{
return $this->_oArrayObject->ksort();
}
public function natcassesort()
{
return $this->_oArrayObject->natcassesort();
}
public function offsetExists($index)
{
return $this->_oArrayObject->offsetExists($index);
}
public function offsetSet($index, $value)
{
return $this->_oArrayObject->offsetSet($index, $value);
}
public function offsetUnset($index)
{
return $this->_oArrayObject->offsetUnset($index);
}
public function serialize()
{
return $this->_oArrayObject->serialize();
}
public function setFlags($iFlags)
{
return $this->_oArrayObject->setFlags($iFlags);
}
public function setIteratorClass($iterator_class)
{
return $this->_oArrayObject->setIteratorClass($iterator_class);
}
public function uasort($cmp_function)
{
return $this->_oArrayObject->uasort($cmp_function);
}
public function uksort($cmp_function)
{
return $this->_oArrayObject->uksort($cmp_function);
}
public function unserialize($serialized)
{
return $this->_oArrayObject->unserialize($serialized);
}
}
Now this code works as desired:
$a = new MoxuneArrayObject();
$a['b'] = array('c' => array('d'));
unset($a['b']['c']);
var_dump($a);
Still have to modify some code though..; I don't see any way round that.
It seems to me that the "overloaded" bracket operator of ArrayObject is returning a copy of the nested array, and not a reference to the original. Thus, when you call $a['b'], you are getting a copy of the internal array that ArrayObject is using to store the data. Further resolving it to $a['b']['c'] is just giving you the element "c" inside a copy, so calling unset() on it is not unsetting the element "c" in the original.
ArrayObject implements the ArrayAccess interface, which is what actually allows the bracket operator to work on an object. The documentation for ArrayAccess::offsetGet indicates that, as of PHP 5.3.4, references to the original data in ArrayObject's internal array can be acquired using the =& operator, as quickshiftin indicated in his example.
You can use unset($a->b['c']); instead of unset($a['b']['c']); in case if there won't be a huge problem to do a such replacement for all same situations within your project
I seem to have a partial solution. unset seems to work if all the nested arrays are instances of ArrayObject. In order to ensure all the nested arrays are ArrayObjects as well, we can derive instead from this class:
class ArrayWrapper extends ArrayObject {
public function __construct($input=array(), $flags=ArrayObject::STD_PROP_LIST, $iterator_class='ArrayIterator') {
foreach($input as $key=>$value) {
if(is_array($value)) {
$input[$key] = new self($value, $flags, $iterator_class);
}
}
parent::__construct($input, $flags, $iterator_class);
}
public function offsetSet($offset, $value) {
parent::offsetSet($offset, is_array($value) ? new ArrayWrapper($value) : $value);
}
}
(updated for recursiveness; untested)
And then whenever you try to add a nested array, it will automatically get converted to an ArrayWrapper instead.
Unfortunately many of the other array functions, such as array_key_exists don't work on ArrayObjects.

What is the best method to merge two PHP objects?

We have two PHP5 objects and would like to merge the content of one into the second. There are no notion of subclasses between them so the solutions described in the following topic cannot apply.
How do you copy a PHP object into a different object type
//We have this:
$objectA->a;
$objectA->b;
$objectB->c;
$objectB->d;
//We want the easiest way to get:
$objectC->a;
$objectC->b;
$objectC->c;
$objectC->d;
Remarks:
These are objects, not classes.
The objects contain quite a lot of fields so a foreach would be quite slow.
So far we consider transforming objects A and B into arrays then merging them using array_merge() before re-transforming into an object but we can't say we are proud if this.
If your objects only contain fields (no methods), this works:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
This actually also works when objects have methods. (tested with PHP 5.3 and 5.6)
foreach($objectA as $k => $v) $objectB->$k = $v;
You could create another object that dispatches calls to magic methods to the underlying objects. Here's how you'd handle __get, but to get it working fully you'd have to override all the relevant magic methods. You'll probably find syntax errors since I just entered it off the top of my head.
class Compositor {
private $obj_a;
private $obj_b;
public function __construct($obj_a, $obj_b) {
$this->obj_a = $obj_a;
$this->obj_b = $obj_b;
}
public function __get($attrib_name) {
if ($this->obj_a->$attrib_name) {
return $this->obj_a->$attrib_name;
} else {
return $this->obj_b->$attrib_name;
}
}
}
Good luck.
I understand that using the generic objects [stdClass()] and casting them as arrays answers the question, but I thought the Compositor was a great answer. Yet I felt it could use some feature enhancements and might be useful for someone else.
Features:
Specify reference or clone
Specify first or last entry to take precedence
Multiple (more than two) object merging with syntax similarity to array_merge
Method linking: $obj->f1()->f2()->f3()...
Dynamic composites: $obj->merge(...) /* work here */ $obj->merge(...)
Code:
class Compositor {
protected $composite = array();
protected $use_reference;
protected $first_precedence;
/**
* __construct, Constructor
*
* Used to set options.
*
* #param bool $use_reference whether to use a reference (TRUE) or to copy the object (FALSE) [default]
* #param bool $first_precedence whether the first entry takes precedence (TRUE) or last entry takes precedence (FALSE) [default]
*/
public function __construct($use_reference = FALSE, $first_precedence = FALSE) {
// Use a reference
$this->use_reference = $use_reference === TRUE ? TRUE : FALSE;
$this->first_precedence = $first_precedence === TRUE ? TRUE : FALSE;
}
/**
* Merge, used to merge multiple objects stored in an array
*
* This is used to *start* the merge or to merge an array of objects.
* It is not needed to start the merge, but visually is nice.
*
* #param object[]|object $objects array of objects to merge or a single object
* #return object the instance to enable linking
*/
public function & merge() {
$objects = func_get_args();
// Each object
foreach($objects as &$object) $this->with($object);
// Garbage collection
unset($object);
// Return $this instance
return $this;
}
/**
* With, used to merge a singluar object
*
* Used to add an object to the composition
*
* #param object $object an object to merge
* #return object the instance to enable linking
*/
public function & with(&$object) {
// An object
if(is_object($object)) {
// Reference
if($this->use_reference) {
if($this->first_precedence) array_push($this->composite, $object);
else array_unshift($this->composite, $object);
}
// Clone
else {
if($this->first_precedence) array_push($this->composite, clone $object);
else array_unshift($this->composite, clone $object);
}
}
// Return $this instance
return $this;
}
/**
* __get, retrieves the psudo merged object
*
* #param string $name name of the variable in the object
* #return mixed returns a reference to the requested variable
*
*/
public function & __get($name) {
$return = NULL;
foreach($this->composite as &$object) {
if(isset($object->$name)) {
$return =& $object->$name;
break;
}
}
// Garbage collection
unset($object);
return $return;
}
}
Usage:
$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);
Example:
$obj1 = new stdClass();
$obj1->a = 'obj1:a';
$obj1->b = 'obj1:b';
$obj1->c = 'obj1:c';
$obj2 = new stdClass();
$obj2->a = 'obj2:a';
$obj2->b = 'obj2:b';
$obj2->d = 'obj2:d';
$obj3 = new Compositor();
$obj3->merge($obj1, $obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj2:a, obj2:b, obj1:c, obj2:d
$obj1->c;
$obj3 = new Compositor(TRUE);
$obj3->merge($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, obj1:c, obj2:d
$obj1->c = 'obj1:c';
$obj3 = new Compositor(FALSE, TRUE);
$obj3->with($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, #obj1:c, obj2:d
$obj1->c = 'obj1:c';
A very simple solution considering you have object A and B:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
That's all. You now have objA with all values from objB.
The \ArrayObject class has the possibility to exchange the current array to disconnect the original reference. To do so, it comes with two handy methods: exchangeArray() and getArrayCopy(). The rest is plain simple array_merge() of the provided object with the ArrayObjects public properties:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
The usage is as easy as this:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
a solution To preserve,both methods and properties from merged onjects is to create a combinator class that can
take any number of objects on __construct
access any method using __call
accsess any property using __get
class combinator{
function __construct(){
$this->melt = array_reverse(func_get_args());
// array_reverse is to replicate natural overide
}
public function __call($method,$args){
forEach($this->melt as $o){
if(method_exists($o, $method)){
return call_user_func_array([$o,$method], $args);
//return $o->$method($args);
}
}
}
public function __get($prop){
foreach($this->melt as $o){
if(isset($o->$prop))return $o->$prop;
}
return 'undefined';
}
}
simple use
class c1{
public $pc1='pc1';
function mc1($a,$b){echo __METHOD__." ".($a+$b);}
}
class c2{
public $pc2='pc2';
function mc2(){echo __CLASS__." ".__METHOD__;}
}
$comb=new combinator(new c1, new c2);
$comb->mc1(1,2);
$comb->non_existing_method(); // silent
echo $comb->pc2;
I would go with linking the second object into a property of the first object. If the second object is the result of a function or method, use references. Ex:
//Not the result of a method
$obj1->extra = new Class2();
//The result of a method, for instance a factory class
$obj1->extra =& Factory::getInstance('Class2');
To merge any number of raw objects
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array('array_merge', $objects);
}
This snippet of code will recursively convert that data to a single type (array or object) without the nested foreach loops. Hope it helps someone!
Once an Object is in array format you can use array_merge and convert back to Object if you need to.
abstract class Util {
public static function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__METHOD__, $d) : $d;
}
public static function array_to_object($d) {
return is_array($d) ? (object) array_map(__METHOD__, $d) : $d;
}
}
Procedural way
function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__FUNCTION__, $d) : $d;
}
function array_to_object($d) {
return is_array($d) ? (object) array_map(__FUNCTION__, $d) : $d;
}
All credit goes to: Jason Oakley
Here is a function that will flatten an object or array. Use this only if you are sure your keys are unique. If you have keys with the same name they will be overwritten. You will need to place this in a class and replace "Functions" with the name of your class. Enjoy...
function flatten($array, $preserve_keys=1, &$out = array(), $isobject=0) {
# Flatten a multidimensional array to one dimension, optionally preserving keys.
#
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion
# $isobject - is internally set in order to remember if we're using an object or array
if(is_array($array) || $isobject==1)
foreach($array as $key => $child)
if(is_array($child))
$out = Functions::flatten($child, $preserve_keys, $out, 1); // replace "Functions" with the name of your class
elseif($preserve_keys + is_string($key) > 1)
$out[$key] = $child;
else
$out[] = $child;
if(is_object($array) || $isobject==2)
if(!is_object($out)){$out = new stdClass();}
foreach($array as $key => $child)
if(is_object($child))
$out = Functions::flatten($child, $preserve_keys, $out, 2); // replace "Functions" with the name of your class
elseif($preserve_keys + is_string($key) > 1)
$out->$key = $child;
else
$out = $child;
return $out;
}
Let's keep it simple!
function copy_properties($from, $to, $fields = null) {
// copies properties/elements (overwrites duplicates)
// can take arrays or objects
// if fields is set (an array), will only copy keys listed in that array
// returns $to with the added/replaced properties/keys
$from_array = is_array($from) ? $from : get_object_vars($from);
foreach($from_array as $key => $val) {
if(!is_array($fields) or in_array($key, $fields)) {
if(is_object($to)) {
$to->$key = $val;
} else {
$to[$key] = $val;
}
}
}
return($to);
}
If that doesn't answer your question, it will surely help towards the answer.
Credit for the code above goes to myself :)

Categories