Using foreach over an object implementing ArrayAccess and Iterator - php

Is there a way to iterate over an object's keys implementing ArrayAccess and Iterator interfaces? Array access works as a charm but I can't use foreach on those objects which would help me a lot. Is it possible? I have such code so far:
<?php
class IteratorTest implements ArrayAccess, Iterator {
private $pointer = 0;
public function offsetExists($index) {
return isset($this->objects[$index]);
}
public function offsetGet($index) {
return $this->objects[$index];
}
public function offsetSet($index, $newValue) {
$this->objects[$index] = $newValue;
}
public function offsetUnset($index) {
unset($this->objects[$index]);
}
public function key() {
return $this->pointer;
}
public function current() {
return $this->objects[$this -> pointer];
}
public function next() {
$this->pointer++;
}
public function rewind() {
$this->pointer = 0;
}
public function seek($position) {
$this->pointer = $position;
}
public function valid() {
return isset($this->objects[$this -> pointer]);
}
}
$it = new IteratorTest();
$it['one'] = 1;
$it['two'] = 2;
foreach ($it as $k => $v) {
echo "$k: $v\n";
}
// expected result:
// one: 1
// two: 2
Thanks for any help and hints.

I use this to implement iterator. Maybe you can adapt to your code ;)
class ModelList implements Iterator{
public $list;
private $index = 0;
public $nb;
public $nbTotal;
/**
* list navigation
*/
public function rewind(){$this->index = 0;}
public function current(){$k = array_keys($this->list);$var = $this->list[$k[$this->index]];return $var;}
public function key(){$k = array_keys($this->list);$var = $k[$this->index];return $var;}
public function next(){$k = array_keys($this->list);if (isset($k[++$this->index])) {$var = $this->list[$k[$this->index]];return $var;} else {return false;}}
public function valid(){$k = array_keys($this->list);$var = isset($k[$this->index]);return $var;}
/**
*
* Constructor
*/
public function __construct() {
$this->list = array();
$this->nb = 0;
$this->nbTotal = 0;
return $this;
}
}

while ($it->valid()) {
echo $it->key().' '.$it->current();
$it->next();
}
Would be my approach, however, this function looks iffy:
public function next() {
$this->pointer++;
}
Incrementing 'one' isn't likely to give you 'two'. Try the code in the answers to this question to get the next array key:
$keys = array_keys($this->objects);
$position = array_search($this->key(), $keys);
if (isset($keys[$position + 1])) {
$this->pointer = $keys[$position + 1];
} else {
$this->pointer = false;
}

Related

PHP - Json is empty after converting object

So basically I want to have an Object Class with a bunch of setters that I can manipulate and then convert straight to JSON. In the past I was using an already existing JSON as model(json->object->json). Now I want to just have object->json.
But at the moment I get an empty array when I try to use json_encode.
Also had some trouble with my $index that I'm using because I can have multiple numerical indexes inside "items"
PHP:
<?php
namespace ProjectApi\Models;
use ArrayObject;
use stdClass;
class MagentoInvoice
{
public $capture = true;
public $notify = true;
public function __construct()
{
}
public function setCapture($capture)
{
$this->capture = $capture;
}
public function getCapture()
{
return $this->capture;
}
public function setNotify($notify)
{
$this->notify = $notify;
}
public function getNotify()
{
return $this->notify;
}
public function setItems($itemsCount)
{
$i = 0;
while($i < $itemsCount)
{
$this->items = new stdClass();
$i++;
}
}
public function getItems()
{
return $this->items;
}
public function setProductQty($index, $qty)
{
$this->items->$index->qty = $qty;
}
public function getProductQty($index)
{
return $this->items->$index->qty;
}
public function setProductOrderItemId($index, $id)
{
$this->items->$index->order_item_id = $id;
}
public function getProductOrderItemId($index)
{
return $this->items->$index->order_item_id;
}
}
JSON
{"capture":true,"items":[{"order_item_id":123,"qty":1},{"order_item_id":321,"qty":1}],"notify":true}
$body = new MagentoInvoice();
$body->setCapture(true);
$body->setNotify(true);
if($firstProductId != null && $secondProductId != null)
{
$body->setItems(2);
$body->setProductQty(0, 1);
$body->setProductOrderItemId(0, $firstProductId);
$body->setProductQty(1, 1);
$body->setProductOrderItemId(1, $secondProductId);
}
print_r(json_encode($body));

PHP 7 Arrays - Detection of property of an element of a 2D array

I want my PHP IDE (NuSphere PhpEd) to detect a property of my 2D array element ( an object ) whose property is not showing up after I type a right arrow in my IDE.
Is there any way in PHP 7 to auto generate suggestions of a multidimensional array element properties where each element is an object with certain properties?
<?php
class Cell
{
private $color;
public function __construct()
{
$this->color = "red";
}
public function __get($propertyName)
{
if ($propertyName == 'color')
return $this->color;
}
public function __set($propertyName, $value)
{
if ($propertyName == 'color')
$this->color = $value;
}
}
class Matrix implements ArrayAccess
{
private $matrix = array();
public function __construct($maxX, $maxY)
{
$this->matrix = array_fill(1, $maxX, array_fill(1, $maxY, null));
}
public function &offsetGet($name)
{
return $this->matrix[$name];
}
public function offsetSet($name, $value)
{
$this->matrix[$name] = $value;
}
public function offsetExists($name)
{
return isset($this->matrix[$name]);
}
public function offsetUnset($name)
{
unset($this->matrix[$name]);
}
}
$matrix = new Matrix(3,3);
for ($xIdx = 1; $xIdx <= 3; $xIdx++)
for ($yIdx = 1; $yIdx <= 3; $yIdx++)
$matrix[$xIdx][$yIdx] = new Cell();
$matrix[2][2]->color = "green";
echo $matrix[2][2]->color;
?>
If you're happy to use phpdoc annotations, you can use the Type[][] annotation to declare a variable as being a 2D array of Type. In the case of a class property that looks like:
/** #var Cell[][] */
private $matrix = [];
Or for the return value of a class method:
/**
* #return Cell[][]
*/
public function getMatrix() {
return $this->matrix;
}
In the case of PHPStorm, that provides this:
I tried a workaround in order to force phpdoc to pick up my property or method following the arrow.
Here is what I did.
class Matrix
{
protected $maxX;
protected $maxY;
private $matrix = array();
public function __construct($maxX, $maxY)
{
$this->maxX = $maxX;
$this->maxY = $maxY;
$this->matrix = array_fill(1, $maxX, array_fill(1, $maxY, 0));
return $this;
}
public function getMaxX()
{
return $this->maxX;
}
public function getMaxY()
{
return $this->maxY;
}
public function get($x, $y)
{
if (isset($this->matrix[$x][$y]))
return $this->matrix[$x][$y];
throw new OutOfBoundsException("Array at indices $x, $y is out of bounds!");
}
}
class Main
{
public function __construct()
{
}
public function setArrayVal($x, $y)
{
$cell = new Cell();
//Set Value in some arbitrary method in Main Class
$this->matrix($x, $y)->set($cell);
//Or if $val is public in Cell class
// $this->matrix($x, $y)->val = $cell;
}
//Method of Main Class
public function matrix($x, $y) : Cell //Note Cell here specified for type hinting
{
//Note, matrix below is a property, not method, whose type corresponds to the matrix class
return $this->matrix->set($x, $y);
}
}
class Cell
{
private $val;
public function __construct()
{
}
// Method set in Cell class
public function set($val)
{
$this->val = $val;
}
}

PHP SkipList put method always returns null

I'm trying to implement a skip list in PHP using the pseudocode from http://www.mathcs.emory.edu/~cheung/Courses/323/Syllabus/Map/skip-list-impl.html. I managed to get it working fine in java, but not in PHP. My put method is always returning null, and therefore, my get method too returns null.
I don't quite understand where I'm going wrong, so I'd appreciate any assistance!
<?php
interface SkipListEntry {
public function getPrev();
public function getNext();
public function getAbove();
public function getBelow();
public function getKey();
public function getValue();
public function setValue($v);
public function setPrev($v);
public function setNext($v);
public function setAbove($v);
public function setBelow($v);
public function hasPrev();
public function hasNext();
public function hasAbove();
public function hasBelow();
}
class SkipListNode implements SkipListEntry {
private $prev;
private $next;
private $above;
private $below;
private $key;
private $value;
public static $posInf = "+oo";
public static $negInf = "-oo";
function __construct($a, $b) {
$this->key = $a;
$this->value = $b;
}
public function getPrev(){
return $this->prev;
}
public function getNext(){
return $this->next;
}
public function getAbove(){
return $this->above;
}
public function getBelow(){
return $this->below;
}
public function getKey(){
return $this->key;
}
public function getValue(){
return $this->value;
}
public function setValue($n){
$this->value = $n;
}
public function setPrev($n) {
$this->prev = $n;
}
public function setNext($n) {
$this->next = $n;
}
public function setAbove($n) {
$this->above = $n;
}
public function setBelow($n) {
$this->below = $n;
}
public function hasPrev(){
return !is_null($this->prev);
}
public function hasNext(){
return !is_null($this->next);
}
public function hasAbove(){
return !is_null($this->above);
}
public function hasBelow(){
return !is_null($this->below);
}
}
class SkipList{
private $topLeft;
private $topRight;
private $height = 0;
private $totalEntries = 0;
private $head;
function __construct(){
$this->topLeft = new SkipListNode(SkipListNode::$negInf, null);
$this->topRight = new SkipListNode(SkipListNode::$posInf, null);
$this->topLeft->setNext($this->topRight);
$this->topRight->setPrev($this->topLeft);
$this->head = $this->topLeft;
}
public function size() {
return $this->totalEntries;
}
public function isEmpty(){
return $this->totalEntries == 0;
}
public function search($key){
$p = $this->head;
while (true) {
while (!$p->getNext()->getKey() == SkipListNode::$posInf
&& strcmp($p->getNext()->getKey(), $key) <= 0) {
$p = $p->getNext();
}
if ($p->hasBelow()){
$p = $p->getBelow();
}
else {
break;
}
}
return $p;
}
public function put($key, $value){
$searchElement = $this->search($key);
if ($key == $searchElement->getKey()){
$oldValue = $searchElement->getValue();
$searchElement->setValue($value);
return $oldValue;
}
$newEntry = new SkipListNode($key, $value);
$newEntry->setPrev($searchElement);
$newEntry->setNext($searchElement->getNext());
$searchElement->getNext()->setPrev($newEntry);
$searchElement->setNext($newEntry);
$currentHeight = 0;
for ($j = 1; $j <= $this->coinFlip(); $j ++){
if ($currentHeight >= $this->height){
$this->createAdditionalLayer();
}
while (is_null($searchElement->getAbove())){
$searchElement = $searchElement->getprev();
}
$searchElement = $searchElement->getAbove();
$aboveElement = new SkipListNode($key, null);
$aboveElement->setPrev($searchElement);
$aboveElement->setNext($searchElement->getNext());
$aboveElement->setBelow($newEntry);
$searchElement->getNext()->setPrev($aboveElement);
$searchElement->setNext($aboveElement);
$newEntry->setAbove($aboveElement);
$newEntry = $aboveElement;
$currentHeight ++;
}
$this->totalEntries ++;
return null;
}
public function get($key){
$p = $this->search($key);
if ($p->getKey() == $key){
return $p->getValue();
}
return null;
}
private function createAdditionalLayer(){
$newtopLeft = new SkipListNode(SkipListNode::$negInf, null);
$newtopRight = new SkipListNode(SkipListNode::$posInf, null);
$newtopLeft->setNext($newtopRight);
$newtopLeft->setBelow($this->head);
$newtopRight->setPrev($newtopLeft);
$this->head->setAbove($newtopLeft);
$this->head = $newtopLeft;
$this->height ++;
}
private function coinFlip(){
$total = 0;
$current = -1;
while ($current != 1){
$current = rand(0,1);
$total ++;
}
return $total;
}
}
// test
$a = new SkipList();
var_dump($a->put("a", "b"));
var_dump($a->put("a", "c")); // should return c (returns null)
var_dump($a->size()); // should return 1 (returns 2)
var_dump($a->get("a")); // should return c, (returns null)
Thank you!
I found some problems in a search function:
please change your code with this and try:
public function search($key){
$p = $this->head;
while (true) {
while ($p->getNext()->getKey() != SkipListNode::$posInf
&& strcmp($p->getNext()->getKey(), $key) == 0) {
$p = $p->getNext();
}
if ($p->hasBelow()){
$p = $p->getBelow();
}
else {
break;
}
}
return $p;
}
The result is:
var_dump($a->put("a", "b"));
var_dump($a->put("a", "c")); string 'b',
var_dump($a->size()); int 1,
var_dump($a->get("a")); string 'c'

Php: turning it into a recursive function

I have currently two classes.
the ArrayCompare class:
<?php
namespace App\Tools\RegexExtract;
class ArrayCompare
{
public function compare(Array $arrayToCompare)
{
$elementData = new ElementMetaData();
$metaData = $elementData->extract($arrayToCompare[0], [], $initial=true);
foreach ($arrayToCompare as $currentElement) {
$metaData = $elementData->extract($currentElement, $metaData);
}
return $metaData;
}
}
which uses the ElementMetaData class
<?php
/**
* A class for extracting meta data from an element.
*/
namespace App\Tools\RegexExtract;
class ElementMetaData
{
public function extract($element, $metaDataToCompare = [], $initial = false)
{
if ($initial == true) {
$this->isInteger($element) ? $returnMetaData['isInteger'] = $this->isInteger($element) : null;
$returnMetaData['length'] = $this->length($element);
}
else {
$returnMetaData=$metaDataToCompare;
if ($returnMetaData != []) {
if (isset ($returnMetaData['isInteger']) && !$this->isInteger($element)) {
unset($returnMetaData['isInteger']);
}
if (isset ($returnMetaData['length']) && $this->length($element) != $returnMetaData['length']) {
unset($returnMetaData['length']);
}
}
}
return $returnMetaData;
}
private function isInteger($element)
{
return is_int($element);
}
private function length($element)
{
return strlen($element);
}
}
the basic functionality is:
given I have an array
$arr=[1,2,3];
I want to get the "similarities" between ALL Elements. According to a an array i Predefine...so this would deliver this result:
$metaArray=['isInteger'=>true,'length'=>1];
and this would deliver just length as similarity:
$arr=[1,2,'D'];
$metaArray=['length'=>1];
While this array would deliver an empty result []
$arr=[1,2,'3D']; // result is [] since not all integers or not all of same length.
Now my solution does not use recursive functions...but I am sure it can be used somehow.
Also, I want to add more "criteria"....So "isEmailAdress", "beginswithA"....etc....and this would make my if statements a horror....so what is the best strategy/design pattern to follow here?
#deceze beat me to it by fair margin... but I'll still post my solution that works basically with the same principles.
abstract class abstractComparer
{
private $array;
private $result = true;
protected $name;
public function compareArray($array)
{
$current = null;
foreach ($array as $index => $value)
{
$this->result = $this->result && $this->compareValues($index, $current, $value);
$current = $value;
}
}
public function getResult()
{
return $this->result;
}
public function getName()
{
return $this->name;
}
public abstract function compareValues($index, $value1, $value2);
public abstract function getSuccessValue();
}
class intComparer extends abstractComparer
{
protected $name = "isInteger";
public function compareValues($index, $value1, $value2)
{
return is_int($value2);
}
public function getSuccessValue()
{
return true;
}
}
class lengthComparer extends abstractComparer
{
protected $name = "length";
protected $length = 0;
public function compareValues($index, $value1, $value2)
{
$this->length = strlen($value2);
return $index == 0 || strlen($value1) == $this->length;
}
public function getSuccessValue()
{
return $this->length;
}
}
And do the actual processing like this:
$temp = [1,2,3];
$comparers = [new intComparer(), new lengthComparer()];
$result = array();
foreach ($comparers as $comparer)
{
$comparer->compareArray($temp);
if ($comparer->getResult())
{
$result[$comparer->getName()] = $comparer->getSuccessValue();
}
}
//var_dump($result);
I don't see any need for recursion here, so I'll just make a suggestion for a design approach:
Implement each criterion as a class:
abstract class Criterion {
protected $valid = true;
abstract public function initialize($value);
abstract public function check($value);
public function isValid() {
return $this->valid;
}
}
class Length extends Criterion {
protected $length;
public function initialize($value) {
$this->length = strlen($value);
}
public function check($value) {
if ($this->length != strlen($value)) {
$this->valid = false;
}
}
}
You then make an array of all your criteria:
$criteria = [new Length, ...];
foreach ($criteria as $criterion) {
$criterion->initialize($values[0]);
}
And slowly whittle them down through your values:
foreach ($values as $value) {
foreach ($criteria as $criterion) {
$criterion->check($value);
}
}
$commonCriteria = array_filter($criteria, function (Criterion $criterion) {
return $criterion->isValid();
});

class implementing Iterators loops

I have a problem with implementing Iterator interface
Here is the code:
class User_Model_Users implements Iterator, Countable
{
protected $_count;
protected $_gateway;
protected $_resultSet;
public function __construct($results, $gateway)
{
$this->setGateway($gateway);
$this->_resultSet = $results;
}
public function setGateway(User_Model_UserGateway $gateway)
{
$this->_gateway = $gateway;
return $this;
}
public function getGateway()
{
return $this->_gateway;
}
public function count()
{
if (null === $this->_count) {
$this->_count = count($this->_resultSet);
}
return $this->_count;
}
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
$result = $this->_resultSet [$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet [$key] = $result;
}
return $result;
}
public function key()
{
return key($this->_resultSet);
}
public function next()
{
return next($this->_resultSet);
}
public function rewind()
{
return reset($this->_resultSet);
}
public function valid()
{
return (bool) $this->current();
}
}
As a $result I provide Zend_Db_Table_Rowset but it can be also other object or array.
How can I fix this code so that I works in a foreach loop?
I don't get any errors as it is an infinite loop.
Wild guess (haven't really delved into the code):
Since your implementation of valid() is
public function valid()
{
return (bool) $this->current();
}
you should make sure that current() returns false when there are no more elements
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
if ( is_null($key) ) { // could also be is_null($key)||false===$key, not sure...
return false;
}
$result = $this->_resultSet[$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet[$key] = $result;
}
return $result;
}
and btw: your function key() doesn't implement the instanceof Iterator case like current() does.
Test script:
<?php
class User_Model_Users implements Iterator, Countable
{
protected $_count;
protected $_gateway;
protected $_resultSet;
public function __construct($results, $gateway)
{
$this->setGateway($gateway);
$this->_resultSet = $results;
$this->_count = null;
}
public function setGateway(User_Model_UserGateway $gateway)
{
$this->_gateway = $gateway;
return $this;
}
public function getGateway()
{
return $this->_gateway;
}
public function count()
{
if ( is_null($this->_count) ) {
$this->_count = count($this->_resultSet);
}
return $this->_count;
}
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
if ( is_null($key) ) {
return false;
}
$result = $this->_resultSet[$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet[$key] = $result;
}
return $result;
}
public function key()
{
return key($this->_resultSet);
}
public function next()
{
return next($this->_resultSet);
}
public function rewind()
{
return reset($this->_resultSet);
}
public function valid()
{
return (bool) $this->current();
}
}
class User_Model_User {
protected $x, $y;
public function __construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
}
class User_Model_UserGateway {
protected $id;
public function __construct() {
$this->id = time();
}
public function createUser($x) {
echo "User_Model_UserGateway::createUser($x)\n";
return new User_Model_User($x, $this->id);
}
}
$a = array('a', 'b', 'c', 'd');
$users = new User_Model_Users($a, new User_Model_UserGateway);
foreach($users as $u) {
print_r($u);
}
prints
User_Model_UserGateway::createUser(a)
User_Model_User Object
(
[x:protected] => a
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(b)
User_Model_User Object
(
[x:protected] => b
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(c)
User_Model_User Object
(
[x:protected] => c
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(d)
User_Model_User Object
(
[x:protected] => d
[y:protected] => 1311167311
)

Categories