PHP 7.1 added the new is_iterable function to check whether a variable can be iterated. But although you may iterate over an objects properties is_iterable returns false for them.
This seems to be intentional, as it is documented and I couldn't find any PHP bugs regarding the issue but I wonder why this was done?
<?php
$a = new stdClass();
$a->foo = 'bar';
var_dump(is_iterable($a));
foreach ($a as $key => $value) {
var_dump($key, $value);
}
Outputs
boolean false
string 'foo' (length=3)
string 'bar' (length=3)
But I would expect it to output
boolean true
string 'foo' (length=3)
string 'bar' (length=3)
Its a question of definition, and "why is it done" is a tricky question, probably better for the PHP devs.
But some information: You can foreach over an object, and it will show the visible properies according to the manual
PHP 5 provides a way for objects to be defined so it is possible to iterate through a list of items, with, for example a foreach statement. By default, all visible properties will be used for the iteration.
The is_iterable is checking for specific things, see the manual
Verify that the contents of a variable is accepted by the iterable
pseudo-type, i.e. that it is an array or an object implementing Traversable
An object does not implement Traversable if you don't do so yourself (and is not one of the 'other' things, like an array) so it is not "iterable" in that sence.
Long story short: foreach can iterate over anything that is iterable, but not everything that foreach can iterate over is called iterable. Its a chair-has-4-legs vs everything-with-4-legs-is-a-chair kinda issue.
I would see the fact that you can iterate over an object without explicitly implementing iterable is a sort of exception, and leave it at that. The only other anwer would be "they did it because".
So I have this slightly annoying issue: say I have a foreach loop like this:
foreach ($arr as $key=>$value) {
do_something($key);
}
In my eclipse environment, I have turned on the feature that displays warnings for unused variables, which is really helpful. However, it complains for all such occurences, where the $value is not used in the loop.
I was wondering if there is some syntax where I don't use this, like is available for list() :
list(,,$my_var) = some_func();
//these returns an array with 3 elements, but I only need the last one
Note: The obvious would be to use array_keys(), but I don't want a function call; I'm merely asking if there's a shorthand I don't know of, or something like it. This is why the question PHP foreach that returns keys only does not cover what I'm asking.
TBH, I couldn't find any resource to back this answer, it works fine as far as my tests went, BUT I CAN'T SAY FOR SURE WHETHER THIS IS OR ISN'T RECOMMENDED TO USE. (Probably not)
Here is what I came up with:
$arr = array('kN1' => '50', 'kN2' => 400);
//$arr = array('50', 400);
foreach ($arr as $var => $var) { // use same variable for both key and value
print_r($var);
echo '<br>';
}
// kN1
// kN2
Run Viper
To get rid of the warning without introducing too much overhead just unset the unused variable once the loop is done.
foreach ($arr as $key => &$val) {
print_r($key);
}
unset($val);
BTW: I believe one should use a reference to the unused variable (&$val instead of $val). Otherwise you might end up producing a full copy of the variable with each iteration and that might be a costly operation.
I have created a php class that implements the Iterator interface. I am successfully able to iterate through the class like this:
foreach($data as $key=>$value)
echo $key;
This, however, consistently gives me a strange result:
foreach($data as $item)
echo key($item)
The first style calls the key() method in my class for every element. The latter never calls it.
Am I missing something? That should work, shouldn't it?
Update: I think I am missing something. The key function doesn't work as I would expect it to for a simple array, either:
$test = [['name'=>'foo'],['name'=>'bar'],['name'=>'fizz']];
foreach($test as $key=>$item)
echo $key;
foreach ($test as $item)
echo key($item);
gives me 012namenamename... not quite what I was expecting. I think I need to just use the $key=>value notation, and never count on key() in a loop.
Your first version is correct, which is why it works. For a short explanation of why your second method doesn't work, see this comment in the manual.
This is because (from the manual for foreach() )
On each iteration, the value of the current element is assigned to $value and the internal array pointer is advanced by one
So your second method is fetching the next key, not the current one.
I have some data represented as multi-dimensional arrays. I would like to embed these data into my OO application and provide extra functionality to these data but also a way to access it both with the traditional random access and using an iterator (so I could use foreach). In other words I would like to implement a wrapping class for a multi-dimensional array.
What I already tried:
I. ArrayAccess
The problem with this one is that the server uses PHP 5.2.17 and &offsetGet() gives an error, thus I can't return by reference, which means I can't implement multidimensional element access:
$obj[$key1][$key2] = $value;
Note, that upgrading to a newer PHP version is currently not an option
II. Using magic methods
This is a bit trickier, but my problems rose when I tried using a variable as key. E.g.
$obj->$key1[$key2] = $value;
The interpreter first evaluated $key1[$key2] throwing a notice and returning the first char of $key1, and uses that as key on my array. I don't know how to solve this one either. I thought of using brackets to force operation priority, but that had the same problem as in my next attempt:
III. Using simple get and set functions
Again, old PHP. It cries when I try to write:
$obj->get($key1)[$key2] = $value;
Because it doesn't know how to index an expression ending in round brackets.
There's still the lost resort option: make the array public and forget OO all together. But before I go there I want to be certain that there's just really no other way of doing this.
Sometimes, it's best shown with an example. For instance, you could have a multidimensional array with ordered quantities of a product, where the first index defines a customer, the second an order and the third the products. The value then is the quantity.
IV. Using simple get and set functions, but with multiple parameters:
$value = $obj->get($key1, $key2);
$obj->set($key1, $key2, $value);
or
$quantity = $orderedQuantities($customerName, $orderNo, $productCode);
The essence of writing an object wrapper, is that it looks like an object and behaves like such. If you implement it so that it looks and behaves like an array, you may just as well use an array. The only reason to wrap an array in an object that behaves like an array is that you can pass it around by reference, without using & parameters.
If that is your goal, just assign the array to $obj, and use $obj->arrayprop[$key1][$key2]. If on the other hand you want it to behave like an object, skip the array syntax altogether.
V. Using nested objects
So using the same example, you can write a customers object (kind of a collection), which embeds order objects, etc..
$quantity = $customers->getCustomerByName($customerName)->getOrder($orderNo)->getProduct($productCode)->quantity;
or, to make it more readable:
$customer = $customers->getCustomerByName($customerName);
$order = $customer->getOrder($orderNo);
$orderLine= $order->getOrderLine($productCode);
$quantity = $product->quantity;
Now, each of these entities are represented by an object. The advantage is that you can easily extend this structure with extra properties. You can give the customer collection 'findByCustomerNo' and 'findByName' methods to find a specific customer, but also a 'filterByPostalCode', which could return a new collection containing only the customers from that postal code.
$order could now contain functions to calculate totals.
$orderLine may contain only a productCode (key) and a quantity, but by making it an object, you can easily add discount, a customized name or description or other properties.
All in all, this structure allows a lot of flexibility, although it needs a little more typing at first and a little getting used to.
Using nested objects can make ArrayAccess work, without passing references. But you can forget about built in array functions completely.
<?php
class ArrObj implements ArrayAccess {
private $arr;
public function __construct($arr = null) {
$this->arr = $arr;
}
public function offsetExists($key) {
return isset($this->arr[$key]);
}
public function offsetGet($key) {
return $this->arr[$key];
}
public function offsetSet($key, $val) {
return $this->arr[$key] = $val;
}
public function offsetUnset($key) {
unset($this->arr[$key]);
}
}
$a = new ArrObj(array(
'foo' => new ArrObj(array(
'bar' => 'qwe'
))
));
echo $a['foo']['bar'] . '<br />';
$a['foo']['bar'] = 'asd';
echo $a['foo']['bar'] . '<br />';
?>
Outputs:
qwe
asd
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.