General help about Iterators with OOP in PHP - php

I am just learning about OOP in PHP from a book and the section on Iterators and Iteration has me stumped.
For what I understand, I think in order to loop through an object's attributes, you need to implement the built-in class Iterator. Then implement the IteratorAggregate interface and create a getIterator method within. However, I am currently confused on the role each of these elements play and in what format they need to be written. In other words, I am just looking for a simply (plain-English) explanation of these concepts and a simple example. Any help would be greatly appreciated!!!!!
And thank you for your time and help in advance!!!!

The thing you want to loop over must implement Traversable. However, you can't implement Traversable directly; you have to implement one of its subtypes. How you'll do things depends on how the object will be used.
If you wanted, you could just implement Iterator. That works well enough if your type is intended to be a forward-only list of stuff already. If you decide to build an iterator, you'll have 5 methods to implement:
current, which retrieves the value at the current location;
key, which retrieves the current location's key;
next, which advances to the next location;
rewind, which resets the current location to the first; and
valid, which returns false if iteration has fallen off the end of the list of stuff being iterated over.
PHP calls those methods over the course of iteration. Specifically, let's say you have code like this:
foreach ($it as $key => $value) {
doStuffWith($key, $value);
}
This is rather equivalent to the following:
for ($it->rewind(); $it->valid(); $it->next()) {
$value = $it->current();
$key = $it->key(); // only if the foreach has a `$key =>`
doStuffWith($key, $value);
}
Basically you just need to build a type that implements Iterator and responds properly to those methods being invoked in roughly that order. Ideally it should also be possible for something to happen in between...but that's usually not an issue unless you're passing references around.
If you don't need custom iteration, you could instead just implement IteratorAggregate, and return an existing iterator type if it's able to do what you need. (For example, if you want to allow looping over an internal array, there's already an ArrayIterator made for the job. No need to roll your own.) For IteratorAggregate, you only have to implement getIterator, which returns something that's traversable. This is a better solution if you have something that can already be traversed by one of the built-in SPL iterators, or can easily be reduced to an array or something.
That same loop above, if called on an IteratorAggregate, would equate to something like
foreach ($it->getIterator() as $key => $value) {
doStuffWith($key, $value);
}
getIterator() has to return either an implementation of Iterator or some primitive traversable thingie, like an array.
As for Java-style iterators, you might build an Iterator subtype that can remember its place and loop over your collection, and then implement IteratorAggregate on your collection to return an instance of the iterator type.

For basic functionality you only really need to implement Iterator and add the relevant functionality for the rewind, valid, key, current, and next methods. See below for an example:
/**
* Awesome
*
* Do awesome stuff
*/
final class Awesome implements Iterator
{
/**
* An array of data
*
* #access private
* #var array $_data
*/
private $_data;
/**
* Store the initial data
*
* #access public
* #param array $data
*/
public function __construct(array $data = array())
{
$this->_data = $data;
}
/**
* Rewind the iterator
*
* #access public
*/
public function rewind()
{
reset($this->_data);
}
/**
* Validate the existence of the next element
*
* #access public
* #return boolean
*/
public function valid()
{
return isset($this->_data[$this->key()]);
}
/**
* Return the current key
*
* #access public
* #return integer
*/
public function key()
{
return key($this->_data);
}
/**
* Return the current value
*
* #access public
* #return mixed
*/
public function current()
{
return current($this->_data);
}
/**
* Increment the iteration index
*
* #access public
*/
public function next()
{
next($this->_data);
}
}
// Instantiate a new Awesome object
$awesome = new Awesome(array('Michael', ' ', 'Rushton', ' ', 'is', ' ', 'awesome', '!'));
// Iterate over the awesome object and output a universal truth
foreach ($awesome as $almost_awesome)
{
echo $almost_awesome;
}
If you wish to instead iterate over the object's properties simply change the __construct to:
/**
* Construct the object
*
* #access public
*/
public function __construct()
{
// Get the properties
$object_vars = get_object_vars($this);
// Unset the data reference
unset($object_vars['_data']);
// Set the data
$this->_data = $object_vars;
}

Related

PHP - Type hint for Laravel Collection or Array

I would like to create a function which accepts any traversable object as parameter, for example Laravel Collection/Array. Is there any way to type hint this condition in the function parameter??
I want the effect of both the following in single definition:
function test(array $traversable)
{
print_r($traversable);
}
AND
function test(Illuminate\Support\Collection $traversable)
{
print_r($traversable);
}
AND the DocBlock should be
/**
* Function to do something
*
* #param Collection|Array $traversable Traversable parameter
* #return null Absolutely nothing
*/
PHP 7.1 will introduce the iterable typehint which will do exactly that:
function test(iterable $items) { /*...*/ }
See PHP - rfc:iterable.
Until then you can't use any typehint if you want to accept both Traversable and array. The only thing you can do is use a proper #param annotation to document it:
/**
* #param \Traversable|array $items
*/
function test($items) { /*...*/ }

How to properly typehint SimpleXMLElement?

Is there a way to properly typehint a \SimpleXMLElement? So that I do not have to typehint all what it accesses also a a \SimpleXMLElement?
If I want to have typehinting all the way, I currently have to do it this way:
/**
* #var \SimpleXMLElement $values (this is not! an array, yet it is traversable)
*/
$values = $response->params->param->value->array->data->value;
foreach ($values as $row) {
$row = $row->array->data->value;
/**
* #var \SimpleXMLElement $row
*/
$entry = $row[0];
/**
* #var \SimpleXMLElement $entry
*/
$xmlString = $entry->asXML();
}
This seems utterly verbose and redundant. Is there a way to typehint a SimpleXMLElement so that all what it returns will also be coreclty typehinted?
If you Ctrl-click through to the "definition" of SimpleXMLElement in PHPStorm, you will see that it has a stub class definition which it uses for auto-completion and code analysis.
In older versions of PHPStorm, the overloaded -> operator was represented in that stub as follows (taken from PHPStorm 9.0):
/**
* Provides access to element's children
* #param $name child name
* #return SimpleXMLElement[]
*/
function __get($name) {}
Note that the return type here is SimpleXMLElement[], i.e. "an array of SimpleXMLElement objects". This allows it to correctly auto-complete if you write something like $node->childName[0]->grandChild[0]->asXML(), but not if you use the short-hand form of $node->childName->grandChild->asXML()
This could be classed as a bug in the IDE, and was filed in their public tracker as WI-15760, which is now fixed.
As of PHPStorm 2018.1.2, the stub instead declares the return type of __get() as SimpleXMLElement and also declares implements ArrayAccess with offsetGet() also returning SimpleXMLElement.
/**
* Provides access to element's children
* #access private Method not callable directly, stub exists for typehint only
* #param string $name child name
* #return SimpleXMLElement
*/
private function __get($name) {}
/**
* Class provides access to children by position, and attributes by name
* #access private Method not callable directly, stub exists for typehint only
* #param string|int $offset
* #return SimpleXMLElement Either a named attribute or an element from a list of children
*/
private function offsetGet ($offset) {}
This should correctly auto-complete for both explicit [0] and short-hand cases.
(The #access private is a hack to stop the method showing up in auto-complete results, since you can't actually call $node->__get() or $node->offsetGet() in real PHP code.)

In php, how can I determine equality when an object proxy is involved?

In my php application I have been comparing objects with the usual equality comparison operator, e.g.:
if ($objectA == $objectB) { ... }
Recently I implemented proxies (for objects which are expensive to load) however this means the equality operator no longer works. Is there a simple way around this? One that doesn't rely on reflection?
For the moment, I have resorted to testing the unique identifier of each object, e.g.
if ($objectA->getId() == $objectB->getId) { ... }
But this has two problems: 1) I need to refactor all existing code, and 2) in the future I may need to compare objects which are value objects (not entities).
I'm not hopeful of an easy solution since I think it would require a new magic method...
Here's my AbstractProxy class. Any help appreciated...
abstract class KOOP_Base_AbstractProxy
implements KOOP_Base_iDomain
{
use KOOP_Trait_Helper_Helper;
/**
* #var integer Object identifier
*/
protected $_id = null;
/**
* #var KOOP_Base_AbstractMapper
*/
protected $_mapper = null;
/**
* #var KOOP_Base_AbstractDomain Actual object
*/
protected $_subject = null;
/**
* Store object id for lazy loading
*
* #param integer $id Object identifier
* #param string $mapper Mapper by which to retrieve object
*/
public function __construct($id, $mapper)
{
$this->_id = $id;
$this->_mapper = $mapper;
}
/**
* Get subject
*
* #return KOOP_Base_AbstractDomain
*/
protected function getSubject()
{
if (!$this->_subject) {
$this->_subject = $this->getMapper($this->_mapper)->find($this->_id);
}
return $this->_subject;
}
/**
* Get property
*
* #param string $property
* #return mixed
*/
public function __get($property)
{
return $this->getSubject()->$property;
}
/**
* Set property
*
* #param string $property
* #param mixed $value
* #return void
*/
public function __set($property, $value)
{
$this->getSubject()->$property = $value;
}
/**
* Is property set?
*
* #param $property
* #return boolean
*/
public function __isset($property)
{
return isset($this->getSubject()->$property);
}
/**
* Unset property
*
* #param string $property
* #return mixed
*/
public function __unset($property)
{
unset($this->getSubject()->$property);
}
/**
* Call method
*
* #param string $method Method to call
* #param array $params Parameters to pass
* #return mixed
*/
public function __call($method, array $params)
{
return call_user_func_array(array($this->getSubject(), $method), $params);
}
/**
* Get id
*
* Saves having to retrieve the entire object when only the ID is required.
*/
public function getId()
{
return $this->_id;
}
}
Proxies do break object equality, and there's no utterly clean way to fix this. In a fully object oriented language you would handle this by operator overloading (which I don't recommend) or implementing a custom .equals() function (as in Java). Sadly, PHP simply does not support object orientation at this level, so you will have some decisions to make.
1) I would prefer to have your proxy class provide an equals() function which takes as input a reference to the object you want to test against and compares it to the proxied object - which shouldn't be much more 'expensive' than it was to not use a proxy at all. Example in pseudo-PHP code (my apologies if my reference syntax is off, it's been a while):
public function equals (&$toCompare)
{
if ($_subject == $toCompare)
{
return true;
}
else
{
return false;
}
}
The downside is simple: you have to refactor your code that involves this proxied object, and you have to remember that "==" does not work on this proxied object type while you are working. If you don't deal with these objects much, or if you deal with them all the time, this is fine. If you deal with them regularly but intermittently, or if others must work with them on occasion, then this will cause bugs when you/they forget about this equality problem.
2) Use an Operator Overloading extension to the language. I haven't done this, I don't know if it works, and it might be a nightmare. I include it for theoretical completeness.
Personally, I think I'd just hack it with the pseudo-Java approach call it a day, as I think it would actually work and require nothing more than using the function correctly (and remembering to use it in the first place).

PHP callback vs template

I've built a list rendering class:
class ListRenderer
{
/**
* #param int $columns number of columns
* #param string $element container element
* #param string $styleClass container style
*/
public function __construct($columns,$element='div',$styleClass=''){...}
...
/**
* #param mixed $callback function to render items - should take two
* parameters ($item,$index)
* #param array $list items to render
*/
public function renderArrayList($callback,$list){...}
/**
* #param mixed $callback function to render items - should take 3 parameters
* ($row,$i,$count) $i and $count are the position and total items
* #param string $sql query string
* #param string $errorMessage
* #param int $blanks number of blank items to render. The callback will be
* invoked with a null $row parameter for the blank records.
*/
public function renderQueryList($callback,$sql,$errorMessage,$blanks=0){...}
...
}
The callback function renders a single item.
This could also be accomplished using templates:
class ListRenderer
{
...
//$itemRenderer implements ListItemRenderer
public function renderArrayList($itemRenderer,$list){...}
//$itemRenderer implements ListItemRenderer
public function renderQueryList($itemRenderer,$sql,$errorMessage,$blanks=0){...}
...
}
template ListItemRenderer
{
public function renderArrayItem($item,$index);
public function renderQueryItem($row,$index,$count);
}
class SomeClass implements ListItemRenderer
{
...
public function renderArrayItem($item,$index){...}
public function renderQueryItem($row,$index,$count){...}
...
}
I'm not sure why I went with callbacks on this one; coming from a Java background I would normally be inclined to use the second approach.
It seems to me that:
Callbacks are more flexible
Templates would limit a single class to one renderArrayItem function, for example, where callbacks would allow use of multiple functions per class for that purpose.
The template approach requires the function to be a class member.
Callbacks tend to produce less maintainable code.
Are there any strong reasons to go one way or the other on this?
There can be multiple reasons for the one versus the other and the other way round. Especially for your case I have no clue what the difference is because I don't know your application.
So I ask back: Why one versus the other? If you still don't know which way to go, or unsure if you want the one or the other explicitly, why don't you make a callback variant you can use when needed? You can inject the callbacks when instantiating the class:
class ListItemCallbackRenderer implements ListItemRenderer
{
private $callbacks;
public function __construct(array $callbacks)
{
$this->callbacks = $callbacks;
}
public function renderArrayItem($item,$index)
{
$callback = $this->callbacks[__FUNCTION__];
// ...
}
public function renderQueryItem($row,$index,$count)
{
$callback = $this->callbacks[__FUNCTION__];
// ...
}
}
Done this, the interface stays the same which makes your overall design more fluid and you can decide which variant to use everywhere in your application. No need to degrade yourself to one method actually.

How php iterator class works ? (Iterate through zend table row set)

I have object which contains 100 records. I want to iterate through it and delete all the data in the object.
How can I do this in PHP iterator class ?
(Object is ZEND table row set object)
(Here delete means we are just making delete_flag in the database to 1. Data won't be deleted physically from the database.)
Eg:
$zendTableRowSetObject->list[0]->delete_flag = 1
$zendTableRowSetObject->list[2]->delete_flag = 1
$zendTableRowSetObject->list[3]->delete_flag = 1
$zendTableRowSetObject->save();
->save() is the Zend function, this will update the object which used to calls this method.
(Other than this any other method http://php.net/manual/en/language.oop5.iterations.php)
(Without using foreach loop is there any way to do it ?)
Give me some examples .
Here is my iterator class
class PersonListIter implements Iterator
{
protected $_PersonList;
/**
* Index of current entries
* It's used for iterator
* #var integer
*/
protected $_entryIndex = 0;
/**
* Entries data sets
* #var array
*/
protected $_entries;
/*
* Initialization of data.
*
* #params Zend_Db_Table_Rowset $list Row Object
* #return null
*/
public function __construct ( $list )
{
$this->_PersonList = $list;
$this->_entryIndex = 0;
$this->_entries = $list->getCount();
}
/*
* Iterator interface method to rewind index
* #return null
*/
public function rewind()
{
$this->_entryIndex = 0;
}
/*
* Iterator interface method to return Current entry
* #return Zend_Db_Table_Row Current Entry
*/
public function current()
{
return $this->_PersonList->getElement($this->_entryIndex);
}
/*
* Iterator interface method to return index of current entry
* #return int Current Entry Index
*/
public function key()
{
return $this->_entryIndex;
}
/*
* Iterator interface method to set the next index
* #return null
*/
public function next()
{
$this->_entryIndex += 1;
}
/*
* Iterator interface method to validate the current index
* #return enum 0/1
*/
public function valid()
{
return (0 <= $this->_entryIndex && $this->_entryIndex < $this->entries)?1:0;
}
} // class PersonListIter
$zendTableRowSetObject is the PersonList object in the iterator class
You cannot delete all of them at once, you have to iterate through (using foreach or while in conjunction with next()) to delete them.
While surfing I have found the following link which might interest you. This explains the use of implement iterator pattern in PHP in an nice way. >> http://www.fluffycat.com/PHP-Design-Patterns/Iterator/

Categories