.gitignore000064400000000010144760116270006533 0ustar00vendor/ .travis.yml000064400000000141144760116270006661 0ustar00language: php php: - 5.3 - 5.4 before_script: - composer --prefer-source --dev install README.md000064400000000070144760116270006030 0ustar00# Doctrine Collections Collections Abstraction library composer.json000064400000001364144760116270007302 0ustar00{ "name": "doctrine/collections", "type": "library", "description": "Collections Abstraction library", "keywords": ["collections", "array", "iterator"], "homepage": "http://www.doctrine-project.org", "license": "MIT", "authors": [ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, {"name": "Roman Borschel", "email": "roman@code-factory.org"}, {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} ], "require": { "php": ">=5.3.2" }, "autoload": { "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } } } lib/Doctrine/Common/Collections/ArrayCollection.php000064400000032264144760116270016411 0ustar00. */ namespace Doctrine\Common\Collections; use Closure, ArrayIterator; use Doctrine\Common\Collections\Expr\Expression; use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; /** * An ArrayCollection is a Collection implementation that wraps a regular PHP array. * * @since 2.0 * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel */ class ArrayCollection implements Collection, Selectable { /** * An array containing the entries of this collection. * * @var array */ private $_elements; /** * Initializes a new ArrayCollection. * * @param array $elements */ public function __construct(array $elements = array()) { $this->_elements = $elements; } /** * Gets the PHP array representation of this collection. * * @return array The PHP array representation of this collection. */ public function toArray() { return $this->_elements; } /** * Sets the internal iterator to the first element in the collection and * returns this element. * * @return mixed */ public function first() { return reset($this->_elements); } /** * Sets the internal iterator to the last element in the collection and * returns this element. * * @return mixed */ public function last() { return end($this->_elements); } /** * Gets the current key/index at the current internal iterator position. * * @return mixed */ public function key() { return key($this->_elements); } /** * Moves the internal iterator position to the next element. * * @return mixed */ public function next() { return next($this->_elements); } /** * Gets the element of the collection at the current internal iterator position. * * @return mixed */ public function current() { return current($this->_elements); } /** * Removes an element with a specific key/index from the collection. * * @param mixed $key * @return mixed The removed element or NULL, if no element exists for the given key. */ public function remove($key) { if (isset($this->_elements[$key]) || array_key_exists($key, $this->_elements)) { $removed = $this->_elements[$key]; unset($this->_elements[$key]); return $removed; } return null; } /** * Removes the specified element from the collection, if it is found. * * @param mixed $element The element to remove. * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. */ public function removeElement($element) { $key = array_search($element, $this->_elements, true); if ($key !== false) { unset($this->_elements[$key]); return true; } return false; } /** * ArrayAccess implementation of offsetExists() * * @see containsKey() * * @param mixed $offset * @return bool */ public function offsetExists($offset) { return $this->containsKey($offset); } /** * ArrayAccess implementation of offsetGet() * * @see get() * * @param mixed $offset * @return mixed */ public function offsetGet($offset) { return $this->get($offset); } /** * ArrayAccess implementation of offsetSet() * * @see add() * @see set() * * @param mixed $offset * @param mixed $value * @return bool */ public function offsetSet($offset, $value) { if ( ! isset($offset)) { return $this->add($value); } return $this->set($offset, $value); } /** * ArrayAccess implementation of offsetUnset() * * @see remove() * * @param mixed $offset * @return mixed */ public function offsetUnset($offset) { return $this->remove($offset); } /** * Checks whether the collection contains a specific key/index. * * @param mixed $key The key to check for. * @return boolean TRUE if the given key/index exists, FALSE otherwise. */ public function containsKey($key) { return isset($this->_elements[$key]) || array_key_exists($key, $this->_elements); } /** * Checks whether the given element is contained in the collection. * Only element values are compared, not keys. The comparison of two elements * is strict, that means not only the value but also the type must match. * For objects this means reference equality. * * @param mixed $element * @return boolean TRUE if the given element is contained in the collection, * FALSE otherwise. */ public function contains($element) { return in_array($element, $this->_elements, true); } /** * Tests for the existence of an element that satisfies the given predicate. * * @param Closure $p The predicate. * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ public function exists(Closure $p) { foreach ($this->_elements as $key => $element) { if ($p($key, $element)) { return true; } } return false; } /** * Searches for a given element and, if found, returns the corresponding key/index * of that element. The comparison of two elements is strict, that means not * only the value but also the type must match. * For objects this means reference equality. * * @param mixed $element The element to search for. * @return mixed The key/index of the element or FALSE if the element was not found. */ public function indexOf($element) { return array_search($element, $this->_elements, true); } /** * Gets the element with the given key/index. * * @param mixed $key The key. * @return mixed The element or NULL, if no element exists for the given key. */ public function get($key) { if (isset($this->_elements[$key])) { return $this->_elements[$key]; } return null; } /** * Gets all keys/indexes of the collection elements. * * @return array */ public function getKeys() { return array_keys($this->_elements); } /** * Gets all elements. * * @return array */ public function getValues() { return array_values($this->_elements); } /** * Returns the number of elements in the collection. * * Implementation of the Countable interface. * * @return integer The number of elements in the collection. */ public function count() { return count($this->_elements); } /** * Adds/sets an element in the collection at the index / with the specified key. * * When the collection is a Map this is like put(key,value)/add(key,value). * When the collection is a List this is like add(position,value). * * @param mixed $key * @param mixed $value */ public function set($key, $value) { $this->_elements[$key] = $value; } /** * Adds an element to the collection. * * @param mixed $value * @return boolean Always TRUE. */ public function add($value) { $this->_elements[] = $value; return true; } /** * Checks whether the collection is empty. * * Note: This is preferable over count() == 0. * * @return boolean TRUE if the collection is empty, FALSE otherwise. */ public function isEmpty() { return ! $this->_elements; } /** * Gets an iterator for iterating over the elements in the collection. * * @return ArrayIterator */ public function getIterator() { return new ArrayIterator($this->_elements); } /** * Applies the given function to each element in the collection and returns * a new collection with the elements returned by the function. * * @param Closure $func * @return Collection */ public function map(Closure $func) { return new static(array_map($func, $this->_elements)); } /** * Returns all the elements of this collection that satisfy the predicate p. * The order of the elements is preserved. * * @param Closure $p The predicate used for filtering. * @return Collection A collection with the results of the filter operation. */ public function filter(Closure $p) { return new static(array_filter($this->_elements, $p)); } /** * Applies the given predicate p to all elements of this collection, * returning true, if the predicate yields true for all elements. * * @param Closure $p The predicate. * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. */ public function forAll(Closure $p) { foreach ($this->_elements as $key => $element) { if ( ! $p($key, $element)) { return false; } } return true; } /** * Partitions this collection in two collections according to a predicate. * Keys are preserved in the resulting collections. * * @param Closure $p The predicate on which to partition. * @return array An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. */ public function partition(Closure $p) { $coll1 = $coll2 = array(); foreach ($this->_elements as $key => $element) { if ($p($key, $element)) { $coll1[$key] = $element; } else { $coll2[$key] = $element; } } return array(new static($coll1), new static($coll2)); } /** * Returns a string representation of this object. * * @return string */ public function __toString() { return __CLASS__ . '@' . spl_object_hash($this); } /** * Clears the collection. */ public function clear() { $this->_elements = array(); } /** * Extract a slice of $length elements starting at position $offset from the Collection. * * If $length is null it returns all elements from $offset to the end of the Collection. * Keys have to be preserved by this method. Calling this method will only return the * selected slice and NOT change the elements contained in the collection slice is called on. * * @param int $offset * @param int $length * @return array */ public function slice($offset, $length = null) { return array_slice($this->_elements, $offset, $length, true); } /** * Select all elements from a selectable that match the criteria and * return a new collection containing these elements. * * @param Criteria $criteria * @return Collection */ public function matching(Criteria $criteria) { $expr = $criteria->getWhereExpression(); $filtered = $this->_elements; if ($expr) { $visitor = new ClosureExpressionVisitor(); $filter = $visitor->dispatch($expr); $filtered = array_filter($filtered, $filter); } if ($orderings = $criteria->getOrderings()) { $next = null; foreach (array_reverse($orderings) as $field => $ordering) { $next = ClosureExpressionVisitor::sortByField($field, $ordering == 'DESC' ? -1 : 1, $next); } usort($filtered, $next); } $offset = $criteria->getFirstResult(); $length = $criteria->getMaxResults(); if ($offset || $length) { $filtered = array_slice($filtered, (int)$offset, $length); } return new static($filtered); } } lib/Doctrine/Common/Collections/Collection.php000064400000020115144760116270015402 0ustar00. */ namespace Doctrine\Common\Collections; use Closure, Countable, IteratorAggregate, ArrayAccess; /** * The missing (SPL) Collection/Array/OrderedMap interface. * * A Collection resembles the nature of a regular PHP array. That is, * it is essentially an ordered map that can also be used * like a list. * * A Collection has an internal iterator just like a PHP array. In addition, * a Collection can be iterated with external iterators, which is preferrable. * To use an external iterator simply use the foreach language construct to * iterate over the collection (which calls {@link getIterator()} internally) or * explicitly retrieve an iterator though {@link getIterator()} which can then be * used to iterate over the collection. * You can not rely on the internal iterator of the collection being at a certain * position unless you explicitly positioned it before. Prefer iteration with * external iterators. * * @since 2.0 * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel */ interface Collection extends Countable, IteratorAggregate, ArrayAccess { /** * Adds an element at the end of the collection. * * @param mixed $element The element to add. * @return boolean Always TRUE. */ function add($element); /** * Clears the collection, removing all elements. */ function clear(); /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, FALSE otherwise. */ function contains($element); /** * Checks whether the collection is empty (contains no elements). * * @return boolean TRUE if the collection is empty, FALSE otherwise. */ function isEmpty(); /** * Removes the element at the specified index from the collection. * * @param string|integer $key The kex/index of the element to remove. * @return mixed The removed element or NULL, if the collection did not contain the element. */ function remove($key); /** * Removes the specified element from the collection, if it is found. * * @param mixed $element The element to remove. * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. */ function removeElement($element); /** * Checks whether the collection contains an element with the specified key/index. * * @param string|integer $key The key/index to check for. * @return boolean TRUE if the collection contains an element with the specified key/index, * FALSE otherwise. */ function containsKey($key); /** * Gets the element at the specified key/index. * * @param string|integer $key The key/index of the element to retrieve. * @return mixed */ function get($key); /** * Gets all keys/indices of the collection. * * @return array The keys/indices of the collection, in the order of the corresponding * elements in the collection. */ function getKeys(); /** * Gets all values of the collection. * * @return array The values of all elements in the collection, in the order they * appear in the collection. */ function getValues(); /** * Sets an element in the collection at the specified key/index. * * @param string|integer $key The key/index of the element to set. * @param mixed $value The element to set. */ function set($key, $value); /** * Gets a native PHP array representation of the collection. * * @return array */ function toArray(); /** * Sets the internal iterator to the first element in the collection and * returns this element. * * @return mixed */ function first(); /** * Sets the internal iterator to the last element in the collection and * returns this element. * * @return mixed */ function last(); /** * Gets the key/index of the element at the current iterator position. * */ function key(); /** * Gets the element of the collection at the current iterator position. * */ function current(); /** * Moves the internal iterator position to the next element. * */ function next(); /** * Tests for the existence of an element that satisfies the given predicate. * * @param Closure $p The predicate. * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ function exists(Closure $p); /** * Returns all the elements of this collection that satisfy the predicate p. * The order of the elements is preserved. * * @param Closure $p The predicate used for filtering. * @return Collection A collection with the results of the filter operation. */ function filter(Closure $p); /** * Applies the given predicate p to all elements of this collection, * returning true, if the predicate yields true for all elements. * * @param Closure $p The predicate. * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. */ function forAll(Closure $p); /** * Applies the given function to each element in the collection and returns * a new collection with the elements returned by the function. * * @param Closure $func * @return Collection */ function map(Closure $func); /** * Partitions this collection in two collections according to a predicate. * Keys are preserved in the resulting collections. * * @param Closure $p The predicate on which to partition. * @return array An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. */ function partition(Closure $p); /** * Gets the index/key of a given element. The comparison of two elements is strict, * that means not only the value but also the type must match. * For objects this means reference equality. * * @param mixed $element The element to search for. * @return mixed The key/index of the element or FALSE if the element was not found. */ function indexOf($element); /** * Extract a slice of $length elements starting at position $offset from the Collection. * * If $length is null it returns all elements from $offset to the end of the Collection. * Keys have to be preserved by this method. Calling this method will only return the * selected slice and NOT change the elements contained in the collection slice is called on. * * @param int $offset * @param int $length * @return array */ function slice($offset, $length = null); } lib/Doctrine/Common/Collections/Criteria.php000064400000013165144760116270015060 0ustar00. */ namespace Doctrine\Common\Collections; use Doctrine\Common\Collections\Expr\Expression; use Doctrine\Common\Collections\Expr\CompositeExpression; /** * Criteria for filtering Selectable collections. * * @author Benjamin Eberlei * @since 2.3 */ class Criteria { /** * @var string */ const ASC = 'ASC'; /** * @var string */ const DESC = 'DESC'; /** * @var \Doctrine\Common\Collections\ExpressionBuilder */ private static $expressionBuilder; /** * @var \Doctrine\Common\Collections\Expr\Expression */ private $expression; /** * @var array|null */ private $orderings; /** * @var int */ private $firstResult; /** * @var int */ private $maxResults; /** * Creates an instance of the class. * * @return Criteria */ public static function create() { return new static(); } /** * Return the expression builder. * * @return \Doctrine\Common\Collections\ExpressionBuilder */ public static function expr() { if (self::$expressionBuilder === null) { self::$expressionBuilder = new ExpressionBuilder(); } return self::$expressionBuilder; } /** * Construct new criteria * * @param Expression $expression * @param array $orderings * @param int $firstResult * @param int $maxResults */ public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) { $this->expression = $expression; $this->orderings = $orderings; $this->firstResult = $firstResult; $this->maxResults = $maxResults; } /** * Set the where expression to evaluate when this criteria is searched for. * * @param Expression * @return Criteria */ public function where(Expression $expression) { $this->expression = $expression; return $this; } /** * Append the where expression to evaluate when this criteria is searched for * using an AND with previous expression. * * @param Expression * @return Criteria */ public function andWhere(Expression $expression) { if ($this->expression === null) { return $this->where($expression); } $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array( $this->expression, $expression )); return $this; } /** * Append the where expression to evaluate when this criteria is searched for * using an OR with previous expression. * * @param Expression * @return Criteria */ public function orWhere(Expression $expression) { if ($this->expression === null) { return $this->where($expression); } $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array( $this->expression, $expression )); return $this; } /** * Get the expression attached to this criteria. * * @return Expression|null */ public function getWhereExpression() { return $this->expression; } /** * Get current orderings of this Criteria * * @return array */ public function getOrderings() { return $this->orderings; } /** * Set the ordering of the result of this criteria. * * Keys are field and values are the order, being either ASC or DESC. * * @see Criteria::ASC * @see Criteria::DESC * * @param array * @return Criteria */ public function orderBy(array $orderings) { $this->orderings = $orderings; return $this; } /** * Get current first result option of the critera. * * @return firstResult. */ public function getFirstResult() { return $this->firstResult; } /** * Set number of first result that this criteria should return. * * @param firstResult the value to set. * @return Criteria */ public function setFirstResult($firstResult) { $this->firstResult = $firstResult; return $this; } /** * Get maxResults. * * @return maxResults. */ public function getMaxResults() { return $this->maxResults; } /** * Set maxResults. * * @param maxResults the value to set. * @return Criteria */ public function setMaxResults($maxResults) { $this->maxResults = $maxResults; return $this; } } lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php000064400000014453144760116270021311 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * Walks an expression graph and turns it into a PHP closure. * * This closure can be used with {@Collection#filter()} and is used internally * by {@ArrayCollection#select()}. * * @author Benjamin Eberlei * @since 2.3 */ class ClosureExpressionVisitor extends ExpressionVisitor { /** * Access the field of a given object. This field has to be public directly * or indirectly (through an accessor get* or a magic method, __get, __call). * * is*() is not supported. * * @return mixed */ static public function getObjectFieldValue($object, $field) { $accessor = "get" . $field; if (method_exists($object, $accessor) || method_exists($object, '__call')) { return $object->$accessor(); } if ($object instanceof \ArrayAccess) { return $object[$field]; } return $object->$field; } /** * Helper for sorting arrays of objects based on multiple fields + * orientations. * * @param string $name * @param int $orientation * @param Closure $next * @return Closure */ static public function sortByField($name, $orientation = 1, \Closure $next = null) { if (!$next) { $next = function() { return 0; }; } return function ($a, $b) use ($name, $next, $orientation) { $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); if ($aValue === $bValue) { return $next($a, $b); } return (($aValue > $bValue) ? 1 : -1) * $orientation; }; } /** * {@inheritDoc} */ public function walkComparison(Comparison $comparison) { $field = $comparison->getField(); $value = $comparison->getValue()->getValue(); // shortcut for walkValue() switch ($comparison->getOperator()) { case Comparison::EQ: case Comparison::IS: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; }; case Comparison::NEQ: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; }; case Comparison::LT: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; }; case Comparison::LTE: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; }; case Comparison::GT: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; }; case Comparison::GTE: return function ($object) use ($field, $value) { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; }; case Comparison::IN: return function ($object) use ($field, $value) { return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); }; case Comparison::NIN: return function ($object) use ($field, $value) { return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); }; default: throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); } } /** * {@inheritDoc} */ public function walkValue(Value $value) { return $value->getValue(); } /** * {@inheritDoc} */ public function walkCompositeExpression(CompositeExpression $expr) { $expressionList = array(); foreach ($expr->getExpressionList() as $child) { $expressionList[] = $this->dispatch($child); } switch($expr->getType()) { case CompositeExpression::TYPE_AND: return $this->andExpressions($expressionList); case CompositeExpression::TYPE_OR: return $this->orExpressions($expressionList); default: throw new \RuntimeException("Unknown composite " . $expr->getType()); } } private function andExpressions($expressions) { return function ($object) use ($expressions) { foreach ($expressions as $expression) { if ( ! $expression($object)) { return false; } } return true; }; } private function orExpressions($expressions) { return function ($object) use ($expressions) { foreach ($expressions as $expression) { if ($expression($object)) { return true; } } return false; }; } } lib/Doctrine/Common/Collections/Expr/Comparison.php000064400000004027144760116270016343 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * Comparison of a field with a value by the given operator. * * @author Benjamin Eberlei * @since 2.3 */ class Comparison implements Expression { const EQ = '='; const NEQ = '<>'; const LT = '<'; const LTE = '<='; const GT = '>'; const GTE = '>='; const IS = 'IS'; const IN = 'IN'; const NIN = 'NIN'; private $field; private $op; private $value; public function __construct($field, $operator, $value) { if ( ! ($value instanceof Value)) { $value = new Value($value); } $this->field = $field; $this->op = $operator; $this->value = $value; } public function getField() { return $this->field; } public function getValue() { return $this->value; } public function getOperator() { return $this->op; } public function visit(ExpressionVisitor $visitor) { return $visitor->walkComparison($this); } } lib/Doctrine/Common/Collections/Expr/CompositeExpression.php000064400000004327144760116270020256 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * Expression of Expressions combined by AND or OR operation. * * @author Benjamin Eberlei * @since 2.3 */ class CompositeExpression implements Expression { const TYPE_AND = 'AND'; const TYPE_OR = 'OR'; private $type; private $expressions = array(); public function __construct($type, array $expressions) { $this->type = $type; foreach ($expressions as $expr) { if ($expr instanceof Value) { throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); } if ( ! ($expr instanceof Expression)) { throw new \RuntimeException("No expression given to CompositeExpression."); } $this->expressions[] = $expr; } } /** * Return the list of expressions nested in this composite. * * @return Expression[] */ public function getExpressionList() { return $this->expressions; } public function getType() { return $this->type; } public function visit(ExpressionVisitor $visitor) { return $visitor->walkCompositeExpression($this); } } lib/Doctrine/Common/Collections/Expr/Expression.php000064400000002314144760116270016365 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * Expression for the {@link Selectable} interface. * * @author Benjamin Eberlei */ interface Expression { public function visit(ExpressionVisitor $visitor); } lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php000064400000005046144760116270017752 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * An Expression visitor walks a graph of expressions and turns them into a * query for the underlying implementation. * * @author Benjamin Eberlei */ abstract class ExpressionVisitor { /** * Convert a comparison expression into the target query language output * * @param Comparison $comparison * * @return mixed */ abstract public function walkComparison(Comparison $comparison); /** * Convert a value expression into the target query language part. * * @param Value $value * * @return mixed */ abstract public function walkValue(Value $value); /** * Convert a composite expression into the target query language output * * @param CompositeExpression $expr * * @return mixed */ abstract public function walkCompositeExpression(CompositeExpression $expr); /** * Dispatch walking an expression to the appropriate handler. * * @param Expression * * @return mixed */ public function dispatch(Expression $expr) { switch (true) { case ($expr instanceof Comparison): return $this->walkComparison($expr); case ($expr instanceof Value): return $this->walkValue($expr); case ($expr instanceof CompositeExpression): return $this->walkCompositeExpression($expr); default: throw new \RuntimeException("Unknown Expression " . get_class($expr)); } } } lib/Doctrine/Common/Collections/Expr/Value.php000064400000002520144760116270015301 0ustar00. */ namespace Doctrine\Common\Collections\Expr; class Value implements Expression { private $value; public function __construct($value) { $this->value = $value; } public function getValue() { return $this->value; } public function visit(ExpressionVisitor $visitor) { return $visitor->walkValue($this); } } lib/Doctrine/Common/Collections/ExpressionBuilder.php000064400000007344144760116270016766 0ustar00. */ namespace Doctrine\Common\Collections; use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\Common\Collections\Expr\CompositeExpression; use Doctrine\Common\Collections\Expr\Value; /** * Builder for Expressions in the {@link Selectable} interface. * * @author Benjamin Eberlei * @since 2.3 */ class ExpressionBuilder { /** * @return CompositeExpression */ public function andX($x = null) { return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); } /** * @return CompositeExpression */ public function orX($x = null) { return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function eq($field, $value) { return new Comparison($field, Comparison::EQ, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function gt($field, $value) { return new Comparison($field, Comparison::GT, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function lt($field, $value) { return new Comparison($field, Comparison::LT, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function gte($field, $value) { return new Comparison($field, Comparison::GTE, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function lte($field, $value) { return new Comparison($field, Comparison::LTE, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function neq($field, $value) { return new Comparison($field, Comparison::NEQ, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function isNull($field) { return new Comparison($field, Comparison::IS, new Value(null)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function in($field, array $values) { return new Comparison($field, Comparison::IN, new Value($values)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function notIn($field, array $values) { return new Comparison($field, Comparison::NIN, new Value($values)); } } lib/Doctrine/Common/Collections/Selectable.php000064400000003653144760116270015362 0ustar00. */ namespace Doctrine\Common\Collections; /** * Interface for collections that allow efficient filtering with an expression API. * * Goal of this interface is a backend independent method to fetch elements * from a collections. {@link Expression} is crafted in a way that you can * implement queries from both in-memory and database-backed collections. * * For database backed collections this allows very efficient access by * utilizing the query APIs, for example SQL in the ORM. Applications using * this API can implement efficient database access without having to ask the * EntityManager or Repositories. * * @author Benjamin Eberlei * @since 2.3 */ interface Selectable { /** * Select all elements from a selectable that match the expression and * return a new collection containing these elements. * * @param Criteria $criteria * @return Collection */ function matching(Criteria $criteria); } phpunit.xml.dist000064400000001467144760116270007737 0ustar00 ./tests/Doctrine/ ./lib/Doctrine/ performance tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php000064400000014634144760116270022732 0ustar00. */ namespace Doctrine\Tests\Common\Collections; use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; use Doctrine\Common\Collections\ExpressionBuilder; /** * @group DDC-1637 */ class ClosureExpressionVisitorTest extends \PHPUnit_Framework_TestCase { private $visitor; private $builder; public function setUp() { $this->visitor = new ClosureExpressionVisitor(); $this->builder = new ExpressionBuilder(); } public function testWalkEqualsComparison() { $closure = $this->visitor->walkComparison($this->builder->eq("foo", 1)); $this->assertTrue($closure(new TestObject(1))); $this->assertFalse($closure(new TestObject(2))); } public function testWalkNotEqualsComparison() { $closure = $this->visitor->walkComparison($this->builder->neq("foo", 1)); $this->assertFalse($closure(new TestObject(1))); $this->assertTrue($closure(new TestObject(2))); } public function testWalkLessThanComparison() { $closure = $this->visitor->walkComparison($this->builder->lt("foo", 1)); $this->assertFalse($closure(new TestObject(1))); $this->assertTrue($closure(new TestObject(0))); } public function testWalkLessThanEqualsComparison() { $closure = $this->visitor->walkComparison($this->builder->lte("foo", 1)); $this->assertFalse($closure(new TestObject(2))); $this->assertTrue($closure(new TestObject(1))); $this->assertTrue($closure(new TestObject(0))); } public function testWalkGreaterThanEqualsComparison() { $closure = $this->visitor->walkComparison($this->builder->gte("foo", 1)); $this->assertTrue($closure(new TestObject(2))); $this->assertTrue($closure(new TestObject(1))); $this->assertFalse($closure(new TestObject(0))); } public function testWalkGreaterThanComparison() { $closure = $this->visitor->walkComparison($this->builder->gt("foo", 1)); $this->assertTrue($closure(new TestObject(2))); $this->assertFalse($closure(new TestObject(1))); $this->assertFalse($closure(new TestObject(0))); } public function testWalkInComparison() { $closure = $this->visitor->walkComparison($this->builder->in("foo", array(1, 2, 3))); $this->assertTrue($closure(new TestObject(2))); $this->assertTrue($closure(new TestObject(1))); $this->assertFalse($closure(new TestObject(0))); } public function testWalkNotInComparison() { $closure = $this->visitor->walkComparison($this->builder->notIn("foo", array(1, 2, 3))); $this->assertFalse($closure(new TestObject(1))); $this->assertFalse($closure(new TestObject(2))); $this->assertTrue($closure(new TestObject(0))); $this->assertTrue($closure(new TestObject(4))); } public function testWalkAndCompositeExpression() { $closure = $this->visitor->walkCompositeExpression( $this->builder->andX( $this->builder->eq("foo", 1), $this->builder->eq("bar", 1) ) ); $this->assertTrue($closure(new TestObject(1, 1))); $this->assertFalse($closure(new TestObject(1, 0))); $this->assertFalse($closure(new TestObject(0, 1))); $this->assertFalse($closure(new TestObject(0, 0))); } public function testWalkOrCompositeExpression() { $closure = $this->visitor->walkCompositeExpression( $this->builder->orX( $this->builder->eq("foo", 1), $this->builder->eq("bar", 1) ) ); $this->assertTrue($closure(new TestObject(1, 1))); $this->assertTrue($closure(new TestObject(1, 0))); $this->assertTrue($closure(new TestObject(0, 1))); $this->assertFalse($closure(new TestObject(0, 0))); } public function testSortByFieldAscending() { $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); $sort = ClosureExpressionVisitor::sortByField("foo"); usort($objects, $sort); $this->assertEquals("a", $objects[0]->getFoo()); $this->assertEquals("b", $objects[1]->getFoo()); $this->assertEquals("c", $objects[2]->getFoo()); } public function testSortByFieldDescending() { $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); $sort = ClosureExpressionVisitor::sortByField("foo", -1); usort($objects, $sort); $this->assertEquals("c", $objects[0]->getFoo()); $this->assertEquals("b", $objects[1]->getFoo()); $this->assertEquals("a", $objects[2]->getFoo()); } public function testSortDelegate() { $objects = array(new TestObject("a", "c"), new TestObject("a", "b"), new TestObject("a", "a")); $sort = ClosureExpressionVisitor::sortByField("bar", 1); $sort = ClosureExpressionVisitor::sortByField("foo", 1, $sort); usort($objects, $sort); $this->assertEquals("a", $objects[0]->getBar()); $this->assertEquals("b", $objects[1]->getBar()); $this->assertEquals("c", $objects[2]->getBar()); } } class TestObject { private $foo; private $bar; public function __construct($foo = null, $bar = null) { $this->foo = $foo; $this->bar = $bar; } public function getFoo() { return $this->foo; } public function getBar() { return $this->bar; } } tests/Doctrine/Tests/Common/Collections/CollectionTest.php000064400000016330144760116270017744 0ustar00_coll = new \Doctrine\Common\Collections\ArrayCollection; } public function testIssetAndUnset() { $this->assertFalse(isset($this->_coll[0])); $this->_coll->add('testing'); $this->assertTrue(isset($this->_coll[0])); unset($this->_coll[0]); $this->assertFalse(isset($this->_coll[0])); } public function testToString() { $this->_coll->add('testing'); $this->assertTrue(is_string((string) $this->_coll)); } public function testRemovingNonExistentEntryReturnsNull() { $this->assertEquals(null, $this->_coll->remove('testing_does_not_exist')); } public function testExists() { $this->_coll->add("one"); $this->_coll->add("two"); $exists = $this->_coll->exists(function($k, $e) { return $e == "one"; }); $this->assertTrue($exists); $exists = $this->_coll->exists(function($k, $e) { return $e == "other"; }); $this->assertFalse($exists); } public function testMap() { $this->_coll->add(1); $this->_coll->add(2); $res = $this->_coll->map(function($e) { return $e * 2; }); $this->assertEquals(array(2, 4), $res->toArray()); } public function testFilter() { $this->_coll->add(1); $this->_coll->add("foo"); $this->_coll->add(3); $res = $this->_coll->filter(function($e) { return is_numeric($e); }); $this->assertEquals(array(0 => 1, 2 => 3), $res->toArray()); } public function testFirstAndLast() { $this->_coll->add('one'); $this->_coll->add('two'); $this->assertEquals($this->_coll->first(), 'one'); $this->assertEquals($this->_coll->last(), 'two'); } public function testArrayAccess() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertEquals($this->_coll[0], 'one'); $this->assertEquals($this->_coll[1], 'two'); unset($this->_coll[0]); $this->assertEquals($this->_coll->count(), 1); } public function testContainsKey() { $this->_coll[5] = 'five'; $this->assertTrue($this->_coll->containsKey(5)); } public function testContains() { $this->_coll[0] = 'test'; $this->assertTrue($this->_coll->contains('test')); } public function testSearch() { $this->_coll[0] = 'test'; $this->assertEquals(0, $this->_coll->indexOf('test')); } public function testGet() { $this->_coll[0] = 'test'; $this->assertEquals('test', $this->_coll->get(0)); } public function testGetKeys() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertEquals(array(0, 1), $this->_coll->getKeys()); } public function testGetValues() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertEquals(array('one', 'two'), $this->_coll->getValues()); } public function testCount() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertEquals($this->_coll->count(), 2); $this->assertEquals(count($this->_coll), 2); } public function testForAll() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertEquals($this->_coll->forAll(function($k, $e) { return is_string($e); }), true); $this->assertEquals($this->_coll->forAll(function($k, $e) { return is_array($e); }), false); } public function testPartition() { $this->_coll[] = true; $this->_coll[] = false; $partition = $this->_coll->partition(function($k, $e) { return $e == true; }); $this->assertEquals($partition[0][0], true); $this->assertEquals($partition[1][0], false); } public function testClear() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->_coll->clear(); $this->assertEquals($this->_coll->isEmpty(), true); } public function testRemove() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $el = $this->_coll->remove(0); $this->assertEquals('one', $el); $this->assertEquals($this->_coll->contains('one'), false); $this->assertNull($this->_coll->remove(0)); } public function testRemoveElement() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->assertTrue($this->_coll->removeElement('two')); $this->assertFalse($this->_coll->contains('two')); $this->assertFalse($this->_coll->removeElement('two')); } public function testSlice() { $this->_coll[] = 'one'; $this->_coll[] = 'two'; $this->_coll[] = 'three'; $slice = $this->_coll->slice(0, 1); $this->assertInternalType('array', $slice); $this->assertEquals(array('one'), $slice); $slice = $this->_coll->slice(1); $this->assertEquals(array(1 => 'two', 2 => 'three'), $slice); $slice = $this->_coll->slice(1, 1); $this->assertEquals(array(1 => 'two'), $slice); } public function fillMatchingFixture() { $std1 = new \stdClass(); $std1->foo = "bar"; $this->_coll[] = $std1; $std2 = new \stdClass(); $std2->foo = "baz"; $this->_coll[] = $std2; } /** * @group DDC-1637 */ public function testMatching() { $this->fillMatchingFixture(); $col = $this->_coll->matching(new Criteria(Criteria::expr()->eq("foo", "bar"))); $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); $this->assertNotSame($col, $this->_coll); $this->assertEquals(1, count($col)); } /** * @group DDC-1637 */ public function testMatchingOrdering() { $this->fillMatchingFixture(); $col = $this->_coll->matching(new Criteria(null, array('foo' => 'DESC'))); $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); $this->assertNotSame($col, $this->_coll); $this->assertEquals(2, count($col)); $this->assertEquals('baz', $col[0]->foo); $this->assertEquals('bar', $col[1]->foo); } /** * @group DDC-1637 */ public function testMatchingSlice() { $this->fillMatchingFixture(); $col = $this->_coll->matching(new Criteria(null, null, 1, 1)); $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); $this->assertNotSame($col, $this->_coll); $this->assertEquals(1, count($col)); $this->assertEquals('baz', $col[0]->foo); } public function testCanRemoveNullValuesByKey() { $this->_coll->add(null); $this->_coll->remove(0); $this->assertTrue($this->_coll->isEmpty()); } public function testCanVerifyExistingKeysWithNullValues() { $this->_coll->set('key', null); $this->assertTrue($this->_coll->containsKey('key')); } } tests/Doctrine/Tests/Common/Collections/CriteriaTest.php000064400000005066144760116270017417 0ustar00assertInstanceOf("Doctrine\Common\Collections\Criteria", $criteria); } public function testConstructor() { $expr = new Comparison("field", "=", "value"); $criteria = new Criteria($expr, array("foo" => "ASC"), 10, 20); $this->assertSame($expr, $criteria->getWhereExpression()); $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); $this->assertEquals(10, $criteria->getFirstResult()); $this->assertEquals(20, $criteria->getMaxResults()); } public function testWhere() { $expr = new Comparison("field", "=", "value"); $criteria = new Criteria(); $criteria->where($expr); $this->assertSame($expr, $criteria->getWhereExpression()); } public function testAndWhere() { $expr = new Comparison("field", "=", "value"); $criteria = new Criteria(); $criteria->where($expr); $expr = $criteria->getWhereExpression(); $criteria->andWhere($expr); $where = $criteria->getWhereExpression(); $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); $this->assertEquals(CompositeExpression::TYPE_AND, $where->getType()); $this->assertSame(array($expr, $expr), $where->getExpressionList()); } public function testOrWhere() { $expr = new Comparison("field", "=", "value"); $criteria = new Criteria(); $criteria->where($expr); $expr = $criteria->getWhereExpression(); $criteria->orWhere($expr); $where = $criteria->getWhereExpression(); $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); $this->assertEquals(CompositeExpression::TYPE_OR, $where->getType()); $this->assertSame(array($expr, $expr), $where->getExpressionList()); } public function testOrderings() { $criteria = Criteria::create() ->orderBy(array("foo" => "ASC")); $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); } public function testExpr() { $this->assertInstanceOf('Doctrine\Common\Collections\ExpressionBuilder', Criteria::expr()); } } tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php000064400000006444144760116270021324 0ustar00builder = new ExpressionBuilder(); } public function testAndX() { $expr = $this->builder->andX($this->builder->eq("a", "b")); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\CompositeExpression", $expr); $this->assertEquals(CompositeExpression::TYPE_AND, $expr->getType()); } public function testOrX() { $expr = $this->builder->orX($this->builder->eq("a", "b")); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\CompositeExpression", $expr); $this->assertEquals(CompositeExpression::TYPE_OR, $expr->getType()); } public function testInvalidAndXArgument() { $this->setExpectedException("RuntimeException"); $this->builder->andX("foo"); } public function testEq() { $expr = $this->builder->eq("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::EQ, $expr->getOperator()); } public function testNeq() { $expr = $this->builder->neq("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::NEQ, $expr->getOperator()); } public function testLt() { $expr = $this->builder->lt("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::LT, $expr->getOperator()); } public function testGt() { $expr = $this->builder->gt("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::GT, $expr->getOperator()); } public function testGte() { $expr = $this->builder->gte("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::GTE, $expr->getOperator()); } public function testLte() { $expr = $this->builder->lte("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::LTE, $expr->getOperator()); } public function testIn() { $expr = $this->builder->in("a", array("b")); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::IN, $expr->getOperator()); } public function testNotIn() { $expr = $this->builder->notIn("a", array("b")); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::NIN, $expr->getOperator()); } public function testIsNull() { $expr = $this->builder->isNull("a"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::IS, $expr->getOperator()); } } tests/Doctrine/Tests/DoctrineTestCase.php000064400000000244144760116270014503 0ustar00