Validator.php 7.11 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * 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 on 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 31
 * @since       1.0
 * @version     $Revision$
32
 * @author      Roman Borschel <roman@code-factory.org>
33 34
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 */
35
class Doctrine_Validator
36 37 38 39
{
    /**
     * @var array $validators           an array of validator objects
     */
40
    private static $validators = array();
41

42 43 44 45 46 47 48 49 50
    /**
     * returns a validator object
     *
     * @param string $name
     * @return Doctrine_Validator_Interface
     */
    public static function getValidator($name)
    {
        if ( ! isset(self::$validators[$name])) {
51
            $class = 'Doctrine_Validator_' . ucwords(strtolower($name));
52 53
            if (class_exists($class)) {
                self::$validators[$name] = new $class;
54 55
            } else if (class_exists($name)) {
                self::$validators[$name] = new $name;
56
            } else {
nicobn's avatar
nicobn committed
57
                throw new Doctrine_Exception("Validator named '$name' not available.");
58 59 60 61 62
            }

        }
        return self::$validators[$name];
    }
63

64 65 66 67 68 69 70 71 72
    /**
     * validates a given record and saves possible errors
     * in Doctrine_Validator::$stack
     *
     * @param Doctrine_Record $record
     * @return void
     */
    public function validateRecord(Doctrine_Record $record)
    {
73
        $classMetadata = $record->getTable();
74 75 76 77 78 79 80
        $columns   = $record->getTable()->getColumns();
        $component = $record->getTable()->getComponentName();

        $errorStack = $record->getErrorStack();

        // if record is transient all fields will be validated
        // if record is persistent only the modified fields will be validated
81 82 83
        $fields = ($record->exists()) ? $record->getModified() : $record->getData();
        $err = array();
        foreach ($fields as $fieldName => $value) {
84
            if ($value === Doctrine_Null::$INSTANCE) {
85
                $value = null;
86
            } else if ($value instanceof Doctrine_Record) {
87
                $value = $value->getIncremented();
zYne's avatar
zYne committed
88
            }
89 90
            
            $dataType = $classMetadata->getTypeOf($fieldName);
91

92 93 94 95 96 97 98 99 100 101
            // Validate field type, if type validation is enabled
            if ($classMetadata->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_TYPES) {
                if ( ! self::isValidType($value, $dataType)) {
                    $errorStack->add($fieldName, 'type');
                }
                if ($dataType == 'enum') {
                    $enumIndex = $classMetadata->enumIndex($fieldName, $value);
                    if ($enumIndex === false) {
                        $errorStack->add($fieldName, 'enum');
                    }
102 103
                }
            }
104 105
            
            // Validate field length, if length validation is enabled
106
            if ($record->getTable()->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_LENGTHS) {
107 108
                if ( ! $this->validateLength($value, $dataType, $classMetadata->getFieldLength($fieldName))) {
                    $errorStack->add($fieldName, 'length');
109 110 111
                }
            }

112 113 114 115 116
            // Run all custom validators
            foreach ($classMetadata->getFieldValidators($fieldName) as $validatorName => $args) {
                if ( ! is_string($validatorName)) {
                    $validatorName = $args;
                    $args = array();
117
                }
118
                $validator = self::getValidator($validatorName);
zYne's avatar
zYne committed
119
                $validator->invoker = $record;
120 121
                $validator->field = $fieldName;
                $validator->args = $args;
zYne's avatar
zYne committed
122
                if ( ! $validator->validate($value)) {
123
                    $errorStack->add($fieldName, $validatorName);
124 125 126 127
                }
            }
        }
    }
128

129
    /**
130
     * Validates the length of a field.
131
     */
132
    private function validateLength($value, $type, $maximumLength)
133
    {
134
        if ($type == 'timestamp' || $type == 'integer' || $type == 'enum') {
135
            return true;
136
        } else if ($type == 'array' || $type == 'object') {
137 138 139 140
            $length = strlen(serialize($value));
        } else {
            $length = strlen($value);
        }
141
        if ($length > $maximumLength) {
142 143 144 145
            return false;
        }
        return true;
    }
146

147 148 149 150 151 152 153 154
    /**
     * whether or not this validator has errors
     *
     * @return boolean
     */
    public function hasErrors()
    {
        return (count($this->stack) > 0);
155
    }    
156 157 158 159 160 161 162 163 164
    
    /**
     * returns whether or not the given variable is
     * valid type
     *
     * @param mixed $var
     * @param string $type
     * @return boolean
     */
romanb's avatar
romanb committed
165 166 167 168 169 170 171 172 173 174 175
     public static function isValidType($var, $type)
     {
         if ($var === null) {
             return true;
         } else if (is_object($var)) {
             return $type == 'object';
         }
     
         switch ($type) {
             case 'float':
             case 'double':
176
             case 'decimal':
177
                 return (string)$var == strval(floatval($var));
romanb's avatar
romanb committed
178
             case 'integer':
179
                 return (string)$var == strval(intval($var));
romanb's avatar
romanb committed
180 181
             case 'string':
                 return is_string($var) || is_int($var) || is_float($var);
182 183 184 185
             case 'blob':
             case 'clob':
             case 'gzip':
                 return is_string($var);
romanb's avatar
romanb committed
186 187 188 189 190 191 192
             case 'array':
                 return is_array($var);
             case 'object':
                 return is_object($var);
             case 'boolean':
                 return is_bool($var);
             case 'timestamp':
193 194 195 196 197
                 $validator = self::getValidator('timestamp');
                 return $validator->validate($var);
             case 'time':
                 $validator = self::getValidator('time');
                 return $validator->validate($var);
romanb's avatar
romanb committed
198 199 200 201 202 203 204 205 206
             case 'date':
                 $validator = self::getValidator('date');
                 return $validator->validate($var);
             case 'enum':
                 return is_string($var) || is_int($var);
             default:
                 return false;
         }
     }
207
}