Creating a priority queue class in PHP - php

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>';
}

Related

Decorate an Iterator in PHP - enable skipping

I have a class that is basically a decorator for PHP's DirectoryIterator. Each file's contents are processed by the class and then returned by the current() method. Currently, when the file is a dot file or the file cannot be processed, I return false from the current() method. But I would rather like to skip the dot- and unprocessable files, and only return processed data.
P.S. The code below is a simplified example. I don't want to pre-process all files in the constructor.
class Pages implements \Iterator
{
public function __construct(string $path)
{
$this->di = new \DirectoryIterator($path);
}
public function rewind() {
return $this->di->rewind();
}
public function current() {
$file = $this->di->current();
if($file->isDot()) {
return false;
}
$content = file_get_contents($file->getPathName());
if($content === 'Cannot be processed!') {
return false;
}
return $content;
}
public function key() {
return $this->di->key();
}
public function next() {
return $this->di->next();
}
public function valid() {
return $this->di->valid();
}
}
You can greatly simplify the code of your own answer by extending the already existing abstract PHP class FilterIterator :
class DirectoryFilterIterator
extends FilterIterator
{
public function __construct(string $path) {
parent::__construct(new DirectoryIterator($path));
}
public function accept() : bool {
$current = parent::current();
// let's not only ensure it's not a dot but that it's an actual readable file
if(!$current->isFile() || !$current->isReadable()) {
return false;
}
else {
$contents = file_get_contents($current->getPathname());
if($contents === 'Some value') {
return false;
}
}
return true;
}
}
foreach(new DirectoryFilterIterator('.') as $key => $value) {
echo "$key -> $value\n";
}
As you can also see, instead of merely ensuring that the item is not a dot, I ensure the item is an actual readable file as I think that is a little more robust for your intentions.
PS: As your original intention was to create a decorator/wrapper you can of course alter the constructor of DirectoryFilterIterator to accept a DirectoryIterator instead:
public function __construct(DirectoryIterator $it) {
parent::__construct($it);
}
...and then change the instantiation to:
foreach(new DirectoryFilterIterator(new DirectoryIterator('.')) as $key => $value) {
echo "$key -> $value\n";
}
Finally I found this solution, hope someone finds it helpful. One could apply this pattern to any decorated Iterator, effectively ending up with something like a "filtered" iterator.
class FilteredIterator implements \Iterator
{
protected $currentProcessed = null;
protected $key = -1; // Keys are maintained by the decorating class
public function __construct(Iterator $iterator)
{
$this->it = $iterator;
}
// Iterator interface START -->
public function rewind() {
$this->it->rewind();
$this->forward();
}
public function current() {
return $this->currentProcessed;
}
public function key() {
return $this->key;
}
public function next() {
$this->it->next();
$this->forward();
}
public function valid() {
return $this->it->valid();
}
// <-- Iterator interface END
protected function forward() : void
{
if(!$this->it->valid()) {
// Stop when iterator is done
return;
}
// Pseudo code, insert expensive processing function here -->
$content = $this->it->current();
if($content === 5) {
// <-- Pseudo code END
// Skip
$this->next();
return;
}
// Update position
$this->key++;
// Cache successfully processed step
$this->currentProcessed = $content;
}
}
$arrayObject = new ArrayObject([1,2,3,4,5,6]);
$filteredIterator = new FilteredIterator($arrayObject->getIterator());
foreach ($filteredIterator as $key => $value) {
echo $key . ' -> ' . $value . "\n";
}
//0 -> 1
//1 -> 2
//2 -> 3
//3 -> 4
//4 -> 6

Why does a class that implements ArrayAccess, Iterator, and Countable not work with array_filter()?

I have the following class:
<?php
/*
* Abstract class that, when subclassed, allows an instance to be used as an array.
* Interfaces `Countable` and `Iterator` are necessary for functionality such as `foreach`
*/
abstract class AArray implements ArrayAccess, Iterator, Countable
{
private $container = array();
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
public function rewind() {
reset($this->container);
}
public function current() {
return current($this->container);
}
public function key() {
return key($this->container);
}
public function next() {
return next($this->container);
}
public function valid() {
return $this->current() !== false;
}
public function count() {
return count($this->container);
}
}
?>
Then, I have another class that sub-classes AArray:
<?php
require_once 'AArray.inc';
class GalleryCollection extends AArray { }
?>
When I fill a GalleryCollection instance with data and then try to use it in array_filter(), in the first argument, I get the following error:
Warning: array_filter() [function.array-filter]: The first argument should be an array in
Because array_filter only works with arrays.
Look at other options, like FilterIterator, or create an array from your object first.

PHP array_key_exists() and SPL ArrayAccess interface: not compatible?

I wrote a simple collection class so that I can store my arrays in objects:
class App_Collection implements ArrayAccess, IteratorAggregate, Countable
{
public $data = array();
public function count()
{
return count($this->data);
}
public function offsetExists($offset)
{
return (isset($this->data[$offset]));
}
public function offsetGet($offset)
{
if ($this->offsetExists($offset))
{
return $this->data[$offset];
}
return false;
}
public function offsetSet($offset, $value)
{
if ($offset)
{
$this->data[$offset] = $value;
}
else
{
$this->data[] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
public function getIterator()
{
return new ArrayIterator($this->data);
}
}
Problem: when calling array_key_exists() on this object, it always returns "false" as it seems this function is not being handled by the SPL. Is there any way around this?
Proof of concept:
$collection = new App_Collection();
$collection['foo'] = 'bar';
// EXPECTED return value: bool(true)
// REAL return value: bool(false)
var_dump(array_key_exists('foo', $collection));
This is a known issue which might be addressed in PHP6. Until then, use isset() or ArrayAccess::offsetExists().

Custom foreach results for dynamic proxy class - magic methods?

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;
}
}

PHP - Hashed array, insert at Index?

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);
}
}

Categories