array_values() doesn't work with ArrayAccess object.
neither does array_keys()
why?
if I can access $object['key'] I should be able to do all kind of array operations
No, you've misunderstood the utility of ArrayAccess. It isn't just a sort of wrapper for an array. Yes, the standard example for implementing it uses a private $array variable whose functionality is wrapped by the class, but that isn't a particularly useful one. Often, you may as well just use an array.
One good example of ArrayAccess is when the script doesn't know what variables are available.
As a fairly silly example, imagine an object that worked with a remote server. Resources on that server can be read, updated and deleted using an API across a network. A programmer decides they want to wrap that functionality with array-like syntax, so $foo['bar'] = 'foobar' sets the bar resource on that server to foobar and echo $foo['bar'] retrieves it. The script has no way of finding out what keys or values are present without trying all possible values.
So ArrayAccess allows the use of array syntax for setting, updating, retrieving or deleting from an object with array-like syntax: no more, no less.
Another interface, Countable, allows the use of count(). You could use both interfaces on the same class. Ideally, there would be more such interfaces, perhaps including those that can do array_values or array_keys, but currently they don't exist.
ArrayAccess is very limited. It does not allow the use of native array_ functions (no existing interface does).
If you need to do more array-like operations on your object, then you are essentially creating a collection. A collection should be manipulated by its methods.
So, create an object and extend ArrayObject. This implements IteratorAggregate, Traversable, ArrayAccess, Serializable and Countable.
If you need the keys, simply add an array_keys method:
public function array_keys($search_value = null, $strict = false)
{
return call_user_func_array('array_keys', array($this->getArrayCopy(), $search_value, $strict));
}
Then you can:
foreach ($object->array_keys() as $key) {
echo $object[$key];
}
The ArrayObject/ArrayAccess allows objects to work as arrays, but they're still objects. So instead of array_keys() (which work only on arrays) you should use get_object_vars(), for example:
var_dump(array_keys(get_object_vars($ArrObj)));
or convert your ArrayObject by casting it into array by (array) $ArrObj, e.g.:
var_dump(array_keys((array)$ArrObj));
Related
Is there a way to extend the native PHP array with a __toString() function/method?
I would like to be able to do
$a = array($x, $y, $z);
echo $a;
This should neither complain with a Array to string conversion notice nor the dull output of Array but instead do whatever I implement in the __toString() method of the array.
(In the __toString I would do something like iterate over the array and call __toString() on each element and concatenate that together to a string that describes the whole array.)
(I know that this can be done through e.g. a wrapper object, that's not the question. I want to tweak PHP on a more subtle level.)
Php arrays are not objects, it's an ordered map. The most relevant documentation is maybe the one concerning Arrays
As mike said, the only way to add a method for array manipulation is using ArrayObject which is closer to array than what you can find in other languages.
But:
ArrayObject implies the use of Iterator, but it's OO.
I'm having a few problems with ArrayIterator (And, indeed, the same problem with ArrayObject).
For 99% of everything, my extended ArrayIterator behaves like an array and is working great.
Unfortunately, implode() does not like being given an ArrayIterator (or ArrayObject).
I can't spot in the docs anywhere which suggests any other classes to implement on by extended ArrayIterator, nor any other methods to override.
Can anyone suggest how to get this working? (Note: Casting to an array every time I use implode is not a solution, as I'd like this array-like object to work EXACTLY as an array, and not have the code using it to have to know/care/cast)
The easiest correct solution is to use iterator_to_array to feed implode, e.g.
$traversable = /* your iterator, ArrayObject or any other type of Traversable */
echo implode(",", iterator_to_array($traversable));
This will work as expected with anything that can be iterated with foreach.
try downcasting the array ((array) $arrayObject) : implode(",", (array) $arrayObject);
I have an object that implements the ArrayableInterface (BTW, it's from Laravel's Eloquent ORM).
This object is $articles. So naturally, I can do this:
foreach ($articles as $article)
echo $article->title . "<br/>";
But I can't do this:
shuffle($articles);
I get the shuffle() expects parameter 1 to be array, object given warning.
No, it's not a bug.
PHP 5 allows you to use foreach() to loop through objects that aren't arrays. These objects are called Iterators.
Unfortunately, the old array-based functions, like shuffle() cannot process Iterators.
The main reason for this is that an Iterator may not even be sortable -- for example, you can have iterators that read directly from a file or a URL, and read a new line of data each time the foreach() loop cycles. This clearly can't be sorted because it's read during the foreach() process.
You can convert an Iterator into an array, using the cleverly named iterator_to_array() function. However, this may be a bad idea if you don't know how much data the iterator is going to process, as you may find it uses a lot of memory.
Some iterators may provide methods within the iterator object itself for sorting or filtering the data. If so, this is a better solution than trying to sort it as an array.
If you're working with an ORM, then this implies that your Iterator object is reading data from a DB. In this case, sorting it via the DB query (ie ORDER BY or whatever methods the ORM provides to do that) would probably be a better solution than sorting the data in PHP.
I don't know what the interface does.. but nor will the shuffle function because it only recognises arrays. You'd need to do this:
$array = iterator_to_array($articles);
$shuffled = shuffle($array);
From an OOP perspective, really, your object should contain the shuffle implementation:
$articles->shuffle();
Can somebody explain clearly the fundamental differences between ArrayIterator, ArrayObject and Array in PHP in terms of functionality and operation? Thanks!
Array is a native php type. You can create one using the php language construct array(), or as of php 5.4 onwards []
ArrayObject is an object that work exactly like arrays. These can be created using new keyword
ArrayIterator is like ArrayObject but it can iterate on itself. Also created using new
Comparing Array vs (ArrayObject/ArrayIterator)
They both can be used using the php's array syntax, for eg.
$array[] = 'foo';
$object[] = 'foo';
// adds new element with the expected numeric key
$array['bar'] = 'foo';
$object['bar'] = 'foo';
// adds new element with the key "bar"
foreach($array as $value);
foreach($object as $value);
// iterating over the elements
However, they are still objects vs arrays, so you would notice the differences in
is_array($array); // true
is_array($object); // false
is_object($array); // false
is_object($object); // true
Most of the php array functions expect arrays, so using objects there would throw errors. There are many such functions. For eg.
sort($array); // works as expected
sort($object); // Warning: sort() expects parameter 1 to be array, object given in ......
Finally, objects can do what you would expect from a stdClass object, i.e. accessing public properties using the object syntax
$object->foo = 'bar'; // works
$array->foo = 'bar'; // Warning: Attempt to assign property of non-object in ....
Arrays (being the native type) are much faster than objects. On the other side, the ArrayObject & ArrayIterator classes have certain methods defined that you can use, while there is no such thing for arrays
Comparing ArrayObject vs ArrayIterator
The main difference between these 2 is in the methods the classes have.
The ArrayIterator implements Iterator interface which gives it methods related to iteration/looping over the elements. ArrayObject has a method called exchangeArray that swaps it's internal array with another one. Implementing a similar thing in ArrayIterator would mean either creating a new object or looping through the keys & unseting all of them one by one & then setting the elements from new array one-by-one.
Next, since the ArrayObject cannot be iterated, when you use it in foreach it creates an ArrayIterator object internally(same as arrays). This means php creates a copy of the original data & there are now 2 objects with same contents. This will prove to be inefficient for large arrays. However, you can specify which class to use for iterator, so you can have custom iterators in your code.
ArrayObject and array are somewhat alike. Merely a collection of objects (or native types). They have some different methods that you can call, but it mostly boils down to the same thing.
However, an Iterator is something else completely. The iterator design pattern is a way to secure your array (making it only readable). Lets take the next example:
You have a class that has an array. You can add items to that array by using addSomethingToMyArray. Note however, that we do something to item before we actually add it to the array. This could be anything, but lets for a moment act like it is very important that this method is fired for EVERY item that we want to add to the array.
class A
{
private $myArray;
public function returnMyArray()
{
return $this->myArray;
}
public function addSomethingToMyArray( $item )
{
$this->doSomethingToItem( $item );
array_push( $item );
}
}
The problem with this, is that you pass the array by reference here. That means that classes that actually use returnMyArray get the real myArray object. That means that classes other than A can add things to that array, and therefor also change the array inside A without having to use the addSOmethingToMyArray. But we needed to doSOmethingToItem, remember? This is an example of a class not in control of its own inner status.
The solution to this is an iterator. Instead of passing the array, we pass the array to a new object, that can only READ things from the array. The most simple Iterator ever is something like this:
<?php
class MyIterator{
private $array;
private $index;
public function __construct( $array )
{
$this->array = $array;
}
public function hasNext()
{
return count( $this->array ) > $this->index;
}
public function next()
{
$item = $this->array[ $this->index ];
this->$index++;
return $item;
}
}
?>
As you can see, i have no way of adding new items to the given array, but i do have posibilities to read the array like this:
while( $iterator->hasNext() )
$item = $iterator->next();
Now there is again, only one way to add items to myArray in A, namely via the addSomethingToArray method. So that is what an Iterator is, it is somewhat of a shell around arrays, to provide something called encapsulation.
Array is array type, ArrayObject and ArrayIterator are built-in classes, with their instances being object type, that partially behave like arrays on syntax and usage level.
For the classes I think the quick idea can be gained from the interfaces they implement:
class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable {
class ArrayIterator implements SeekableIterator, ArrayAccess, Serializable, Countable {
Both classes implement:
ArrayAccess, so they support array syntax to access values
Countable, so they support count of values, like arrays do
Iterator (though in different ways), so they fit iterable type and can be looped over with foreach and such
One way to look at it, is that classes go through a lot of trouble to work like an array. The point is that being classes, they can be extended and customized.
They would also be inherently interoperable with any code that expects Iterator in general, so the basic use case is just that - wrapping to feed array data into iterator code.
So in a nutshell ArrayObject and ArrayIterator are Do It Yourself arrays (with some implementation differences between the two). Their instances (partially) behave like array type, but as classes they are extensible and as Iterator implementers they are interoperable with code that wants that.
Unless you need a deeply custom behavior and/or Iterator interoperability, sticking with array type is probably the way to go between these.
Notably there are also implementations of collections around, that aim to deliver the class benefits with more friendly abstractions.
array is one the eight primitive types in PHP. Allthough it comes with a lot of built-in utility functions, but they are all procedural.
Both the ArrayObject and the ArrayIterator allow us to make arrays first class citizens in an object oriented program (OOP).
Difference between ArrayObject and the ArrayIterator is that, since ArrayIterator implements SeekableIterator interface, you can do $myArray->seek(10); with ArrayIterator.
An Iterator is an object that enables a programmer to traverse a container, particularly lists. Various types of iterators are often provided via a container's interface.
There is no much difference between ArrayObject and Array as they represent the same things albeit using different object types.
ArrayIterator is an Iterator that iterates over Array-like objects, this includes all objects that implement ArrayAcess and the native Array type.
In fact, when you foreach over an array, PHP internally creates ArrayIterator to do the traversing and transform your code to look as if typed this,
for( $arrayIterator->rewind(); $arrayIterator->valid(); $arrayIterator-
>next())
{ $key = $arrayIteartor->key();
$value = $arrayIterator->current();
}
So you can see, every collection object has an Iterator except for your defined collections which you need to define your own Iterators for.
EDIT:forgot to address the normal array in my answer..
Before you read all this, if your new to php and not trying to do anything tricky just storing values to be used later, then just use the array primitive type.
$data = [1,2,3,4,5];
//or
$data = array(1,2,3,4,5);
Array is the entry point and you should get used to using first them before considering using the others. If you do not understand arrays then you will probably will not understand the benefits of using the others.
( See https://www.w3schools.com/php/php_arrays.asp for a tutorial on arrays. )
Otherwise continue reading...
I have the same question and after coming here decided to just spend some time testing them out and here is my findings and conclusions...
First lets clear up some things that were said by others that I immediately tested.
ArrayObject and ArrayItterator do not protect the data. Both can still be passed by reference in a for-each loop (see down he bottom for example).
Both return true for is_object(), both return false for is_array() and both allow direct access to the values as an array without providing protection for adding values etc. and both can be passed by reference allowing the original data to be manipulated during a foreach loop.
$array = new ArrayIterator();
var_dump(is_array($array)); // false
var_dump(is_object($array)); // true
$array[] = 'value one';
var_dump($array[0]);//string(9) "value one"
$array = new ArrayObject();
var_dump(is_array($array)); // false
var_dump(is_object($array)); // true
$array[] = 'value one';
var_dump($array[0]);//string(9) "value one"
The big difference can be seen in the functions that are available for either of them.
ArrayIteroator has all the functions required to traverse the values such as a foreach loop. ( A foreach loop will call rewind(),valid(),current(),key() methods )
see: https://www.php.net/manual/en/class.iterator.php for an excellent example of the concept (lower level class documentation).
While an ArrayObject can still be iterated over and access values the same way, it does not offer public access to pointer functions.
Object is kind of like adding a wrapper arount the ArrayItterator object and has public getIterator ( void ) : ArrayIterator that will faciliate traversing of the values inside.
You can always get the ArrayItterator From ArrayObject if your really need the added functions.
ArrayItterator is best choice if you have your own pseudo foreach loop for traversing in weird ways and want better control instead of just start to end traversal.
ArrayItterator would also be a good choice for overriding the default array behavior when iterated over by a foreach loop. eg..
//I originally made to this to solve some problem where generic conversion to XML via a foreach loop was used,
//and I had a special case where a particular API wanted each value to have the same name in the XML.
class SameKey extends ArrayIterator{
public function key()
{
return "AlwaysTheSameKey";
}
}
$extendedArrayIterator = new SameKey(['value one','value two','value three']);
$extendedArrayIterator[] = 'another item added after construct';
//according to foreach there all the keys are the same
foreach ($extendedArrayIterator as $key => $value){
echo "$key: ";//key is always the same
var_dump($value);
}
//can still be access as array with index's if you need to differentiate the values
for ($i = 0; $i < count($extendedArrayIterator); $i++){
echo "Index [$i]: ";
var_dump($extendedArrayIterator[$i]);
}
The ArrayObject might be a good choice if you have more high level complexities with itterators going on for example...
//lets pretend I have many custom classes extending ArrayIterator each with a different behavior..
$O = new ArrayObject(['val1','val2','val3']);
//and I want to change the behavior on the fly dynamically by changing the iterator class
if ($some_condition = true) $O->setIteratorClass(SameKey::class);
foreach ($O as $key => $value){
echo "$key: ";//AlwaysTheSameKey:
var_dump($value);
}
One example might be changing the output of the same data set such as having a bunch of custom iterators that will return the values in a different format when traversing the same data set. eg...
class AustralianDates extends ArrayIterator{
public function current()
{
return Date('d/m/Y',parent::current());
}
}
$O = new ArrayObject([time(),time()+37474,time()+37845678]);
//and I want to change the behaviour on the fly dynamically by changing the iterator class
if ($some_condition = true) $O->setIteratorClass(AustralianDates::class);
foreach ($O as $key => $value){
echo "$key: ";//AlwaysTheSameKey:
var_dump($value);
}
Obviously there are probably better ways to do this kind of thing.
In short these are the major major advantages differences I can see.
ArrayItorator
- lower level ability to control or extend & override traversal behaviors.
VS
ArrayObject
- one Container for data but able to change ArrayIterator class.
There are also other differences you might care to inspect but I'd imagine you wont fully grasp them all until you use them extensively.
It appears both objects can be used by reference in a foreach but NOT when using a custom itterator class via an ArrayObject..
//reference test
$I = new ArrayIterator(['mouse','tree','bike']);
foreach ($I as $key => &$value){
$value = 'dog';
}
var_dump($I);//all values in the original are now 'dog'
$O = new ArrayObject(['mouse','tree','bike']);
foreach ($O as $key => &$value){
$value = 'dog';
}
var_dump($O);//all values in the original are now 'dog'
$O->setIteratorClass(SameKey::class);
foreach ($O as $key => &$value){//PHP Fatal error: An iterator cannot be used with foreach by reference
$value = 'dog';
}
var_dump($O);
Recommendation/Conclusion
Use arrays.
If you want to do something tricky then,
I'd recommend always using ArrayIterator to start off with and only move on to ArrayObject if you want to something specific that only ArrayObject can do.
Considering the ArrayObject can make use of any custom ArrayIterators you have created along the way, id say this is the logical path to take.
Hope this helps you as much as it helped me looking into it.
Arrays
An array in PHP is actually an ordered map. A map is a type that
associates values to keys. This type is optimized for several
different uses; it can be treated as an array, list (vector), hash
table (an implementation of a map), dictionary, collection, stack,
queue, and probably more. As array values can be other arrays, trees
and multidimensional arrays are also possible.
The ArrayObject class
This class allows objects to work as arrays.
The ArrayIterator class
This iterator allows to unset and modify values and keys while
iterating over Arrays and Objects.
When you want to iterate over the same array multiple times you need
to instantiate ArrayObject and let it create ArrayIterator instances
that refer to it either by using foreach or by calling its
getIterator() method manually.
In the middle of a period of big refactoring at work, I wish to introduce stdClass ***** as a way to return data from functions and I'm trying to find non-subjective arguments to support my decision.
Are there any situations when would it be best to use one instead of the other ??
What benefits would I get to use stdClass instead of arrays ??
Some would say that functions have to be as little and specific to be able to return one single value. My decision to use stdClass is temporal, as I hope to find the right Value Objects for each process on the long run.
The usual approach is
Use objects when returning a defined data structure with fixed branches:
$person
-> name = "John"
-> surname = "Miller"
-> address = "123 Fake St"
Use arrays when returning a list:
"John Miller"
"Peter Miller"
"Josh Swanson"
"Harry Miller"
Use an array of objects when returning a list of structured information:
$person[0]
-> name = "John"
-> surname = "Miller"
-> address = "123 Fake St"
$person[1]
-> name = "Peter"
-> surname = "Miller"
-> address = "345 High St"
Objects are not suitable to hold lists of data, because you always need a key to address them. Arrays can fulfill both functions - hold arbitrary lists, and a data structure.
Therefore, you can use associative arrays over objects for the first and third examples if you want to. I'd say that's really just a question of style and preference.
#Deceze makes a number of good points on when to use an object (Validation, type checking and future methods).
Using stdClass to fulfill the same function as an array is not very useful IMHO, it just adds the overhead of an object without any real benefit. You're also missing out on many useful array functions (e.g. array_intersect). You should at least create your own class to enable type checking, or add methods to the object to make it worth using an object.
I don't think there is any reasonable advantage of using a stdClass over an array as long as your sole intention is to return multiple arbitrary datatypes from a function call.
Since you cannot technically return multiple values natively, you have to use a container that can hold all other datatypes available in PHP. That would be either an object or an array.
function fn1() { return array(1,2); }
function fn2() { return array('one' => 1, 'two' => 2); }
function fn3() { return (object) array(1,2); }
function fn4() { return (object) array('one' => 1, 'two' => 2); }
All of the above would work. The array is a tiny negligible fraction faster and less work to type. It also has a clearly defined purpose in contrast to the generic stdClass (which is a bit wishywashy, isnt it). Both only have an implicit interface, so you will have to look at the docs or the function body to know what they will contain.
If you want to use objects at any cost, you could use ArrayObject or SplFixedArray, but if you look at their APIs would you say you need their functionality for the simple task of returning random multiple values? I don't think so. Don't get me wrong though: if you want to use stdClass, then use it. It's not like it would break anything. But you also would not gain anything. To add at least some benefit, you could create a separate class named ReturnValues for this.
Could be a simple tagging class
class ReturnValues {}
or something more functional
class ReturnValues implements Countable
{
protected $values;
public function __construct() { $this->values = func_get_args(); }
public function __get($key) return $this->values[$key]; }
public function count() { return count($this->values); }
}
Granted, it doesn't do much and getting the values out of it is still done through an implict interface, but at least the class has a more clearly defined responsibility now. You could extend from this class to create ReturnValue objects for particular operations and give those an explicit interface:
class FooReturnValues extends ReturnValues
{
public function getFoo() { return $this->values['foo']; }
public function getBar() { return $this->values['foo']; }
}
Now the developer just has to look at the API to know which multiple values foo() will return. Of course, having to write concrete ReturnValue classes for each and every operation that might return multiple values could become tedious quickly. And personally, I find this overengineered for the initial purpose.
Anyway, hope that makes any sense.
Well, there are 3 differences:
they have an identity. which is why the default for passing array arguments is call by value and for objects call by sharing.
there is a semantical difference. If you use an object, anyone who reads the code understand, that the value represents the model some sort of entitity, while an array is supposed to act as a collection or a map
And last but not least, refactoring becomes signifficantly easier. If you want to use a concrete class rather than stdClass, all you have to do is to instantiate another class. Which also allows you to add methods.
greetz
back2dos
The only OBJECTIVE vote of confidence I can find is:
json_decode uses stdClass by default so us mortals in userland should use stdClass for similar situations.
I find stdClass objects over arrays useful when I need to keep my code clean and somewhat sentence-like readable. Take for example function getProperties() which returns a set of properties, say data about a person (name, age, sex). If getProperties() would return an associative array, when you want to use one of the returned properties, you would write two instructions:
$data = getProperties();
echo $data['name'];
On the other hand, if getProperties() returns a stdClass then you could write that in just one instruction:
echo getProperties()->name;
In tests of array vs stdclass they way php handles dynamic properties is slower then associative arrays. I'm not saying this to argue micro optimization but rather if your going to do this your better off defining a dataclass with no methods and set public properties. Esp if you are using php 5.4+. Under the hood defined properties are mapped directly to a c array with no hashtable where as dynamic ones need to use a hash table.
This has the added bonus of later becoming a full class without any major interface reworking.