Custom foreach results for dynamic proxy class - magic methods? - php

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

Related

Creating a priority queue class in 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>';
}

Using ArrayObject to store arrays

I am trying to store an array and manipulate that array using a custom class that extends ArrayObject.
class MyArrayObject extends ArrayObject {
protected $data = array();
public function offsetGet($name) {
return $this->data[$name];
}
public function offsetSet($name, $value) {
$this->data[$name] = $value;
}
public function offsetExists($name) {
return isset($this->data[$name]);
}
public function offsetUnset($name) {
unset($this->data[$name]);
}
}
The problem is if I do this:
$foo = new MyArrayObject();
$foo['blah'] = array('name' => 'bob');
$foo['blah']['name'] = 'fred';
echo $foo['blah']['name'];
The output is bob and not fred. Is there any way I can get this to work without changing the 4 lines above?
This is a known behaviour of ArrayAccess ("PHP Notice: Indirect modification of overloaded element of MyArrayObject has no effect" ...).
http://php.net/manual/en/class.arrayaccess.php
Implement this in MyArrayObject:
public function offsetSet($offset, $data) {
if (is_array($data)) $data = new self($data);
if ($offset === null) {
$this->data[] = $data;
} else {
$this->data[$offset] = $data;
}
}

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().

PHP Is there anyway I can simulate an array operator on the return value of a function [duplicate]

This question already exists:
Closed 11 years ago.
Possible Duplicate:
Any way to access array directly after method call?
In C# and other languages, I can do something like this
$value = $obj->getArray()[0];
But not in PHP. Any workarounds or am I doomed to do this all the time?
$array = $obj->getArray();
$value = $array[0];
No, you can't do it without using array_shift (Which only gets the first element). If you want to access the third or fourth, most likely you'd want to do a function like this:
function elemnt($array, $element)
{
return $array[$element];
}
$value = element($obj->getArray(), 4);
Also, see this question, as it is an exact duplicate: Any way to access array directly after method call?
I think you are doomed to do it that way :(
You can do this:
$res = array_pop(array_slice(somefunc(1), $i, 1));
If this is a one-off or occasional thing where the situation in your example holds true, and you're retrieving the first element of the return array, you can use:
$value = array_shift($obj->getArray());
If this is a pervasive need and you often need to retrieve elements other than the first (or last, for which you can use array_pop()), then I'd arrange to have a utility function available like so:
function elementOf($array, $index = 0) {
return $array[$index];
}
$value = elementOf($obj->getArray());
$otherValue = elementOf($obj->getArray(), 2);
Well, maybe this could help you, in php spl, pretty usefull, you can make you a Special Array for you:
<?php
class OArray extends ArrayObject{
public function __set($name, $val) {
$this[$name] = $val;
}
public function __get($name) {
return $this[$name];
}
}
class O{
function __construct(){
$this->array = new OArray();
$this->array[] = 1;
$this->array[] = 2;
$this->array[] = 3;
$this->array[] = 4;
}
function getArray(){
return $this->array;
}
}
$o = new O();
var_dump( $o->getArray()->{1} );
<?php
class MyArray implements Iterator ,ArrayAccess
{
private $var = array();
//-- ArrayAccess
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->var[] = $value;
} else {
$this->var[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->var[$offset]);
}
public function offsetUnset($offset) {
unset($this->var[$offset]);
}
public function offsetGet($offset) {
return isset($this->var[$offset]) ? $this->var[$offset] : null;
}//-- Iterator
public function __construct($array){
if (is_array($array)) {
$this->var = $array;
}
}
public function rewind() {
reset($this->var);
}
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() !== false);
}
}
$values = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
$it = new MyArray($values);
foreach ($it as $a => $b) {
print "$a: $b<br>";
}
echo $it["one"]."<br>";?>

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