Version 1
[yaffs-website] / vendor / doctrine / collections / lib / Doctrine / Common / Collections / Expr / ClosureExpressionVisitor.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\Common\Collections\Expr;
21
22 /**
23  * Walks an expression graph and turns it into a PHP closure.
24  *
25  * This closure can be used with {@Collection#filter()} and is used internally
26  * by {@ArrayCollection#select()}.
27  *
28  * @author Benjamin Eberlei <kontakt@beberlei.de>
29  * @since  2.3
30  */
31 class ClosureExpressionVisitor extends ExpressionVisitor
32 {
33     /**
34      * Accesses the field of a given object. This field has to be public
35      * directly or indirectly (through an accessor get*, is*, or a magic
36      * method, __get, __call).
37      *
38      * @param object $object
39      * @param string $field
40      *
41      * @return mixed
42      */
43     public static function getObjectFieldValue($object, $field)
44     {
45         if (is_array($object)) {
46             return $object[$field];
47         }
48
49         $accessors = array('get', 'is');
50
51         foreach ($accessors as $accessor) {
52             $accessor .= $field;
53
54             if ( ! method_exists($object, $accessor)) {
55                 continue;
56             }
57
58             return $object->$accessor();
59         }
60
61         // __call should be triggered for get.
62         $accessor = $accessors[0] . $field;
63
64         if (method_exists($object, '__call')) {
65             return $object->$accessor();
66         }
67
68         if ($object instanceof \ArrayAccess) {
69             return $object[$field];
70         }
71
72         return $object->$field;
73     }
74
75     /**
76      * Helper for sorting arrays of objects based on multiple fields + orientations.
77      *
78      * @param string   $name
79      * @param int      $orientation
80      * @param \Closure $next
81      *
82      * @return \Closure
83      */
84     public static function sortByField($name, $orientation = 1, \Closure $next = null)
85     {
86         if ( ! $next) {
87             $next = function() {
88                 return 0;
89             };
90         }
91
92         return function ($a, $b) use ($name, $next, $orientation) {
93             $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
94             $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);
95
96             if ($aValue === $bValue) {
97                 return $next($a, $b);
98             }
99
100             return (($aValue > $bValue) ? 1 : -1) * $orientation;
101         };
102     }
103
104     /**
105      * {@inheritDoc}
106      */
107     public function walkComparison(Comparison $comparison)
108     {
109         $field = $comparison->getField();
110         $value = $comparison->getValue()->getValue(); // shortcut for walkValue()
111
112         switch ($comparison->getOperator()) {
113             case Comparison::EQ:
114                 return function ($object) use ($field, $value) {
115                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
116                 };
117
118             case Comparison::NEQ:
119                 return function ($object) use ($field, $value) {
120                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
121                 };
122
123             case Comparison::LT:
124                 return function ($object) use ($field, $value) {
125                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
126                 };
127
128             case Comparison::LTE:
129                 return function ($object) use ($field, $value) {
130                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
131                 };
132
133             case Comparison::GT:
134                 return function ($object) use ($field, $value) {
135                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
136                 };
137
138             case Comparison::GTE:
139                 return function ($object) use ($field, $value) {
140                     return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
141                 };
142
143             case Comparison::IN:
144                 return function ($object) use ($field, $value) {
145                     return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
146                 };
147
148             case Comparison::NIN:
149                 return function ($object) use ($field, $value) {
150                     return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
151                 };
152
153             case Comparison::CONTAINS:
154                 return function ($object) use ($field, $value) {
155                     return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
156                 };
157
158             default:
159                 throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
160         }
161     }
162
163     /**
164      * {@inheritDoc}
165      */
166     public function walkValue(Value $value)
167     {
168         return $value->getValue();
169     }
170
171     /**
172      * {@inheritDoc}
173      */
174     public function walkCompositeExpression(CompositeExpression $expr)
175     {
176         $expressionList = array();
177
178         foreach ($expr->getExpressionList() as $child) {
179             $expressionList[] = $this->dispatch($child);
180         }
181
182         switch($expr->getType()) {
183             case CompositeExpression::TYPE_AND:
184                 return $this->andExpressions($expressionList);
185
186             case CompositeExpression::TYPE_OR:
187                 return $this->orExpressions($expressionList);
188
189             default:
190                 throw new \RuntimeException("Unknown composite " . $expr->getType());
191         }
192     }
193
194     /**
195      * @param array $expressions
196      *
197      * @return callable
198      */
199     private function andExpressions($expressions)
200     {
201         return function ($object) use ($expressions) {
202             foreach ($expressions as $expression) {
203                 if ( ! $expression($object)) {
204                     return false;
205                 }
206             }
207             return true;
208         };
209     }
210
211     /**
212      * @param array $expressions
213      *
214      * @return callable
215      */
216     private function orExpressions($expressions)
217     {
218         return function ($object) use ($expressions) {
219             foreach ($expressions as $expression) {
220                 if ($expression($object)) {
221                     return true;
222                 }
223             }
224             return false;
225         };
226     }
227 }