.gitignore000064400000000010144760113340006526 0ustar00vendor/ .travis.yml000064400000000151144760113340006655 0ustar00language: php php: - 5.3 - 5.4 - 5.5 before_script: - composer --prefer-source --dev install README.md000064400000000070144760113340006023 0ustar00# Doctrine Collections Collections Abstraction library composer.json000064400000001524144760113340007273 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/" } }, "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } } } lib/Doctrine/Common/Collections/ArrayCollection.php000064400000020015144760113340016373 0ustar00. */ namespace Doctrine\Common\Collections; use Closure, ArrayIterator; 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; } /** * {@inheritDoc} */ public function toArray() { return $this->_elements; } /** * {@inheritDoc} */ public function first() { return reset($this->_elements); } /** * {@inheritDoc} */ public function last() { return end($this->_elements); } /** * {@inheritDoc} */ public function key() { return key($this->_elements); } /** * {@inheritDoc} */ public function next() { return next($this->_elements); } /** * {@inheritDoc} */ public function current() { return current($this->_elements); } /** * {@inheritDoc} */ 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; } /** * {@inheritDoc} */ public function removeElement($element) { $key = array_search($element, $this->_elements, true); if ($key !== false) { unset($this->_elements[$key]); return true; } return false; } /** * Required by interface ArrayAccess. * * {@inheritDoc} */ public function offsetExists($offset) { return $this->containsKey($offset); } /** * Required by interface ArrayAccess. * * {@inheritDoc} */ public function offsetGet($offset) { return $this->get($offset); } /** * Required by interface ArrayAccess. * * {@inheritDoc} */ public function offsetSet($offset, $value) { if ( ! isset($offset)) { return $this->add($value); } return $this->set($offset, $value); } /** * Required by interface ArrayAccess. * * {@inheritDoc} */ public function offsetUnset($offset) { return $this->remove($offset); } /** * {@inheritDoc} */ public function containsKey($key) { return isset($this->_elements[$key]) || array_key_exists($key, $this->_elements); } /** * {@inheritDoc} */ public function contains($element) { return in_array($element, $this->_elements, true); } /** * {@inheritDoc} */ public function exists(Closure $p) { foreach ($this->_elements as $key => $element) { if ($p($key, $element)) { return true; } } return false; } /** * {@inheritDoc} */ public function indexOf($element) { return array_search($element, $this->_elements, true); } /** * {@inheritDoc} */ public function get($key) { if (isset($this->_elements[$key])) { return $this->_elements[$key]; } return null; } /** * {@inheritDoc} */ public function getKeys() { return array_keys($this->_elements); } /** * {@inheritDoc} */ public function getValues() { return array_values($this->_elements); } /** * {@inheritDoc} */ public function count() { return count($this->_elements); } /** * {@inheritDoc} */ public function set($key, $value) { $this->_elements[$key] = $value; } /** * {@inheritDoc} */ public function add($value) { $this->_elements[] = $value; return true; } /** * {@inheritDoc} */ public function isEmpty() { return ! $this->_elements; } /** * Required by interface IteratorAggregate. * * {@inheritDoc} */ public function getIterator() { return new ArrayIterator($this->_elements); } /** * {@inheritDoc} */ public function map(Closure $func) { return new static(array_map($func, $this->_elements)); } /** * {@inheritDoc} */ public function filter(Closure $p) { return new static(array_filter($this->_elements, $p)); } /** * {@inheritDoc} */ public function forAll(Closure $p) { foreach ($this->_elements as $key => $element) { if ( ! $p($key, $element)) { return false; } } return true; } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ public function clear() { $this->_elements = array(); } /** * {@inheritDoc} */ public function slice($offset, $length = null) { return array_slice($this->_elements, $offset, $length, true); } /** * {@inheritDoc} */ 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.php000064400000020664144760113340015406 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. * * @return void */ 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. * * @return void */ 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. * * @return int|string */ function key(); /** * Gets the element of the collection at the current iterator position. * * @return mixed */ function current(); /** * Moves the internal iterator position to the next element and returns this element. * * @return mixed */ 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 int|string|bool The key/index of the element or FALSE if the element was not found. */ function indexOf($element); /** * Extracts 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 The offset to start from. * @param int|null $length The maximum number of elements to return, or null for no limit. * * @return array */ function slice($offset, $length = null); } lib/Doctrine/Common/Collections/Criteria.php000064400000013435144760113340015053 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|null */ private static $expressionBuilder; /** * @var \Doctrine\Common\Collections\Expr\Expression|null */ private $expression; /** * @var array|null */ private $orderings; /** * @var int|null */ private $firstResult; /** * @var int|null */ private $maxResults; /** * Creates an instance of the class. * * @return Criteria */ public static function create() { return new static(); } /** * Returns the expression builder. * * @return \Doctrine\Common\Collections\ExpressionBuilder */ public static function expr() { if (self::$expressionBuilder === null) { self::$expressionBuilder = new ExpressionBuilder(); } return self::$expressionBuilder; } /** * Construct a new Criteria. * * @param Expression $expression * @param array|null $orderings * @param int|null $firstResult * @param int|null $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; } /** * Sets the where expression to evaluate when this Criteria is searched for. * * @param Expression $expression * * @return Criteria */ public function where(Expression $expression) { $this->expression = $expression; return $this; } /** * Appends the where expression to evaluate when this Criteria is searched for * using an AND with previous expression. * * @param Expression $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; } /** * Appends the where expression to evaluate when this Criteria is searched for * using an OR with previous expression. * * @param Expression $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; } /** * Gets the expression attached to this Criteria. * * @return Expression|null */ public function getWhereExpression() { return $this->expression; } /** * Gets the current orderings of this Criteria. * * @return array */ public function getOrderings() { return $this->orderings; } /** * Sets 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 $orderings * * @return Criteria */ public function orderBy(array $orderings) { $this->orderings = $orderings; return $this; } /** * Gets the current first result option of this Criteria. * * @return int|null */ public function getFirstResult() { return $this->firstResult; } /** * Set the number of first result that this Criteria should return. * * @param int|null $firstResult The value to set. * * @return Criteria */ public function setFirstResult($firstResult) { $this->firstResult = $firstResult; return $this; } /** * Gets maxResults. * * @return int|null */ public function getMaxResults() { return $this->maxResults; } /** * Sets maxResults. * * @param int|null $maxResults The value to set. * * @return Criteria */ public function setMaxResults($maxResults) { $this->maxResults = $maxResults; return $this; } } lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php000064400000016042144760113340021300 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 { /** * Accesses the field of a given object. This field has to be public * directly or indirectly (through an accessor get*, is*, or a magic * method, __get, __call). * * @param object $object * @param string $field * * @return mixed */ public static function getObjectFieldValue($object, $field) { $accessors = array('get', 'is'); foreach ($accessors as $accessor) { $accessor .= $field; if ( ! method_exists($object, $accessor)) { continue; } return $object->$accessor(); } // __call should be triggered for get. $accessor = $accessors[0] . $field; if (method_exists($object, '__call')) { return $object->$accessor(); } if ($object instanceof \ArrayAccess || is_array($object)) { 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 */ public static 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); }; case Comparison::CONTAINS: return function ($object) use ($field, $value) { return false !== strpos(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()); } } /** * @param array $expressions * * @return callable */ private function andExpressions($expressions) { return function ($object) use ($expressions) { foreach ($expressions as $expression) { if ( ! $expression($object)) { return false; } } return true; }; } /** * @param array $expressions * * @return callable */ 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.php000064400000004715144760113340016342 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'; const CONTAINS = 'CONTAINS'; /** * @var string */ private $field; /** * @var string */ private $op; /** * @var Value */ private $value; /** * @param string $field * @param string $operator * @param mixed $value */ public function __construct($field, $operator, $value) { if ( ! ($value instanceof Value)) { $value = new Value($value); } $this->field = $field; $this->op = $operator; $this->value = $value; } /** * @return string */ public function getField() { return $this->field; } /** * @return Value */ public function getValue() { return $this->value; } /** * @return string */ public function getOperator() { return $this->op; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkComparison($this); } } lib/Doctrine/Common/Collections/Expr/CompositeExpression.php000064400000004745144760113340020255 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'; /** * @var string */ private $type; /** * @var Expression[] */ private $expressions = array(); /** * @param string $type * @param array $expressions * * @throws \RuntimeException */ 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; } } /** * Returns the list of expressions nested in this composite. * * @return Expression[] */ public function getExpressionList() { return $this->expressions; } /** * @return string */ public function getType() { return $this->type; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkCompositeExpression($this); } } lib/Doctrine/Common/Collections/Expr/Expression.php000064400000002440144760113340016360 0ustar00. */ namespace Doctrine\Common\Collections\Expr; /** * Expression for the {@link Selectable} interface. * * @author Benjamin Eberlei */ interface Expression { /** * @param ExpressionVisitor $visitor * * @return mixed */ public function visit(ExpressionVisitor $visitor); } lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php000064400000005132144760113340017741 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 { /** * Converts a comparison expression into the target query language output. * * @param Comparison $comparison * * @return mixed */ abstract public function walkComparison(Comparison $comparison); /** * Converts a value expression into the target query language part. * * @param Value $value * * @return mixed */ abstract public function walkValue(Value $value); /** * Converts a composite expression into the target query language output. * * @param CompositeExpression $expr * * @return mixed */ abstract public function walkCompositeExpression(CompositeExpression $expr); /** * Dispatches walking an expression to the appropriate handler. * * @param Expression $expr * * @return mixed * * @throws \RuntimeException */ 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.php000064400000002746144760113340015306 0ustar00. */ namespace Doctrine\Common\Collections\Expr; class Value implements Expression { /** * @var mixed */ private $value; /** * @param mixed $value */ public function __construct($value) { $this->value = $value; } /** * @return mixed */ public function getValue() { return $this->value; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkValue($this); } } lib/Doctrine/Common/Collections/ExpressionBuilder.php000064400000010002144760113340016742 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 { /** * @param mixed $x * * @return CompositeExpression */ public function andX($x = null) { return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); } /** * @param mixed $x * * @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 * * @return Comparison */ public function isNull($field) { return new Comparison($field, Comparison::IS, new Value(null)); } /** * @param string $field * @param mixed $values * * @return Comparison */ public function in($field, array $values) { return new Comparison($field, Comparison::IN, new Value($values)); } /** * @param string $field * @param mixed $values * * @return Comparison */ public function notIn($field, array $values) { return new Comparison($field, Comparison::NIN, new Value($values)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function contains($field, $value) { return new Comparison($field, Comparison::CONTAINS, new Value($value)); } } lib/Doctrine/Common/Collections/Selectable.php000064400000003664144760113340015357 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 { /** * Selects all elements from a selectable that match the expression and * returns a new collection containing these elements. * * @param Criteria $criteria * * @return Collection */ function matching(Criteria $criteria); } phpunit.xml.dist000064400000001467144760113340007732 0ustar00 ./tests/Doctrine/ ./lib/Doctrine/ performance tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php000064400000017130144760113340022717 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 testGetObjectFieldValueIsAccessor() { $object = new TestObject(1, 2, true); $this->assertTrue($this->visitor->getObjectFieldValue($object, 'baz')); } public function testGetObjectFieldValueMagicCallMethod() { $object = new TestObject(1, 2, true, 3); $this->assertEquals(3, $this->visitor->getObjectFieldValue($object, 'qux')); } 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 testWalkContainsComparison() { $closure = $this->visitor->walkComparison($this->builder->contains('foo', 'hello')); $this->assertTrue($closure(new TestObject('hello world'))); $this->assertFalse($closure(new TestObject('world'))); } 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()); } public function testArrayComparison() { $closure = $this->visitor->walkComparison($this->builder->eq("foo", 42)); $this->assertTrue($closure(array('foo' => 42))); } } class TestObject { private $foo; private $bar; private $baz; private $qux; public function __construct($foo = null, $bar = null, $baz = null, $qux = null) { $this->foo = $foo; $this->bar = $bar; $this->baz = $baz; $this->qux = $qux; } public function __call($name, $arguments) { if ('getqux' === $name) { return $this->qux; } } public function getFoo() { return $this->foo; } public function getBar() { return $this->bar; } public function isBaz() { return $this->baz; } } tests/Doctrine/Tests/Common/Collections/CollectionTest.php000064400000016330144760113340017737 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.php000064400000005066144760113340017412 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.php000064400000007051144760113340021312 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()); } public function testContains() { $expr = $this->builder->contains("a", "b"); $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); $this->assertEquals(Comparison::CONTAINS, $expr->getOperator()); } } tests/Doctrine/Tests/DoctrineTestCase.php000064400000000244144760113340014476 0ustar00