1<?php 2/* 3 * This file is part of PHPUnit. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11class PHPUnit_Framework_Constraint_Count extends PHPUnit_Framework_Constraint 12{ 13 /** 14 * @var int 15 */ 16 protected $expectedCount = 0; 17 18 /** 19 * @param int $expected 20 */ 21 public function __construct($expected) 22 { 23 parent::__construct(); 24 $this->expectedCount = $expected; 25 } 26 27 /** 28 * Evaluates the constraint for parameter $other. Returns true if the 29 * constraint is met, false otherwise. 30 * 31 * @param mixed $other 32 * 33 * @return bool 34 */ 35 protected function matches($other) 36 { 37 return $this->expectedCount === $this->getCountOf($other); 38 } 39 40 /** 41 * @param mixed $other 42 * 43 * @return bool 44 */ 45 protected function getCountOf($other) 46 { 47 if ($other instanceof Countable || is_array($other)) { 48 return count($other); 49 } elseif ($other instanceof Traversable) { 50 if ($other instanceof IteratorAggregate) { 51 $iterator = $other->getIterator(); 52 } else { 53 $iterator = $other; 54 } 55 56 if ($iterator instanceof Generator) { 57 return $this->getCountOfGenerator($iterator); 58 } 59 60 $key = $iterator->key(); 61 $count = iterator_count($iterator); 62 63 // Manually rewind $iterator to previous key, since iterator_count 64 // moves pointer. 65 if ($key !== null) { 66 $iterator->rewind(); 67 while ($iterator->valid() && $key !== $iterator->key()) { 68 $iterator->next(); 69 } 70 } 71 72 return $count; 73 } 74 } 75 76 /** 77 * Returns the total number of iterations from a generator. 78 * This will fully exhaust the generator. 79 * 80 * @param Generator $generator 81 * 82 * @return int 83 */ 84 protected function getCountOfGenerator(Generator $generator) 85 { 86 for ($count = 0; $generator->valid(); $generator->next()) { 87 $count += 1; 88 } 89 90 return $count; 91 } 92 93 /** 94 * Returns the description of the failure. 95 * 96 * The beginning of failure messages is "Failed asserting that" in most 97 * cases. This method should return the second part of that sentence. 98 * 99 * @param mixed $other Evaluated value or object. 100 * 101 * @return string 102 */ 103 protected function failureDescription($other) 104 { 105 return sprintf( 106 'actual size %d matches expected size %d', 107 $this->getCountOf($other), 108 $this->expectedCount 109 ); 110 } 111 112 /** 113 * @return string 114 */ 115 public function toString() 116 { 117 return sprintf( 118 'count matches %d', 119 $this->expectedCount 120 ); 121 } 122} 123