/src/ListAbstract.php
https://gitlab.com/mac_doggie/list · PHP · 361 lines · 240 code · 45 blank · 76 comment · 48 complexity · 96c8c45adfcace2e9f2428e09a9c1384 MD5 · raw file
- <?php
- namespace Macdoggie\Component\Lists;
- use Macdoggie\Component\Lists\Exceptions\InvalidDataTypeException;
- use Macdoggie\Component\Lists\Exceptions\InvalidDataValueException;
- use Macdoggie\Component\Lists\Exceptions\InvalidRangeException;
- use Macdoggie\Component\Lists\Expressions\ExpressionFactory;
- abstract class ListAbstract implements ListInterface
- {
- /**
- * @var mixed[]
- */
- protected $items;
- protected $position;
- public function __construct()
- {
- $this->items = [];
- $this->position = 0;
- $this->rewind();
- }
- /**
- * @param $item
- * @param int|null $offset
- */
- protected function addItem($item, int $offset = null)
- {
- if (null == $offset) {
- $this->items[] = $item;
- } else {
- array_splice($this->items, $offset, 0, $item);
- }
- }
- /**
- * @return mixed|null
- */
- public function getFirst()
- {
- $this->rewind();
- return ($this->valid()) ? $this->current() : null;
- }
- /**
- * @return mixed|null
- */
- public function getPrevious()
- {
- $this->prev();
- return ($this->valid()) ? $this->current() : null;
- }
- /**
- * @return mixed|null
- */
- public function getNext()
- {
- $this->next();
- return ($this->valid()) ? $this->current() : null;
- }
- /**
- * @return mixed|null
- */
- public function getLast()
- {
- $this->last();
- return ($this->valid()) ? $this->current() : null;
- }
- public function rewind()
- {
- $this->position = 0;
- }
- public function prev()
- {
- --$this->position;
- }
- public function next()
- {
- ++$this->position;
- }
- public function last()
- {
- $this->position = count($this->items) - 1;
- }
- /**
- * @return mixed
- */
- public function current()
- {
- return $this->items[$this->position];
- }
- /**
- * @return int
- */
- public function key()
- {
- return $this->position;
- }
- /**
- * @return bool
- */
- public function valid()
- {
- return isset($this->items[$this->position]);
- }
- /**
- * @param int $offset
- * @param int $length
- * @throws InvalidRangeException
- */
- public function getRange(int $offset, int $length)
- {
- if ($length < 0) {
- $lastIndex = count($this->items) + $length;
- if ($lastIndex < $offset) {
- throw new InvalidRangeException("Range out of bound: [{$offset}, {$length}] results in [{$offset}, {$lastIndex}], size is " . count($this->items));
- }
- }
- }
- /**
- * @param array $query
- * @param int $offset
- * @return mixed|null
- * @throws InvalidDataValueException
- */
- public function findOneBy(array $query, int $offset = 0)
- {
- $result = $this->findBy($query, 1, $offset);
- return ($result) ? $result : null;
- }
- /**
- * @param array $query
- * @param int $limit
- * @param int $offset
- * @return mixed[]
- * @throws InvalidDataTypeException
- */
- public function findBy($query = null, int $limit = 0, int $offset = 0)
- {
- if ($query == null) {
- $items = $this->items;
- } else {
- if (!is_array($query)) {
- throw new InvalidDataTypeException(sprintf("findBy expects parameter 1 to be an array, found: %s", gettype($query)));
- }
- /* Syntax:
- * [
- * ['ISO3Code', '$eqs', 'EUR' ],
- * ['valueBaseISO3Code', '$eq', 'USD' ],
- * ['value', '$gt', 1 ] ,
- * ['$or', [ 'value', '$lt', 2 ], [ 'value', '$gt', 50 ]]
- * ['$or', [ 'value', '$lt', 2 ], ['$and', [ 'value', '$gt', 10 ], [ 'value', '$lt', 30 ]]]
- * ]
- */
- $items = [];
- $limit = ($limit <= 0) ? count($this->items) : $limit;
- for ($i = $offset; $i < count($this->items) && count($items) < $limit; $i++) {
- if ($this->andClause($query, $this->items[$i])) {
- $items[] = $this->items[$i];
- }
- }
- }
- return $items;
- }
- /**
- * @param $orClauses
- * @param $item
- * @return bool
- * @throws InvalidDataTypeException
- * @throws InvalidDataValueException
- */
- private function orClause($orClauses, $item)
- {
- if (!is_array($orClauses)) {
- throw new InvalidDataValueException('orClauses should be an array with OR clauses.');
- }
- for ($i = 0, $result = false; $i < count($orClauses) && !$result; $i++) {
- if (!is_array($orClauses[$i])) {
- throw new InvalidDataValueException('orClause should contain array with OR clauses. Each orClause should be an array with 2 values and an operator $lt, $gt, $lte, $gte or $eq');
- }
- $result = $this->checkClause($orClauses[$i], $item);
- }
- return $result;
- }
- /**
- * @param $andClauses
- * @param $item
- * @return bool
- * @throws InvalidDataTypeException
- * @throws InvalidDataValueException
- */
- private function andClause($andClauses, $item)
- {
- if (empty($andClauses)) {
- $andClauses = [];
- }
- if (!is_array($andClauses)) {
- throw new InvalidDataValueException('andClauses should be an array with AND clauses.');
- }
- for ($i = 0, $result = true; $i < count($andClauses) && $result; $i++) {
- if (!is_array($andClauses[$i])) {
- throw new InvalidDataValueException('andClause should contain array with AND clauses. Each andClause should be an array with 2 values and an operator $lt, $gt, $lte, $gte or $eq');
- }
- $result = $this->checkClause($andClauses[$i], $item);
- }
- return $result;
- }
- /**
- * @param $clause
- * @param $item
- * @return bool
- * @throws InvalidDataTypeException
- * @throws InvalidDataValueException
- */
- private function checkClause($clause, $item)
- {
- if ($clause[0] == '$or') {
- $orClauses = array_slice($clause, 1);
- $result = $this->orClause($orClauses, $item);
- } elseif ($clause[0] == '$and') {
- $andClauses = array_slice($clause, 1);
- $result = $this->andClause($andClauses, $item);
- } else {
- $value1 = null;
- $value2 = null;
- if (is_object($item)) {
- $value1 = (is_string($clause[0]) && substr($clause[0], 0, 1) == ":") ?
- $this->extractValue(substr($clause[0], 1), $item) :
- $clause[0];
- $value2 = (is_string($clause[2]) && substr($clause[2], 0, 1) == ":") ?
- $this->extractValue(substr($clause[2], 1), $item) :
- $clause[2];
- } else {
- $value1 = (is_string($clause[0]) && substr($clause[0], 0, 1) == ":") ?
- $item :
- $clause[0];
- $value2 = (is_string($clause[2]) && substr($clause[2], 0, 1) == ":") ?
- $item :
- $clause[2];
- }
- $expressionFactory = new ExpressionFactory();
- $expression = $expressionFactory->createExpression($clause[1]);
- $result = $expression->validate($value1, $value2);
- }
- return $result;
- }
- private function extractValue($fieldName, $item)
- {
- $valueSet = false;
- $reflection = new \ReflectionObject($item);
- $value = null;
- if (\property_exists($item, $fieldName)) {
- $property = $reflection->getProperty($fieldName);
- if ($property->isPublic()) {
- $value = $item->$fieldName;
- $valueSet = true;
- }
- }
- if (!$valueSet) {
- $field = "get" . ucfirst($fieldName);
- if (\method_exists($item, $field)) {
- $method = $reflection->getMethod($field);
- if ($method->isPublic()) {
- $value = $item->$field();
- } else {
- throw new InvalidDataTypeException("The getter `{$field}` is not accessable, make sure it is public");
- }
- } else {
- throw new InvalidDataTypeException("The field is not accessable, make sure the field `{$fieldName}` is public or there is a public method called `{$field}`");
- }
- }
- return $value;
- }
- public function sum(string $fieldName, array $query = null)
- {
- $items = $this->findBy($query);
- return $this->sumItems($fieldName, $items);
- }
- public function average(string $fieldName, array $query = null)
- {
- $items = $this->findBy($query);
- $sum = $this->sumItems($fieldName, $items);
- return (count($items) > 0) ? ($sum / count($items)) : 0;
- }
- private function sumItems(string $fieldName, array $items)
- {
- $sum = 0;
- foreach($items as $item) {
- $sum += floatval($this->extractValue($fieldName, $item));
- }
- return $sum;
- }
- public function min(string $fieldName, array $query = null)
- {
- $items = $this->findBy($query);
- $min = null;
- foreach($items as $item) {
- $fieldValue = $this->extractValue($fieldName, $item);
- if($min == null || $fieldValue < $min) {
- $min = $fieldValue;
- }
- }
-
- return $min;
- }
- public function Max(string $fieldName, array $query = null)
- {
- $items = $this->findBy($query);
- $max = null;
- foreach($items as $item) {
- $fieldValue = $this->extractValue($fieldName, $item);
- if($max == null || $fieldValue > $max) {
- $max = $fieldValue;
- }
- }
- return $max;
- }
- public function Count()
- {
- return count($this->items);
- }
- }