# Coverage for Doctrine_Validator

Back to coverage report

1 | <?php
2 | /*
3 | * $Id: Validator.php 2965 2007-10-21 08:16:31Z romanb $
4 | *
5 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 | *
17 | * This software consists of voluntary contributions made by many individuals
18 | * and is licensed under the LGPL. For more information, see
19 | * <http://www.phpdoctrine.org>.
20 | */
21 |
22 | /**
23 | * Doctrine_Validator
24 | * Doctrine_Validator performs validations in record properties
25 | *
26 | * @package Doctrine
27 | * @subpackage Validator
28 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
29 | * @link www.phpdoctrine.org
30 | * @since 1.0
31 | * @version $Revision: 2965 $
32 | * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
33 | */
34 | class Doctrine_Validator extends Doctrine_Locator_Injectable
35 | {
36 | /**
37 | * @var array $validators an array of validator objects
38 | */
39 | private static $validators = array();
40 |
41 | /**
42 | * returns a validator object
43 | *
44 | * @param string $name
45 | * @return Doctrine_Validator_Interface
46 | */
47 | public static function getValidator($name)
48 | {
49 | if ( ! isset(self::$validators[$name])) {
50 | $class = 'Doctrine_Validator_' . ucwords(strtolower($name));
51 | if (class_exists($class)) {
52 | self::$validators[$name] = new $class;
53 | } else {
54 | throw new Doctrine_Exception("Validator named '$name' not available.");
55 | }
56 |
57 | }
58 | return self::$validators[$name];
59 | }
60 |
61 | /**
62 | * validates a given record and saves possible errors
63 | * in Doctrine_Validator::$stack
64 | *
65 | * @param Doctrine_Record $record
66 | * @return void
67 | */
68 | public function validateRecord(Doctrine_Record $record)
69 | {
70 | $columns = $record->getTable()->getColumns();
71 | $component = $record->getTable()->getComponentName();
72 |
73 | $errorStack = $record->getErrorStack();
74 |
75 | // if record is transient all fields will be validated
76 | // if record is persistent only the modified fields will be validated
77 | $data = ($record->exists()) ? $record->getModified() : $record->getData();
78 |
79 | $err = array();
80 | foreach ($data as $key => $value) {
81 | if ($value === self::$_null) {
82 | $value = null;
83 | } else if ($value instanceof Doctrine_Record) {
84 | $value = $value->getIncremented();
85 | }
86 |
87 | $column = $columns[$key];
88 |
89 | if ($column['type'] == 'enum') {
90 | $value = $record->getTable()->enumIndex($key, $value);
91 |
92 | if ($value === false) {
93 | $errorStack->add($key, 'enum');
94 | continue;
95 | }
96 | }
97 |
98 | if ($record->getTable()->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_LENGTHS) {
99 | if ( ! $this->validateLength($column, $key, $value)) {
100 | $errorStack->add($key, 'length');
101 |
102 | continue;
103 | }
104 | }
105 |
106 | foreach ($column as $name => $args) {
107 | if (empty($name)
108 | || $name == 'primary'
109 | || $name == 'protected'
110 | || $name == 'autoincrement'
111 | || $name == 'default'
112 | || $name == 'values'
113 | || $name == 'sequence'
114 | || $name == 'zerofill'
115 | || $name == 'scale') {
116 | continue;
117 | }
118 |
119 | if (strtolower($name) === 'notnull' && isset($column['autoincrement'])) {
120 | continue;
121 | }
122 |
123 | if (strtolower($name) == 'length') {
124 | if ( ! ($record->getTable()->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_LENGTHS)) {
125 | if ( ! $this->validateLength($column, $key, $value)) {
126 | $errorStack->add($key, 'length');
127 | }
128 | }
129 | continue;
130 | }
131 |
132 | if (strtolower($name) == 'type') {
133 | if ( ! ($record->getTable()->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_TYPES)) {
134 | if ( ! self::isValidType($value, $column['type'])) {
135 | $errorStack->add($key, 'type');
136 | }
137 | }
138 | continue;
139 | }
140 |
141 | $validator = self::getValidator($name);
142 | $validator->invoker = $record;
143 | $validator->field = $key;
144 | $validator->args = $args;
145 |
146 | if ( ! $validator->validate($value)) {
147 | $errorStack->add($key, $name);
148 |
149 | //$err[$key] = 'not valid';
150 |
151 | // errors found quit validation looping for this column
152 | //break;
153 | }
154 | }
155 |
156 | if ($record->getTable()->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_TYPES) {
157 | if ( ! self::isValidType($value, $column['type'])) {
158 | $errorStack->add($key, 'type');
159 | continue;
160 | }
161 | }
162 | }
163 | }
164 |
165 | /**
166 | * Validates the length of a field.
167 | */
168 | private function validateLength($column, $key, $value)
169 | {
170 | if ($column['type'] == 'timestamp' || $column['type'] == 'integer' ||
171 | $column['type'] == 'enum') {
172 | return true;
173 | } else if ($column['type'] == 'array' || $column['type'] == 'object') {
174 | $length = strlen(serialize($value));
175 | } else {
176 | $length = strlen($value);
177 | }
178 |
179 | if ($length > $column['length']) {
180 | return false;
181 | }
182 | return true;
183 | }
184 |
185 | /**
186 | * whether or not this validator has errors
187 | *
188 | * @return boolean
189 | */
190 | public function hasErrors()
191 | {
192 | return (count($this->stack) > 0);
193 | }
194 |
195 | /**
196 | * phpType
197 | * converts a doctrine type to native php type
198 | *
199 | * @param $portableType portable doctrine type
200 | * @return string
201 | *//*
202 | public static function phpType($portableType)
203 | {
204 | switch ($portableType) {
205 | case 'enum':
206 | return 'integer';
207 | case 'blob':
208 | case 'clob':
209 | case 'mbstring':
210 | case 'timestamp':
211 | case 'date':
212 | case 'gzip':
213 | return 'string';
214 | break;
215 | default:
216 | return $portableType;
217 | }
218 | }*/
219 | /**
220 | * returns whether or not the given variable is
221 | * valid type
222 | *
223 | * @param mixed $var
224 | * @param string $type
225 | * @return boolean
226 | */
227 | /*
228 | public static function isValidType($var, $type)
229 | {
230 | if ($type == 'boolean') {
231 | return true;
232 | }
233 |
234 | $looseType = self::gettype($var);
235 | $type = self::phpType($type);
236 |
237 | switch ($looseType) {
238 | case 'float':
239 | case 'double':
240 | case 'integer':
241 | if ($type == 'string' || $type == 'float') {
242 | return true;
243 | }
244 | case 'string':
245 | case 'array':
246 | case 'object':
247 | return ($type === $looseType);
248 | break;
249 | case 'NULL':
250 | return true;
251 | break;
252 | }
253 | }*/
254 |
255 |
256 | /**
257 | * returns whether or not the given variable is
258 | * valid type
259 | *
260 | * @param mixed $var
261 | * @param string $type
262 | * @return boolean
263 | */
264 | public static function isValidType($var, $type)
265 | {
266 | if ($var === null) {
267 | return true;
268 | } else if (is_object($var)) {
269 | return $type == 'object';
270 | }
271 |
272 | switch ($type) {
273 | case 'float':
274 | case 'double':
275 | return (String)$var == strval(floatval($var));
276 | case 'integer':
277 | return (String)$var == strval(intval($var));
278 | case 'string':
279 | return is_string($var) || is_int($var) || is_float($var);
280 | case 'blob':
281 | case 'clob':
282 | case 'gzip':
283 | return is_string($var);
284 | case 'array':
285 | return is_array($var);
286 | case 'object':
287 | return is_object($var);
288 | case 'boolean':
289 | return is_bool($var);
290 | case 'timestamp':
291 | // todo: validate the timestamp is in YYYY-MM-DD HH:MM:SS format
292 | return true;
293 | case 'date':
294 | $validator = self::getValidator('date');
295 | return $validator->validate($var);
296 | case 'enum':
297 | return is_string($var) || is_int($var);
298 | default:
299 | return false;
300 | }
301 | }
302 |
303 |
304 | /**
305 | * returns the type of loosely typed variable
306 | *
307 | * @param mixed $var
308 | * @return string
309 | *//*
310 | public static function gettype($var)
311 | {
312 | $type = gettype($var);
313 | switch ($type) {
314 | case 'string':
315 | if (preg_match("/^[0-9]+$/",$var)) {
316 | return 'integer';
317 | } elseif (is_numeric($var)) {
318 | return 'float';
319 | } else {
320 | return $type;
321 | }
322 | break;
323 | default:
324 | return $type;
325 | }
326 | }*/
327 | }