The ArrayObject class

(PHP 5, PHP 7)

简介

This class allows objects to work as arrays.

类摘要

ArrayObject implements IteratorAggregate , ArrayAccess , Serializable , Countable {
/* 常量 */
const integer STD_PROP_LIST = 1 ;
const integer ARRAY_AS_PROPS = 2 ;
/* 方法 */
public __construct ([ mixed $input = array() [, int $flags = 0 [, string $iterator_class = "ArrayIterator" ]]] )
public append ( mixed $value ) : void
public asort ( void ) : void
public count ( void ) : int
public exchangeArray ( mixed $input ) : array
public getArrayCopy ( void ) : array
public getFlags ( void ) : int
public getIterator ( void ) : ArrayIterator
public getIteratorClass ( void ) : string
public ksort ( void ) : void
public natcasesort ( void ) : void
public natsort ( void ) : void
public offsetExists ( mixed $index ) : bool
public offsetGet ( mixed $index ) : mixed
public offsetSet ( mixed $index , mixed $newval ) : void
public offsetUnset ( mixed $index ) : void
public serialize ( void ) : string
public setFlags ( int $flags ) : void
public setIteratorClass ( string $iterator_class ) : void
public uasort ( callable $cmp_function ) : void
public uksort ( callable $cmp_function ) : void
public unserialize ( string $serialized ) : void
}

预定义常量

ArrayObject Flags

ArrayObject::STD_PROP_LIST

Properties of the object have their normal functionality when accessed as list (var_dump, foreach, etc.).

ArrayObject::ARRAY_AS_PROPS

Entries can be accessed as properties (read and write).

更新日志

版本 说明
5.3.0 Implements Serializable.

Table of Contents

User Contributed Notes

fatindeed at hotmail dot com 31-Jan-2019 07:28
class RecursiveArrayObject extends \ArrayObject
{
    public function __construct($input = array())
    {
        $data = array();
        foreach ($input as $key => $value) {
            if (is_array($value)) {
                $value = new self($value);
            }
            $data[$key] = $value;
        }
        parent::__construct($data, \ArrayObject::ARRAY_AS_PROPS);
    }
}

$company = new RecursiveArrayObject(array(
    'ceo' => array(
        'id' => 1,
        'name' => 'tony',
        'age' => 36
    ),
    'coo' => array(
        'id' => 2,
        'name' => 'matt',
        'age' => 35
    ),
    'cto' => array(
        'id' => 3,
        'name' => 'james',
        'age' => 35
    )
));

var_dump($company->cto->name); // string(5) "james"
var_dump($company['coo']['name']); // string(4) "matt"
Vuong Nguyen 08-May-2018 03:52
You can easily realise that ArrayObject can use various functions as they are in ArrayIterator to iterate an object-as-a-array. However, you need to "activate" these function (rewind, valid, next and so on...) by using getIterator() first. Actually this function inherits from Iterator Aggregate interface.

Take a look at the following basic example. The results are the same:

<?php

$array
= [1, 2, 3, 4];
$a = new ArrayObject($array);
$b = new ArrayIterator($array);

$iterator = $a->getIterator();

for(
$iterator->rewind(); $iterator->valid(); $iterator->next()){
    echo
$iterator->current()*2;
   
}

for(
$b->rewind(); $b->valid(); $b->next()){
    echo
$b->current()*2;
   
}

//Resulst are the same 2468 AND 2468
rudie 26-Feb-2018 02:08
If you want numerical ArrayObject objects to play nice with json_encode(), implement JsonSerializable:

class JsonSerializableArrayObject extends ArrayObject implements JsonSerializable {
    function jsonSerialize() {
        return $this->getArrayCopy();
    }
}

For assoc ArrayObject objects this isn't neccesary, but for numerical arrays it is, otherwise they will be formatted like

{"0":"jaap","1":"karel"}

instead of

["jaap","karel"]
php at abiusx dot com 07-Feb-2017 05:13
class MyArrayObject extends ArrayObject
{
    function __construct($input=[])
    {
        parent::__construct($input,ArrayObject::ARRAY_AS_PROPS);
    }
    function offsetSet($x,$v)
    {
        echo "offsetSet('{$x}')\n";
        return parent::offsetSet($x,$v);
    }
    function &offsetGet($x)
    {
        echo "offsetGet('{$x}')\n";
        $t=parent::offsetGet($x);
        return $t;
    }
    function __get($x)
    {
        echo "__get('{$x}')\n";
    }
    function __set($x,$v)
    {
        echo "__set('{$x}')\n";
    }
    function offsetExists($x)
    {
        echo "offsetExists('{$x}')\n";
        return parent::offsetExists($x);
    }
}
$x=new MyArrayObject;
$x->hello=5;
$x['hi']=10;
$x['hello']++;
@$x->a->b=7;
$x->a->b++;
var_dump($x);

One would expect the line "$x->a->b" to either invoke offsetGet or offsetSet, but neither will be invoked. The class is completely unaware of this property being added to it, and has no way of knowing.

The output is as follows:
offsetSet('hello')
offsetSet('hi')
offsetGet('hello')
object(MyArrayObject)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(3) {
    ["hello"]=>
    int(5)
    ["hi"]=>
    int(10)
    ["a"]=>
    object(stdClass)#2 (1) {
      ["b"]=>
      int(8)
    }
  }
}
Mahmoud Elnezamy 19-Feb-2016 01:36
<?php
class Prototype extends ArrayObject
{
    private
$___class = null;
   
    public function
__get($key)
    {
        return
$this[$key];
    }

    public function
__set($key, $value)
    {
       
$this[$key] =  $value;
    }
   
    public function
__call($key, $args)
    {
        if(
is_object($this->___class) && is_callable([$this->___class, $key])){
            return
call_user_func_array([$this->___class, $key],$args);
        }
        return
is_callable($c = $this->__get($key)) ? call_user_func_array($c, $args) : null;
    }

    public function
importObj($class$array = []){
       
$this->___class = $class;
        if(
count($array) > 0){
           
$this->import($array);
        }
        return
$this;
    }

    public function
import($input)
    {
       
$this->exchangeArray($input);
        return
$this;
    }

    public function
export()
    {
        return
$this->objectToArray($this->getArrayCopy());
    }

    public function
objectToArray ($object) {
       
$o = [];
        foreach (
$object as $key => $value) {
          
$o[$key] = is_object($value) ? (array) $value: $value;
        }
        return
$o;
    }

}

class
user{
    public
$name = 'Mahmoud Elnezamy';
    public function
getName(){
        return
'You Name is ' . $this->name;
    }
}

//usage you can import object with some array

$add = ['age' => '27', 'country' => 'Egypt'];
$user = new user;
$Prototype = new Prototype;
$Prototype->importObj($user, $add);
//print_r($Prototype);

echo $Prototype->getName().' ';
echo
$Prototype->age.' ';
echo
$Prototype->country;
al.ahmed 26-Jun-2015 02:34
I did some benchmarking.... ArrayObject is super fast.  It is almost identical to regular arrays when doing get access in my test (1 million iteration).  This is insane.  When make a class with ArrayAccess, performance drops is 2x or more.
Gilles A 17-Nov-2014 01:51
// Example STD_PROP_LIST and ARRAY_AS_PROP combined
<?php
$ao
= new ArrayObject();
$ao ->setFlags(ArrayObject::STD_PROP_LIST|ArrayObject::ARRAY_AS_PROPS);

$ao->prop = 'prop data';
$ao['arr'] = 'array data';

print_r($ao);

?>

// Result

ArrayObject Object
(
    [storage:ArrayObject:private] =&gt; Array
        (
            [prop] => prop data
            [arr] => array data
        )

)
rwn dot gallego at gmail dot com 11-Aug-2013 09:30
There is a better explanation about the ArrayObject flags (STD_PROP_LIST and ARRAY_AS_PROPS) right here:

http://stackoverflow.com/a/16619183/1019305

Thanks to JayTaph
mehea 06-Nov-2012 10:28
Long story short b/c arrays by default are passed by value, if you pass an array to a function, the function works on a copy of the array while the original array remains unaltered by the function.

You may cause a change to the array to be reflected in the original array by having the function return the altered array and assign it to the variable for the original array, as follows:

<?php
function my_array_modify($data) {
   
$data['b'] = 2;
    return
$data;    
}

$regularArray = array();

$regularArray['a'] = 1;
 
$regularArray = my_array_modify($regularArray);

var_dump($regularArray['b']); // 2
?>

Or, you may explicitly pass the array by reference in which case there is no need for the function to return the array since the change will have effected the original array, as follows:

<?php
function my_arrayref_modify(&$data) {
   
$data['bb'] = 22;
}
my_arrayref_modify($regularArray);
var_dump($regularArray['bb']); // 22
?>
php5 dot man at lightning dot hu 05-Jan-2012 08:24
As you know ArrayObject is not an array so you can't use the built in array functions. Here's a trick around that:

Extend the ArrayObject class with your own and implement this magic method:

<?php
   
public function __call($func, $argv)
    {
        if (!
is_callable($func) || substr($func, 0, 6) !== 'array_')
        {
            throw new
BadMethodCallException(__CLASS__.'->'.$func);
        }
        return
call_user_func_array($func, array_merge(array($this->getArrayCopy()), $argv));
    }
?>

Now you can do this with any array_* function:
<?php
$yourObject
->array_keys();
?>
- Don't forget to ommit the first parameter - it's automatic!

Note: You might want to write your own functions if you're working with large sets of data.
MarkAndrewSlade at gmail dot com 21-Aug-2011 06:05
I found the description of STD_PROP_LIST a bit vague, so I put together a simple demonstration to show its behavior:

<?php                                                    
                                                         
$a
= new ArrayObject(array(), ArrayObject::STD_PROP_LIST);
   
$a['arr'] = 'array data';                            
   
$a->prop = 'prop data';                              
$b = new ArrayObject();                                  
   
$b['arr'] = 'array data';                            
   
$b->prop = 'prop data';                              
                                                         
// ArrayObject Object                                    
// (                                                     
//      [prop] => prop data                              
// )                                                     
print_r($a);                                             
                                                         
// ArrayObject Object                                    
// (                                                     
//      [arr] => array data                              
// )                                                     
print_r($b);                                             
                                                         
?>
marijn at sensimedia dot nl 21-Jul-2011 12:34
A gotcha that is indeed mentioned in the manual, but isn't readily obvious and just cost me half an hour:

Objects implementing the Serializable interface do NOT get __sleep and __wakeup called; instead, they use serialize and unserialize methods, respectively (why, I don't know, but whatever - I'm sure there's a reason).

Hence, I was trying to serialize a database resultset in an object extending ArrayObject, and needed to fix some stuff regarding database resources on serialize. Took me a while to figure out __sleep wasn't getting called because ArrayObjects implements Serialize...

Presumably the ArrayObject internally implements the serialize/unserialize methods (in a trivial manner), hence the error wasn't apparent immediately (i.e., no fatal error was thrown) and I'd been trying to track why my objects didn't get serialized (they were of course) instead of renaming and fixing the methods.
sfinktah at php dot spamtrak dot org 17-Apr-2011 12:27
If you plan to derive your own class from ArrayObject, and  wish to maintain complete ArrayObject functionality (such as being able to cast to an array), it is necessary to use ArrayObject's own private property "storage".

Since that is impossible to do directly, you must use ArrayObject's offset{Set,Get,Exists,Unset} methods to manipulate it indirectly.

As a side benefit, this means you inherit all the iteration and other functions in complete working order.

This may sound obvious to someone who has never implemented their own ArrayObject class...  but it is far from so.

<?php

class MyArrayObject extends ArrayObject {
        static
$debugLevel = 2;

        static public function
sdprintf() {
                if (static::
$debugLevel > 1) {
                       
call_user_func_array("printf", func_get_args());
                }
        }

        public function
offsetGet($name) {
               
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                return
call_user_func_array(array(parent, __FUNCTION__), func_get_args());
        }
        public function
offsetSet($name, $value) {
               
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                return
call_user_func_array(array(parent, __FUNCTION__), func_get_args());
        }
        public function
offsetExists($name) {
               
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                return
call_user_func_array(array(parent, __FUNCTION__), func_get_args());
        }
        public function
offsetUnset($name) {
               
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
                return
call_user_func_array(array(parent, __FUNCTION__), func_get_args());
        }
}

$mao = new MyArrayObject();
$mao["name"] = "bob";
$mao["friend"] = "jane";
print_r((array)$mao);

/* Output:
 
offsetSet(name,bob)
offsetSet(friend,jane)
Array
(
    [name] => bob
    [friend] => jane
)       */
?>

If you wish to use the "Array as Properties" flag, you simply need to include this in your constructor:

<?php parent::setFlags(parent::ARRAY_AS_PROPS); ?>

This will allow you to do things such as the below example, without overriding __get or __set .

<?php
$mao
->name = "Phil";
echo
$mao["name"];   /* Outputs "Phil" */
?>
rob at tdd dot org dot uk 03-Mar-2011 04:38
I don't believe the same performance is true since PHP 5.3. Using the same fill, read_key and foreach approach on both native arrays and ArrayObjects with 10000 keys I get the following

PHP 5.2

array() fill         0.013101
array() read         0.008685
array() foreach      0.004319
ArrayObject fill     0.014136
ArrayObject read     0.010003
ArrayObject foreach  3.454612

PHP 5.3

array() fill         0.010395
array() read         0.005933
array() foreach      0.001903
ArrayObject fill     0.010598
ArrayObject read     0.006387
ArrayObject foreach  0.003451

This was the code I used for both, an array or ArrayObject is passed into each of the functions. Again PEAR::Benchmark was used to get the results.

<?php
require_once 'Benchmark/Timer.php';

define('KEYS', 10000);

function
fill(&$arr) {
    for (
$i = 1; $i <= KEYS; $i++) {
       
$arr['key-' . $i] = $i;
    }
}

function
read_key(&$arr) {
    for (
$i = 1; $i <= KEYS; $i++) {
       
$v = $arr['key-' . $i];
    }
}

function
fe(&$arr) {
    foreach (
$arr as $key => $value) {
       
$v = $value;
    }
}
?>
danbettles at yahoo dot co dot uk 23-Aug-2009 08:56
To implement array-style appending (e.g. "$object[] = 'foo';") in your own class implementing the ArrayAccess _interface_, all you need do is check if the key passed to your implementation of offsetSet() is NULL.  Something like the following.

<?php

class MyArrayObject implements ArrayAccess {

   
/**
     * @var array
     */
   
private $aValue;

   
// ...

    /**
     * @see ArrayAccess::offsetSet()
     */
   
public function offsetSet ($p_key, $p_value) {
        if (
is_null($p_key)) {
           
$this->aValue[] = $p_value;
        }
        else {
           
$this->aValue[$p_key] = $p_value;
        }
    }

   
// ...
}
?>
skrebbel at gmail dot com 18-Apr-2009 03:25
According to my benchmarks, doing foreach() on an ArrayObject is significantly slower than doing so on a vanilla array(). However, inserting keys and retrieving them is almost the same speed.

So, if performance is important, consider not using ArrayObject or descendant classes when you're iterating over its values a lot.

These are my timing results, using PEAR::Benchmark:

ArrayObject fill            0.01441502571106   
ArrayObject read_key        0.018320083618164   
ArrayObject read_foreach    2.1559031009674   

array() fill                0.012364864349365   
array() read_key            0.013092041015625   
array() read_foreach        0.011217832565308   

In all cases, 'fill()' inserts 10000 numbers at string keys, 'read_key()' reads all of those values by referencing the keys, and 'read_foreach()' does the same by walking through the array(object) with foreach().

As you can see, filling or reading from an ArrayObject by key is only 10% to 15% slower, but doing a foreach() is 200 times as costly. I am not sure what the cause of this may be.
nonproffessional at clockworkgeek dot com 09-Mar-2009 11:04
To get a primitive array type back from an ArrayObject you can use the member function exchangeArray() or more simply just cast it:

<?php
$object
= new ArrayObject();
$object[] = "Hello World!";
// $object is now an ArrayObject with one value appended.

$array = (array) $object;
// $array is now a simple array with the single value "Hello World!".
?>
deminy at deminy dot net 02-Jan-2009 01:31
Generally variable $this can't be used as an array within an object context. For example, following code piece would cause a fatal error:

<?php
class TestThis {
    public function
__set($name, $val) {
       
$this[$name] = $val;
    }

    public function
__get($name) {
        return
$this[$name];
    }
}

$obj = new TestThis();
$obj->a = 'aaa';
echo
$obj->a . "\n";
?>

But things are different when $this is used in an ArrayObject object. e.g., following code piece are valid:

<?php
class TestArrayObject extends ArrayObject {   
    public function
__set($name, $val) {
       
$this[$name] = $val;
    }

    public function
__get($name) {
        return
$this[$name];
    }
}

$obj = new TestArrayObject();
$obj->a = 'aaa';
echo
$obj->a . "\n";
?>
Venelin Vulkov 05-Nov-2008 01:09
Simple example of usage :

<?php

$array
= array('Buck','Jerry','Tomas');

$arrayObject = new ArrayObject($array);
// Add new element
$arrayObject->append('Tweety');

// We are getting the iterator of the object
$iterator = $arrayObject->getIterator();

// Simple while loop
while ($iterator->valid()) {
    echo
$iterator->current() . "\n";
   
$iterator->next();
}

/* Outputs */
Buck
Jerry
Tomas
Tweety

?>

Note that not all the public methods of this class are documented here .
( Which includes a lot sorting methods ) .

Regards
tony dot fraser at gmail dot com 09-Oct-2008 08:30
The code above will pretty much work as it is, though I have since added in some features.

Further notes:
1. Be very aware of this bug until 5.3 becomes stable.
http://bugs.php.net/bug.php?id=41528

Basically, we wrote this collection to be a java-like object cache stored in session (instead of  hitting the soap server or a DB all the time) to load objects in $_SESSION['cache']  But, this particular bug will prevent the object from working in the second page. The variable is there, but there's nothing in it.

The easy work around is to upgrade PHP to 5.3, and it works like a charm, but at the moment 5.3 happens to be in alpha mode.

2. You cannot have a PDO object in as a member variable of a class stored in the ArrayObject if you want it to pass through sessions. You can store it if it's not going into a session, but if you want to cache like we're tying to do, make SURE you $_dbHandle="" wherever you need to.

3. My earlier post was just proof of concept. We have it working now so  UserCollection extends GenericCollectionAbstract.  and GenericCollectionAbstract implements the GenericCollectionInterface. GenericCollectionObject is the object stored in the GenericCollection's $arrayObject data, and is basically just a two property object that holds the ID of the object, and the object itself.

When all is said and done this works like a charm.

page 1 ->
$_SESSION['u'] = new UserCollection();
$_SESSION['u']->getObject(1, $dbWW);
page-2->
echo $_SESSION['u']->getObject(2, $dbWW)->getProperty('email');

And of course, getProperty() is from my user class.

my UserCollecton->getObject overwrite method is as follows:

<?php
public function getObject($_id, $_dbHandle){
       
//error_log('trying to get object');
       
if (parent::objectExists($_id)){
            return
parent::getObject($_id, $_dbHandle);
           
error_log('returning object');
        }
        else{
           
//error_log('collecting new user');
           
$_tempUser = new User();
            if (
$_tempUser->populateByID($_id, $_dbHandle)){
               
parent::addObject($_id, $_tempUser);
                if (
parent::objectExists($_id)) {
                   
$_return = parent::getObject($_id);
                }
                else
$_return = "";
            }
            else {
               
$_return = "";       
            }
           
$this->dbHandle=""; //This has to be done, otherwise it's stored in the object as a private var
                                //and it will break the object if it resides in a session.
           
return $_return;
        }
?>

Shoot me an email if you have any questions.
enjoy.!
tony at tonyandcarol dot com 07-Oct-2008 07:49
My need was to create a java-like collection where I could store objects by their DB primary keys while having the standard stack capabilities of adding, retrieving, and removing objects from collection. ArrayObject didn't quite do what I need it to do so I extended it a little.

<?php
class GenericCollection extends ArrayObject{
    private
$data;
    function
__construct(){
       
$this->data = new ArrayObject();
    }
   
    function
addObject($_id, $_object){
       
$_thisItem = new CollectionObject($_id, $_object);
       
$this->data->offSetSet($_id, $_thisItem);
    }
    function
deleteObject($_id){
       
$this->data->offsetUnset($_id);
    }
    function
getObject($_id){
       
$_thisObject = $this->data->offSetGet($_id);
        return
$_thisObject->getObject();
    }
    function
printCollection() {
       
print_r($this->data);
    }
}

class
CollectionObject {
    private
$id;
    private
$object;
   
    function
__construct($_id, $_object){
       
$this->id = $_id;
       
$this->object = $_object;
    }
    function
getObject(){
        return
$this->object;
    }
    function
printObject() {
       
print_r($this);
    }
}
?>

Call it like so:

<?php
$u1
= new User/Data/Object (); //whatever, just an object.

$myCollection = new GenericCollection();
$myCollection->addObject(1, $u1);                   
print_r($myCollection->getObject(1));
?>

Now you have a simple and functional collection framework. Add methods in for specific types of sorting, we just didn't need anything other than primary key access. And you can add introspection into the collection object if you need to track what kind of an object it is.

tony@tonyandcarol.com
dave at csixty4 dot com 05-Sep-2008 08:28
If you want to use array functions on an ArrayObject, why not use iterator_to_array() to get a standard PHP array?  Do your operations on that array, then instantiate a new ArrayObject, passing it the array.

This might be a little slow on large ArrayObjects, but you'd have access to all of the array functions.
Anonymous 09-Aug-2008 06:17
Too bad the Array functions [1] are not available on this object... otherwise I would be using it all the time.

[1] http://nl.php.net/manual/en/ref.array.php