1 <?php
2
3 /**
4 * Validator class.
5 *
6 * @author gharlan
7 *
8 * @package redaxo\core
9 */
10 class rex_validator
11 {
12 use rex_factory_trait;
13
14 private $types = [];
15 private $message;
16
17 /**
18 * Constructor.
19 */
20 protected function __construct()
21 {
22 // noop
23 }
24
25 /**
26 * Factory method.
27 *
28 * @return static
29 */
30 public static function factory()
31 {
32 $class = static::getFactoryClass();
33 return new $class();
34 }
35
36 /**
37 * Adds a validator.
38 *
39 * @param string $type Validator type (any static method name of this class)
40 * @param null|string $message Message which is used if this validator type does not match
41 * @param mixed $option Type specific option
42 *
43 * @return $this
44 *
45 * @throws InvalidArgumentException
46 */
47 public function add($type, $message = null, $option = null)
48 {
49 if (!method_exists($this, $type)) {
50 throw new InvalidArgumentException('Unknown validator type: ' . $type);
51 }
52 $this->types[] = [$type, $message, $option];
53
54 return $this;
55 }
56
57 /**
58 * Checks whether the given value matches all added validators.
59 *
60 * @param string $value
61 *
62 * @return bool
63 */
64 public function isValid($value)
65 {
66 $this->message = null;
67 foreach ($this->types as $type) {
68 list($type, $message, $option) = $type;
69
70 if ($value === '') {
71 if (strtolower($type) !== 'notempty') {
72 continue;
73 }
74 }
75
76 if (!$this->$type($value, $option)) {
77 $this->message = $message;
78 return false;
79 }
80 }
81 return true;
82 }
83
84 /**
85 * Returns the message.
86 *
87 * @return string[]
88 */
89 public function getMessage()
90 {
91 return $this->message;
92 }
93
94 /**
95 * Checks whether the value is not empty.
96 *
97 * @param string $value
98 *
99 * @return bool
100 */
101 public function notEmpty($value)
102 {
103 return 0 !== strlen($value);
104 }
105
106 /**
107 * Checks whether the value is from the given type.
108 *
109 * @param string $value
110 * @param string $type
111 *
112 * @throws InvalidArgumentException
113 *
114 * @return bool
115 */
116 public function type($value, $type)
117 {
118 switch ($type) {
119 case 'int':
120 case 'integer':
121 return $this->match($value, '/^\d+$/');
122
123 case 'float':
124 case 'real':
125 return is_numeric($value);
126
127 default:
128 throw new InvalidArgumentException('Unknown $type:' . $type);
129 }
130 }
131
132 /**
133 * Checks whether the value has the given min length.
134 *
135 * @param string $value
136 * @param int $minLength
137 *
138 * @return bool
139 */
140 public function minLength($value, $minLength)
141 {
142 return mb_strlen($value) >= $minLength;
143 }
144
145 /**
146 * Checks whether the value has the given max value.
147 *
148 * @param string $value
149 * @param int $maxLength
150 *
151 * @return bool
152 */
153 public function maxLength($value, $maxLength)
154 {
155 return mb_strlen($value) <= $maxLength;
156 }
157
158 /**
159 * Checks whether the value is equal or greater than the given min value.
160 *
161 * @param string $value
162 * @param int $min
163 *
164 * @return bool
165 */
166 public function min($value, $min)
167 {
168 return $value >= $min;
169 }
170
171 /**
172 * Checks whether the value is equal or lower than the given max value.
173 *
174 * @param string $value
175 * @param int $max
176 *
177 * @return bool
178 */
179 public function max($value, $max)
180 {
181 return $value <= $max;
182 }
183
184 /**
185 * Checks whether the value is an URL.
186 *
187 * @param string $value
188 *
189 * @return bool
190 */
191 public function url($value)
192 {
193 return $this->match($value, '@^\w+://(?:[\w-]+\.)*[\w-]+(?::\d+)?(?:/.*)?$@u');
194 }
195
196 /**
197 * Checks whether the value is an email address.
198 *
199 * @param string $value
200 *
201 * @return bool
202 */
203 public function email($value)
204 {
205 return $this->match($value, '/^[\w.-]+@[\w.-]+\.[a-z]{2,}$/ui');
206 }
207
208 /**
209 * Checks whether the value matches the given regex.
210 *
211 * @param string $value
212 * @param string $regex
213 *
214 * @return bool
215 */
216 public function match($value, $regex)
217 {
218 return (bool) preg_match($regex, $value);
219 }
220
221 /**
222 * Checks whether the value does not match the given regex.
223 *
224 * @param string $value
225 * @param string $regex
226 *
227 * @return bool
228 */
229 public function notMatch($value, $regex)
230 {
231 return !$this->match($value, $regex);
232 }
233
234 /**
235 * Checks whether the value is one of the given valid values.
236 *
237 * @param string $value
238 * @param array $validValues
239 *
240 * @return bool
241 */
242 public function values($value, array $validValues)
243 {
244 return in_array($value, $validValues);
245 }
246
247 /**
248 * Checks the value by using the given callable.
249 *
250 * @param string $value
251 * @param callable $callback
252 *
253 * @return bool
254 */
255 public function custom($value, callable $callback)
256 {
257 return $callback($value);
258 }
259 }
260