9630fa23f49fe4a12328f1ec4888f32152fdab13
[m6w6/ascertain] / src / Assert.php
1 <?php
2
3 namespace ascertain;
4
5 /**
6 * Be sure to always pass an error string as last argument to the assertion:
7 * <code>
8 * $testable->assert()
9 * ->that("name")->isLen(3, 40, "must be between 3 and 40 characters long")
10 * ->that("email")->isEmail("is not valid");
11 * </code>
12 */
13 class Assert
14 {
15 /**
16 * Testable properties
17 * @var array
18 */
19 private $properties;
20
21 /**
22 * The name of the currently inspected property
23 * @var string
24 */
25 private $inspectedProperty;
26
27 /**
28 * Whether the argument to when() was true
29 * @var bool
30 */
31 private $inspectCondition = true;
32
33 /**
34 * Whether assertions cause exceptions of this type
35 * @var string
36 */
37 private $exceptionClass;
38
39 /**
40 * Failed assertions
41 * @var array
42 */
43 private $validationErrors = array();
44
45 /**
46 * @param \ascertain\Testable $testable
47 * @param string $exceptionClass
48 */
49 function __construct(Testable $testable, $exceptionClass = null) {
50 $this->properties = $testable->export();
51 $this->exceptionClass = $exceptionClass;
52 }
53
54 /**
55 * @return int count of failed assertions
56 */
57 function hasErrors() {
58 return count($this->validationErrors);
59 }
60
61 /**
62 * @return array of failed assertions
63 */
64 function getErrors() {
65 return $this->validationErrors;
66 }
67
68 /**
69 * Reset failed assertions
70 * @return \ascertain\Assert
71 */
72 function resetErrors() {
73 $this->validationErrors = array();
74 return $this;
75 }
76
77 /**
78 * Set the currently inspected property
79 * @param string $property
80 * @return \ascertain\Assert
81 */
82 function that($property) {
83 $this->inspectCondition = true;
84 $this->inspectedProperty = $property;
85 return $this;
86 }
87
88 /**
89 * The following assertions are only tested if the argument is true
90 * @param bool $condition
91 * @return \ascertain\Assert
92 */
93 function when($condition) {
94 $this->inspectCondition = $condition;
95 return $this;
96 }
97
98 /**
99 * !strcmp($v, $c)
100 * @param string $v
101 * @param string $c
102 * @return bool
103 */
104 function test($v, $c) {
105 return !strcmp($v, $c);
106 }
107
108 /**
109 * !strlen($v)
110 * @param string $v
111 * @return bool
112 */
113 function testNothing($v) {
114 return !strlen($v);
115 }
116
117 /**
118 * is_numeric($v)
119 * @param string $v
120 * @return bool
121 */
122 function testNumeric($v) {
123 return is_numeric($v);
124 }
125
126 /**
127 * Test wheter string representations of original and the int-cast equal
128 * @param mixed $v
129 * @return bool
130 */
131 function testInteger($v) {
132 return ((string) $v) === ((string)(int) $v);
133 }
134
135 /**
136 * Test whether the argument is scalar
137 * @param mixed $v
138 * @param bool $strictNulls
139 * @return bool
140 */
141 function testScalar($v, $strictNulls = false) {
142 return is_scalar($v) && (!$strictNulls || !isset($v));
143 }
144
145 /**
146 * Test wheter the argument constists only of printable characters
147 * @param string $v
148 * @return bool
149 */
150 function testPrintable($v) {
151 return preg_match("/^[[:print:]\\P{Cc}]*\$/u", $v) > 0;
152 }
153
154 /**
155 * Test wheter the string length is between a certain range
156 * @param string $v
157 * @param int $min
158 * @param int $max
159 * @return bool
160 */
161 function testLen($v, $min, $max) {
162 return $this->testRange(function_exists("mb_strlen") ? mb_strlen($v) : strlen($v), $min, $max);
163 }
164
165 /**
166 * Test wheter a value is between a certain range
167 * @param mixed $v
168 * @param mixed $min
169 * @param mixed $max
170 * @return bool
171 */
172 function testRange($v, $min, $max) {
173 return $v >= $min && $v <= $max;
174 }
175
176 /**
177 * Test for a valid email address with FILTER_VALIDATE_EMAIL
178 * @param stting $v
179 * @param int $options
180 * @return bool
181 */
182 function testEmail($v, $options = null) {
183 return filter_var($v, FILTER_VALIDATE_EMAIL, $options) !== false;
184 }
185
186 /**
187 * Test for a valid URL with FILTER_VALIDATE_URL
188 * @param string $v
189 * @param int $options
190 * @return bool
191 */
192 function testUrl($v, $options = null) {
193 return filter_var($v, FILTER_VALIDATE_URL, $options) !== false;
194 }
195
196 /**
197 * Test whether a string contains another string
198 * @param type $v haystack
199 * @param type $n needle
200 * @param bool $ci case-sensitive
201 * @return bool
202 */
203 function testContains($v, $n, $ci = true) {
204 return ($ci ? strpos($v, $n) : stripos($v, $n)) !== false;
205 }
206
207 /**
208 * Thest if a regular expression matches
209 * @param string $v
210 * @param stirng $regex
211 * @param int $flags
212 * @return int
213 */
214 function testRegex($v, $regex, $flags = 0) {
215 return preg_match($regex, $v, null, $flags);
216 }
217
218 /**
219 * Semantic is(Not) wrapper to the assertions
220 * @param string $method
221 * @param array $args
222 * @return \ascertain\Assert
223 * @throws InvalidArgumentException (or rahter the configured exception)
224 */
225 function __call($method, $args) {
226 if ($this->inspectCondition && preg_match("/^is(Not)?(.*)\$/i", $method, $match)) {
227 list(, $not, $test) = $match;
228
229 $error = array_pop($args);
230 array_unshift($args, $this->properties[$this->inspectedProperty]);
231 if (call_user_func_array(array($this, "test$test"), $args) == !!$not) {
232 if (($exception = $this->exceptionClass)) {
233 throw new $exception("$this->inspectedProperty $error");
234 }
235 $this->validationErrors[$this->inspectedProperty][] = $error;
236 }
237 }
238 return $this;
239 }
240
241 }