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 }