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 20namespace Doctrine\Common\Collections; 21 22use ArrayIterator; 23use Closure; 24use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; 25 26/** 27 * An ArrayCollection is a Collection implementation that wraps a regular PHP array. 28 * 29 * Warning: Using (un-)serialize() on a collection is not a supported use-case 30 * and may break when we change the internals in the future. If you need to 31 * serialize a collection use {@link toArray()} and reconstruct the collection 32 * manually. 33 * 34 * @since 2.0 35 * @author Guilherme Blanco <guilhermeblanco@hotmail.com> 36 * @author Jonathan Wage <jonwage@gmail.com> 37 * @author Roman Borschel <roman@code-factory.org> 38 */ 39class ArrayCollection implements Collection, Selectable 40{ 41 /** 42 * An array containing the entries of this collection. 43 * 44 * @var array 45 */ 46 private $elements; 47 48 /** 49 * Initializes a new ArrayCollection. 50 * 51 * @param array $elements 52 */ 53 public function __construct(array $elements = array()) 54 { 55 $this->elements = $elements; 56 } 57 58 /** 59 * Creates a new instance from the specified elements. 60 * 61 * This method is provided for derived classes to specify how a new 62 * instance should be created when constructor semantics have changed. 63 * 64 * @param array $elements Elements. 65 * 66 * @return static 67 */ 68 protected function createFrom(array $elements) 69 { 70 return new static($elements); 71 } 72 73 /** 74 * {@inheritDoc} 75 */ 76 public function toArray() 77 { 78 return $this->elements; 79 } 80 81 /** 82 * {@inheritDoc} 83 */ 84 public function first() 85 { 86 return reset($this->elements); 87 } 88 89 /** 90 * {@inheritDoc} 91 */ 92 public function last() 93 { 94 return end($this->elements); 95 } 96 97 /** 98 * {@inheritDoc} 99 */ 100 public function key() 101 { 102 return key($this->elements); 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 public function next() 109 { 110 return next($this->elements); 111 } 112 113 /** 114 * {@inheritDoc} 115 */ 116 public function current() 117 { 118 return current($this->elements); 119 } 120 121 /** 122 * {@inheritDoc} 123 */ 124 public function remove($key) 125 { 126 if ( ! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { 127 return null; 128 } 129 130 $removed = $this->elements[$key]; 131 unset($this->elements[$key]); 132 133 return $removed; 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 public function removeElement($element) 140 { 141 $key = array_search($element, $this->elements, true); 142 143 if ($key === false) { 144 return false; 145 } 146 147 unset($this->elements[$key]); 148 149 return true; 150 } 151 152 /** 153 * Required by interface ArrayAccess. 154 * 155 * {@inheritDoc} 156 */ 157 public function offsetExists($offset) 158 { 159 return $this->containsKey($offset); 160 } 161 162 /** 163 * Required by interface ArrayAccess. 164 * 165 * {@inheritDoc} 166 */ 167 public function offsetGet($offset) 168 { 169 return $this->get($offset); 170 } 171 172 /** 173 * Required by interface ArrayAccess. 174 * 175 * {@inheritDoc} 176 */ 177 public function offsetSet($offset, $value) 178 { 179 if ( ! isset($offset)) { 180 return $this->add($value); 181 } 182 183 $this->set($offset, $value); 184 } 185 186 /** 187 * Required by interface ArrayAccess. 188 * 189 * {@inheritDoc} 190 */ 191 public function offsetUnset($offset) 192 { 193 return $this->remove($offset); 194 } 195 196 /** 197 * {@inheritDoc} 198 */ 199 public function containsKey($key) 200 { 201 return isset($this->elements[$key]) || array_key_exists($key, $this->elements); 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 public function contains($element) 208 { 209 return in_array($element, $this->elements, true); 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 public function exists(Closure $p) 216 { 217 foreach ($this->elements as $key => $element) { 218 if ($p($key, $element)) { 219 return true; 220 } 221 } 222 223 return false; 224 } 225 226 /** 227 * {@inheritDoc} 228 */ 229 public function indexOf($element) 230 { 231 return array_search($element, $this->elements, true); 232 } 233 234 /** 235 * {@inheritDoc} 236 */ 237 public function get($key) 238 { 239 return isset($this->elements[$key]) ? $this->elements[$key] : null; 240 } 241 242 /** 243 * {@inheritDoc} 244 */ 245 public function getKeys() 246 { 247 return array_keys($this->elements); 248 } 249 250 /** 251 * {@inheritDoc} 252 */ 253 public function getValues() 254 { 255 return array_values($this->elements); 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 public function count() 262 { 263 return count($this->elements); 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 public function set($key, $value) 270 { 271 $this->elements[$key] = $value; 272 } 273 274 /** 275 * {@inheritDoc} 276 */ 277 public function add($element) 278 { 279 $this->elements[] = $element; 280 281 return true; 282 } 283 284 /** 285 * {@inheritDoc} 286 */ 287 public function isEmpty() 288 { 289 return empty($this->elements); 290 } 291 292 /** 293 * Required by interface IteratorAggregate. 294 * 295 * {@inheritDoc} 296 */ 297 public function getIterator() 298 { 299 return new ArrayIterator($this->elements); 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 public function map(Closure $func) 306 { 307 return $this->createFrom(array_map($func, $this->elements)); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 public function filter(Closure $p) 314 { 315 return $this->createFrom(array_filter($this->elements, $p)); 316 } 317 318 /** 319 * {@inheritDoc} 320 */ 321 public function forAll(Closure $p) 322 { 323 foreach ($this->elements as $key => $element) { 324 if ( ! $p($key, $element)) { 325 return false; 326 } 327 } 328 329 return true; 330 } 331 332 /** 333 * {@inheritDoc} 334 */ 335 public function partition(Closure $p) 336 { 337 $matches = $noMatches = array(); 338 339 foreach ($this->elements as $key => $element) { 340 if ($p($key, $element)) { 341 $matches[$key] = $element; 342 } else { 343 $noMatches[$key] = $element; 344 } 345 } 346 347 return array($this->createFrom($matches), $this->createFrom($noMatches)); 348 } 349 350 /** 351 * Returns a string representation of this object. 352 * 353 * @return string 354 */ 355 public function __toString() 356 { 357 return __CLASS__ . '@' . spl_object_hash($this); 358 } 359 360 /** 361 * {@inheritDoc} 362 */ 363 public function clear() 364 { 365 $this->elements = array(); 366 } 367 368 /** 369 * {@inheritDoc} 370 */ 371 public function slice($offset, $length = null) 372 { 373 return array_slice($this->elements, $offset, $length, true); 374 } 375 376 /** 377 * {@inheritDoc} 378 */ 379 public function matching(Criteria $criteria) 380 { 381 $expr = $criteria->getWhereExpression(); 382 $filtered = $this->elements; 383 384 if ($expr) { 385 $visitor = new ClosureExpressionVisitor(); 386 $filter = $visitor->dispatch($expr); 387 $filtered = array_filter($filtered, $filter); 388 } 389 390 if ($orderings = $criteria->getOrderings()) { 391 $next = null; 392 foreach (array_reverse($orderings) as $field => $ordering) { 393 $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); 394 } 395 396 uasort($filtered, $next); 397 } 398 399 $offset = $criteria->getFirstResult(); 400 $length = $criteria->getMaxResults(); 401 402 if ($offset || $length) { 403 $filtered = array_slice($filtered, (int)$offset, $length); 404 } 405 406 return $this->createFrom($filtered); 407 } 408} 409