i wrote an array wrapper class PersonArray which can contain objects of a certain type (Person). Every person has a unique getHash() function which returns the ID + Name as a unique identifier. This allows for speedy retrieval of the Person from the PersonArray. The PersonArray actually holds two internal Arrays. One for the storage of Person objects ($items), and one for the storage of the Hash values ($itemsHash).
I want to create a insertAt(index, Person) function which puts the Person object at the [index] position in the $items array. Is there a way to insertAt a certain position in an array? If so how can I also update the $itemsHash of the PersonArray?
class Person {
function getHash() {
return $this->id . $this->name;
}
}
class PersonArray implements Iterator {
public $items = array();
public $itemsHash = array();
public function Find($pKey) {
if($this->ContainsKey($pKey)) {
return $this->Item($this->internalRegisteredHashList[$pKey]);
}
}
public function Add($object) {
if($object->getHash()) {
$this->internalRegisteredHashList[$object->getHash()] = $this->Count();
array_push($this->items, $object);
}
}
public function getItems() {
return $this->items;
}
function ContainsKey($pKey) {}
function Count() {}
function Item($pKey) {}
//Iteration implementation
public function rewind() {}
public function current() {}
public function key() {}
public function next() {}
public function valid() {}
}
You may find it is faster and easier to use PHP's associative arrays rather than re-implementing them.
As an aside you can also implement the simpler IteratorAggregate if you are actually just iterating over an array.
e.g.
class PersonArray implements IteratorAggregate {
public $items = array();
public function getItems() {
return $this->items;
}
public function Add($object) {
if($object->getHash()) {
$this->items[$object->getHash()] = $object;
}
}
public function Find($pKey) {
if(isset($this->items[$pKey])) {
return $this->items[$pKey];
}
}
public function insertAt($index, $person) {
$tmp = array_slice($this->items, 0, $index);
$tmp[$person->getHash()] = $person;
$tmp = array_merge($tmp, array_slice($this->items, $index));
$this->items = $tmp;
}
//IteratorAggregate implementation
public function getIterator() {
return new ArrayIterator($this->items);
}
}
Related
I am attempting to create a priority queue class with array object in PHP. I know there is the SplPriorityQueue in PHP, but I am trying to practice object oriented programming here. Priority Queues have data and priority level, so I have a rough MyQueue class that implements these attributes. I am not sure if I am going in the right direction here. I have not worked with arrayObject's in PHP before.
public class MyQueue{
private string data;
private int priority;
myQueue = arrayObject(array(data => priority));
}
Priority queue class might look like this:
class MyQueue implements Iterator, Countable {
private $data;
public function __construct() {
$this->data = array();
}
function compare($priority1, $priority2) {}
function count() {
return count($this->data);
}
function extract() {
$result = $this->current();
$this->next();
return $result;
}
function current() {
return current($this->data). ' - ' .$this->key();
}
function key() {
return key($this->data);
}
function next() {
return next($this->data);
}
function insert($name, $priority) {
$this->data[$name] = $priority;
asort($this->data);
return $this;
}
function isEmpty() {
return empty($this->data);
}
function recoverFromCorruption() {}
function rewind() {}
function valid() {
return (null === key($this->data)) ? false : true;
}
}
Usage:
$items = new MyQueue();
$items ->insert('Charles', 8)
->insert('James', 1)
->insert('Michael', 4)
->insert('John', 2)
->insert('David', 6)
->insert('William', 5)
->insert('Robert', 3)
->insert('Richard', 7);
foreach($items as $item) {
echo $item,'<br>';
}
I've got a method that accepts an array of rules as an argument.
public function setRule($name, Array $rules) { ... }
The passed in array should only contain objects that implement the IRule interface, but since I can't type hint the content of an array I would like to know if there's maybe another way of doing it?
I would highly appreciate examples with your answers.
It is not possible in the function header, but you can do instanceof checks later on.
Example:
foreach ($rules as $r) {
if ($r instanceof IRule) {
do_something();
} else {
raise_error();
}
}
Most people now will suggest to check the Array right when you are inside the method, but better try this way;
Implement an Iterator (this is a class that can be used like an array, with foreach for example), and pass this iterator to your class:
class IRuleIterator implements Iterator {
private $var = array();
public function __construct($array) {
if (is_array($array)) {
$this->var = $array;
}
}
public function add($element) {
$this->var[] = $element;
return $this;
}
public function rewind() {
reset($this->var);
return $this;
}
public function current() {
return current($this->var);
}
public function key() {
return key($this->var);
}
public function next() {
return next($this->var);
}
public function valid() {
return ($this->current() instanceof IRule);
}
}
Then your function:
public function setRule($name, IRuleIterator $rules) { /* ... */ }
You can find a full list of those "special PHP objects" which can be implemented here: http://php.net/manual/en/book.spl.php
The ArrayIterator would be even better for your purpose. There are lots of nice things in the SPL, have a look at it :)
I'm trying to get better understanding of iterators in PHP. For this test I wanted to make a tree of items, and list them with different RecursiveIteratorIterator modes. ::SELF_FIRST and ::CHILD_FIRST modes are working as I expect them to. However, it doesn't when I want to list leaves. There must be something I'm missing in implementation which doesn't allow that mode to work properly as it prints out nothing. Is there something wrong with my Obj::hasChildren() method?
Here is the test class:
class Obj implements \RecursiveIterator {
public $children = array();
private $position;
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function valid()
{
return isset($this->children[$this->position]);
}
public function next()
{
$this->position++;
}
public function current()
{
return $this->children[$this->position];
}
public function rewind()
{
$this->position = 0;
}
public function key()
{
return $this->position;
}
public function hasChildren()
{
return !empty($this->children[$this->position]);
}
public function getChildren()
{
return $this->children[$this->position];
}
public function __toString()
{
return $this->name;
}
}
And here is the test:
use RecursiveIteratorIterator as RII;
$o1 = new Obj('Root');
$i1 = new Obj('Item 1');
$i12 = new Obj('Subitem 2');
$i1->children[] = new Obj('Subitem 1');
$i1->children[] = $i12;
$i12->children[] = new Obj('Subsubitem 1');
$i12->children[] = new Obj('Enough....');
$o1->children[] = $i1;
$o1->children[] = new Obj('Item 2');
$o1->children[] = new Obj('Item 3');
foreach (new RII($o1, RII::LEAVES_ONLY) as $o) {
echo "<br>" . $o;
}
What you assume is pointing in the right direction, there is a problem with the hasChildren() method you have. Compare it with the valid() and with the current() method, you then probably already see that it will always return true.
Because as long as there is a current(), hasChildren() returns true:
public function current()
{
return $this->children[$this->position];
}
and:
public function hasChildren()
{
return !empty($this->children[$this->position]);
}
Instead you want to test if the current element has children or not:
public function hasChildren()
{
return !empty($this->current()->children);
}
The slight difference that will give you your output:
Subitem 1
Subsubitem 1
Enough....
Item 2
Item 3
By always returning TRUE for hasChildren(), the RecursiveIteratorIterator is unable to detect any leaves. By concept of a tree, this is not possible but in a traversal process - as you demonstrated with your "bug" - clearly possible :)
See as well (if I may):
How does RecursiveIteratorIterator work in PHP?
I developed a interface and class to shield the PDOStatement.
The interface:
interface ResultSetInterface extends Iterator
{
public function count();
public function all();
}
The class:
class ResultSet implements ResultSetInterface
{
/**
* #var PDOStatement
*/
protected $pdoStatement;
protected $cursor = 0;
protected $current = null;
private $count = null;
public function __construct($pdoStatement)
{
$this->pdoStatement= $pdoStatement;
$this->count = $this->pdoStatement->rowCount();
}
public function rewind()
{
if ($this->cursor > 0) {
throw new Exception('Rewind is not possible');
}
$this->next();
}
public function valid()
{
return $this->cursor <= $this->count;
}
public function next()
{
$this->current = $this->pdoStatement->fetch();
$this->cursor++;
}
public function current()
{
return $this->current;
}
public function key()
{
}
public function count()
{
return $this->count;
}
public function all() {
$this->cursor = $this->count();
return $this->pdoStatement->fetchAll();
}
}
This works fine. But I'm not sure how to use the key() method which is necessary to implement an Iterator class. Any ideas?
First of all, about your interface, I think it would be better for you to extend CountableIterator as you want to add the count()method and there is a magical interface for that purpose in SPL.
About the key method. You have to remember in PHP every iterable content is an association of a key and a value. It is inherited from PHP arrays.
Iterator is a way to overload the foreachoperator and as foreach as a sythax which is composed as foreach($iterator as $key=>$value) you have to give the key method implementation.
In your case you have two solutions :
using the $pdo->cursor
create your own attribute called $currentKey and increment it each time you use nextmethod.
I need to serialize a proxy class. The class uses __set and __get to store values in an array. I want the serialization to look like it is just a flat object. In other words, my class looks like:
class Proxy
{
public $data = array();
public function __get($name)
{
return $data[$name]
}
}
and I want a foreach loop to return all the keys and values in $data, when I say:
foreach($myProxy as $key)
Is this possible?
class Proxy implements IteratorAggregate
{
public $data = array();
public function __get($name)
{
return $data[$name];
}
public function getIterator()
{
$o = new ArrayObject($this->data);
return $o->getIterator();
}
}
$p = new Proxy();
$p->data = array(2, 4, 6);
foreach ($p as $v)
{
echo $v;
}
Output is: 246.
See Object Iteration in the PHP docs for more details.
You want to implement the SPL iterator interface
Something like this:
class Proxy implements Iterator
{
public $data = array();
public function __get($name)
{
return $data[$name]
}
function rewind()
{
reset($this->data);
$this->valid = true;
}
function current()
{
return current($this->data)
}
function key()
{
return key($this->data)
}
function next() {
next($this->data);
}
function valid()
{
return key($this->data) !== null;
}
}