Вход Регистрация
Файл: onlinepoisk.wm-scripts.ru/vendor/AR/lib/Validations.php
Строк: 847
<?php
/**
 * These two classes have been <i>heavily borrowed</i> from Ruby on Rails' ActiveRecord so much that
 * this piece can be considered a straight port. The reason for this is that the vaildation process is
 * tricky due to order of operations/events. The former combined with PHP's odd typecasting means
 * that it was easier to formulate this piece base on the rails code.
 *
 * @package ActiveRecord
 */

namespace ActiveRecord;
use 
ActiveRecordModel;
use 
IteratorAggregate;
use 
ArrayIterator;

/**
 * Manages validations for a {@link Model}.
 *
 * This class isn't meant to be directly used. Instead you define
 * validators thru static variables in your {@link Model}. Example:
 *
 * <code>
 * class Person extends ActiveRecordModel {
 *   static $validates_length_of = array(
 *     array('name', 'within' => array(30,100),
 *     array('state', 'is' => 2)
 *   );
 * }
 *
 * $person = new Person();
 * $person->name = 'Tito';
 * $person->state = 'this is not two characters';
 *
 * if (!$person->is_valid())
 *   print_r($person->errors);
 * </code>
 *
 * @package ActiveRecord
 * @see Errors
 * @link http://www.phpactiverecord.org/guides/validations
 */
class Validations
{
    private 
$model;
    private 
$options = array();
    private 
$validators = array();
    private 
$record;

    private static 
$VALIDATION_FUNCTIONS = array(
        
'validates_presence_of',
        
'validates_size_of',
        
'validates_length_of',
        
'validates_inclusion_of',
        
'validates_exclusion_of',
        
'validates_format_of',
        
'validates_numericality_of',
        
'validates_uniqueness_of'
    
);

    private static 
$DEFAULT_VALIDATION_OPTIONS = array(
        
'on' => 'save',
        
'allow_null' => false,
        
'allow_blank' => false,
        
'message' => null,
    );

    private static  
$ALL_RANGE_OPTIONS = array(
        
'is' => null,
        
'within' => null,
        
'in' => null,
        
'minimum' => null,
        
'maximum' => null,
    );

    private static 
$ALL_NUMERICALITY_CHECKS = array(
        
'greater_than' => null,
        
'greater_than_or_equal_to'  => null,
        
'equal_to' => null,
        
'less_than' => null,
        
'less_than_or_equal_to' => null,
        
'odd' => null,
        
'even' => null
    
);

    
/**
     * Constructs a {@link Validations} object.
     *
     * @param Model $model The model to validate
     * @return Validations
     */
    
public function __construct(Model $model)
    {
        
$this->model $model;
        
$this->record = new Errors($this->model);
        
$this->validators array_intersect(array_keys(Reflections::instance()->get(get_class($this->model))->getStaticProperties()), self::$VALIDATION_FUNCTIONS);
    }

    
/**
     * Returns validator data.
     *
     * @return array
     */
    
public function rules()
    {
        
$data = array();
        
$reflection Reflections::instance()->get(get_class($this->model));

        foreach (
$this->validators as $validate)
        {
            
$attrs $reflection->getStaticPropertyValue($validate);

            foreach (
$attrs as $attr)
            {
                
$field $attr[0];

                if (!isset(
$data[$field]) || !is_array($data[$field]))
                    
$data[$field] = array();

                
$attr['validator'] = $validate;
                unset(
$attr[0]);
                
array_push($data[$field],$attr);
            }
        }
        return 
$data;
    }

    
/**
     * Runs the validators.
     *
     * @return Errors the validation errors if any
     */
    
public function validate()
    {
        
$reflection Reflections::instance()->get(get_class($this->model));

        foreach (
$this->validators as $validate)
            
$this->$validate($reflection->getStaticPropertyValue($validate));

        
$this->record->clear_model();
        return 
$this->record;
    }

    
/**
     * Validates a field is not null and not blank.
     *
     * <code>
     * class Person extends ActiveRecordModel {
     *   static $validates_presence_of = array(
     *     array('first_name'),
     *     array('last_name')
     *   );
     * }
     * </code>
     *
     * Available options:
     *
     * <ul>
     * <li><b>message:</b> custom error message</li>
     * </ul>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_presence_of($attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['blank'], 'on' => 'save'));

        foreach (
$attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$this->record->add_on_blank($options[0], $options['message']);
        }
    }

    
/**
     * Validates that a value is included the specified array.
     *
     * <code>
     * class Car extends ActiveRecordModel {
     *   static $validates_inclusion_of = array(
     *     array('fuel_type', 'in' => array('hyrdogen', 'petroleum', 'electric')),
     *   );
     * }
     * </code>
     *
     * Available options:
     *
     * <ul>
     * <li><b>in/within:</b> attribute should/shouldn't be a value within an array</li>
     * <li><b>message:</b> custome error message</li>
     * </ul>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_inclusion_of($attrs)
    {
        
$this->validates_inclusion_or_exclusion_of('inclusion'$attrs);
    }

    
/**
     * This is the opposite of {@link validates_include_of}.
     *
     * @param array $attrs Validation definition
     * @see validates_inclusion_of
     */
    
public function validates_exclusion_of($attrs)
    {
        
$this->validates_inclusion_or_exclusion_of('exclusion'$attrs);
    }

    
/**
     * Validates that a value is in or out of a specified list of values.
     *
     * @see validates_inclusion_of
     * @see validates_exclusion_of
     * @param string $type Either inclusion or exclusion
     * @param $attrs Validation definition
     */
    
public function validates_inclusion_or_exclusion_of($type$attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES[$type], 'on' => 'save'));

        foreach (
$attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$attribute $options[0];
            
$var $this->model->$attribute;

            if (isset(
$options['in']))
                
$enum $options['in'];
            elseif (isset(
$options['within']))
                
$enum $options['within'];

            if (!
is_array($enum))
                array(
$enum);

            
$message str_replace('%s'$var$options['message']);

            if (
$this->is_null_with_option($var$options) || $this->is_blank_with_option($var$options))
                continue;

            if ((
'inclusion' == $type && !in_array($var$enum)) || ('exclusion' == $type && in_array($var$enum)))
                
$this->record->add($attribute$message);
        }
    }

    
/**
     * Validates that a value is numeric.
     *
     * <code>
     * class Person extends ActiveRecordModel {
     *   static $validates_numericality_of = array(
     *     array('salary', 'greater_than' => 19.99, 'less_than' => 99.99)
     *   );
     * }
     * </code>
     *
     * Available options:
     *
     * <ul>
     * <li><b>only_integer:</b> value must be an integer (e.g. not a float)</li>
     * <li><b>even:</b> must be even</li>
     * <li><b>odd:</b> must be odd"</li>
     * <li><b>greater_than:</b> must be greater than specified number</li>
     * <li><b>greater_than_or_equal_to:</b> must be greater than or equal to specified number</li>
     * <li><b>equal_to:</b> ...</li>
     * <li><b>less_than:</b> ...</li>
     * <li><b>less_than_or_equal_to:</b> ...</li>
     * </ul>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_numericality_of($attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('only_integer' => false));

        
// Notice that for fixnum and float columns empty strings are converted to nil.
        // Validates whether the value of the specified attribute is numeric by trying to convert it to a float with Kernel.Float
        // (if only_integer is false) or applying it to the regular expression /A[+-]?d+Z/ (if only_integer is set to true).
        
foreach ($attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$attribute $options[0];
            
$var $this->model->$attribute;

            
$numericalityOptions array_intersect_key(self::$ALL_NUMERICALITY_CHECKS$options);

            if (
$this->is_null_with_option($var$options))
                continue;

            if (
true === $options['only_integer'] && !is_integer($var))
            {
                if (!
preg_match('/A[+-]?d+Z/', (string)($var)))
                {
                    if (isset(
$options['message']))
                        
$message $options['message'];
                    else
                        
$message Errors::$DEFAULT_ERROR_MESSAGES['not_a_number'];

                    
$this->record->add($attribute$message);
                    continue;
                }
            }
            else
            {
                if (!
is_numeric($var))
                {
                    
$this->record->add($attributeErrors::$DEFAULT_ERROR_MESSAGES['not_a_number']);
                    continue;
                }

                
$var = (float)$var;
            }

            foreach (
$numericalityOptions as $option => $check)
            {
                
$option_value $options[$option];

                if (
'odd' != $option && 'even' != $option)
                {
                    
$option_value = (float)$options[$option];

                    if (!
is_numeric($option_value))
                        throw new  
ValidationsArgumentError("$option must be a number");

                    if (isset(
$options['message']))
                        
$message $options['message'];
                    else
                        
$message Errors::$DEFAULT_ERROR_MESSAGES[$option];

                    
$message str_replace('%d'$option_value$message);

                    if (
'greater_than' == $option && !($var $option_value))
                        
$this->record->add($attribute$message);

                    elseif (
'greater_than_or_equal_to' == $option && !($var >= $option_value))
                        
$this->record->add($attribute$message);

                    elseif (
'equal_to' == $option && !($var == $option_value))
                        
$this->record->add($attribute$message);

                    elseif (
'less_than' == $option && !($var $option_value))
                        
$this->record->add($attribute$message);

                    elseif (
'less_than_or_equal_to' == $option && !($var <= $option_value))
                        
$this->record->add($attribute$message);
                }
                else
                {
                    if (isset(
$options['message']))
                        
$message $options['message'];
                    else
                        
$message Errors::$DEFAULT_ERROR_MESSAGES[$option];

                    if ( (
'odd' == $option && !( Utils::is_odd($var))) || ('even' == $option && ( Utils::is_odd($var))))
                        
$this->record->add($attribute$message);
                }
            }
        }
    }

    
/**
     * Alias of {@link validates_length_of}
     *
     * @param array $attrs Validation definition
     */
    
public function validates_size_of($attrs)
    {
        
$this->validates_length_of($attrs);
    }

    
/**
     * Validates that a value is matches a regex.
     *
     * <code>
     * class Person extends ActiveRecordModel {
     *   static $validates_format_of = array(
     *     array('email', 'with' => '/^.*?@.*$/')
     *   );
     * }
     * </code>
     *
     * Available options:
     *
     * <ul>
     * <li><b>with:</b> a regular expression</li>
     * <li><b>message:</b> custom error message</li>
     * </ul>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_format_of($attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'], 'on' => 'save''with' => null));

        foreach (
$attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$attribute $options[0];
            
$var $this->model->$attribute;

            if (
is_null($options['with']) || !is_string($options['with']) || !is_string($options['with']))
                throw new 
ValidationsArgumentError('A regular expression must be supplied as the [with] option of the configuration array.');
            else
                
$expression $options['with'];

            if (
$this->is_null_with_option($var$options) || $this->is_blank_with_option($var$options))
                continue;

            if (!@
preg_match($expression$var))
            
$this->record->add($attribute$options['message']);
        }
    }

    
/**
     * Validates the length of a value.
     *
     * <code>
     * class Person extends ActiveRecordModel {
     *   static $validates_length_of = array(
     *     array('name', 'within' => array(1,50))
     *   );
     * }
     * </code>
     *
     * Available options:
     *
     * <ul>
     * <li><b>is:</b> attribute should be exactly n characters long</li>
     * <li><b>in/within:</b> attribute should be within an range array(min,max)</li>
     * <li><b>maximum/minimum:</b> attribute should not be above/below respectively</li>
     * </ul>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_length_of($attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
            
'too_long'     => Errors::$DEFAULT_ERROR_MESSAGES['too_long'],
            
'too_short'    => Errors::$DEFAULT_ERROR_MESSAGES['too_short'],
            
'wrong_length' => Errors::$DEFAULT_ERROR_MESSAGES['wrong_length']
        ));

        foreach (
$attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$range_options array_intersect(array_keys(self::$ALL_RANGE_OPTIONS), array_keys($attr));
            
sort($range_options);

            switch (
sizeof($range_options))
            {
                case 
0:
                    throw new  
ValidationsArgumentError('Range unspecified.  Specify the [within], [maximum], or [is] option.');

                case 
1:
                    break;

                default:
                    throw new  
ValidationsArgumentError('Too many range options specified.  Choose only one.');
            }

            
$attribute $options[0];
            
$var $this->model->$attribute;
            
$range_option $range_options[0];

            if (
$this->is_null_with_option($var$options) || $this->is_blank_with_option($var$options))
                continue;

            if (
'within' == $range_option || 'in' == $range_option)
            {
                
$range $options[$range_options[0]];

                if (!(
Utils::is_a('range'$range)))
                    throw new  
ValidationsArgumentError("$range_option must be an array composing a range of numbers with key [0] being less than key [1]");

                if (
is_float($range[0]) || is_float($range[1]))
                    throw new  
ValidationsArgumentError("Range values cannot use floats for length.");

                if ((int)
$range[0] <= || (int)$range[1] <= 0)
                    throw new  
ValidationsArgumentError("Range values cannot use signed integers.");

                
$too_short = isset($options['message']) ? $options['message'] : $options['too_short'];
                
$too_long =  isset($options['message']) ? $options['message'] : $options['too_long'];

                
$too_short str_replace('%d'$range[0], $too_short);
                
$too_long str_replace('%d'$range[1], $too_long);

                if (
strlen($this->model->$attribute) < (int)$range[0])
                    
$this->record->add($attribute$too_short);
                elseif (
strlen($this->model->$attribute) > (int)$range[1])
                    
$this->record->add($attribute$too_long);
            }

            elseif (
'is' == $range_option || 'minimum' == $range_option || 'maximum' == $range_option)
            {
                
$option $options[$range_option];

                if ((int)
$option <= 0)
                    throw new  
ValidationsArgumentError("$range_option value cannot use a signed integer.");

                if (
is_float($option))
                    throw new  
ValidationsArgumentError("$range_option value cannot use a float for length.");

                if (!
is_null($this->model->$attribute))
                {
                    
$messageOptions = array('is' => 'wrong_length''minimum' => 'too_short''maximum' => 'too_long');

                    if (isset(
$options[$messageOptions[$range_option]]))
                        
$message $options[$messageOptions[$range_option]];
                    else
                        
$message $options['message'];

                    
$message str_replace('%d'$option$message);
                    
$attribute_value $this->model->$attribute;
                    
$len strlen($attribute_value);
                    
$value = (int)$attr[$range_option];

                    if (
'maximum' == $range_option && $len $value)
                        
$this->record->add($attribute$message);

                    if (
'minimum' == $range_option && $len $value)
                        
$this->record->add($attribute$message);

                    if (
'is' == $range_option && $len !== $value)
                        
$this->record->add($attribute$message);
                }
            }
        }
    }

    
/**
     * Validates the uniqueness of a value.
     *
     * <code>
     * class Person extends ActiveRecordModel {
     *   static $validates_uniqueness_of = array(
     *     array('name'),
     *     array(array('blah','bleh'), 'message' => 'blech')
     *   );
     * }
     * </code>
     *
     * @param array $attrs Validation definition
     */
    
public function validates_uniqueness_of($attrs)
    {
        
$configuration array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
            
'message' => Errors::$DEFAULT_ERROR_MESSAGES['unique']
        ));

        foreach (
$attrs as $attr)
        {
            
$options array_merge($configuration$attr);
            
$pk $this->model->get_primary_key();
            
$pk_value $this->model->$pk[0];

            if (
is_array($options[0]))
            {
                
$add_record join("_and_"$options[0]);
                
$fields $options[0];
            }
            else
            {
                
$add_record $options[0];
                
$fields = array($options[0]);
            }

            
$sql "";
            
$conditions = array("");

            if (
$pk_value === null)
                
$sql "{$pk[0]} is not null";
            else
            {
                
$sql "{$pk[0]}!=?";
                
array_push($conditions,$pk_value);
            }

            foreach (
$fields as $field)
            {
                
$field $this->model->get_real_attribute_name($field);
                
$sql .= " and {$field}=?";
                
array_push($conditions,$this->model->$field);
            }

            
$conditions[0] = $sql;

            if (
$this->model->exists(array('conditions' => $conditions)))
                
$this->record->add($add_record$options['message']);
        }
    }

    private function 
is_null_with_option($var, &$options)
    {
        return (
is_null($var) && (isset($options['allow_null']) && $options['allow_null']));
    }

    private function 
is_blank_with_option($var, &$options)
    {
        return (
Utils::is_blank($var) && (isset($options['allow_blank']) && $options['allow_blank']));
    }
}

/**
 * Class that holds {@link Validations} errors.
 *
 * @package ActiveRecord
 */
class Errors implements IteratorAggregate
{
    private 
$model;
    private 
$errors;

    public static 
$DEFAULT_ERROR_MESSAGES = array(
           
'inclusion'        => "is not included in the list",
         
'exclusion'        => "is reserved",
          
'invalid'        => "is invalid",
          
'confirmation'    => "doesn't match confirmation",
          
'accepted'        => "must be accepted",
          
'empty'            => "can't be empty",
          
'blank'            => "can't be blank",
          
'too_long'        => "is too long (maximum is %d characters)",
          
'too_short'        => "is too short (minimum is %d characters)",
          
'wrong_length'    => "is the wrong length (should be %d characters)",
          
'taken'            => "has already been taken",
          
'not_a_number'    => "is not a number",
          
'greater_than'    => "must be greater than %d",
          
'equal_to'        => "must be equal to %d",
          
'less_than'        => "must be less than %d",
          
'odd'            => "must be odd",
          
'even'            => "must be even",
        
'unique'        => "must be unique",
          
'less_than_or_equal_to' => "must be less than or equal to %d",
          
'greater_than_or_equal_to' => "must be greater than or equal to %d"
       
);

       
/**
     * Constructs an {@link Errors} object.
     *
     * @param Model $model The model the error is for
     * @return Errors
        */
    
public function __construct(Model $model)
    {
        
$this->model $model;
    }

    
/**
     * Nulls $model so we don't get pesky circular references. $model is only needed during the
     * validation process and so can be safely cleared once that is done.
     */
    
public function clear_model()
    {
        
$this->model null;
    }

    
/**
     * Add an error message.
     *
     * @param string $attribute Name of an attribute on the model
     * @param string $msg The error message
     */
    
public function add($attribute$msg)
    {
        if (
is_null($msg))
            
$msg self :: $DEFAULT_ERROR_MESSAGES['invalid'];

        if (!isset(
$this->errors[$attribute]))
            
$this->errors[$attribute] = array($msg);
        else
            
$this->errors[$attribute][] = $msg;
    }

    
/**
     * Adds an error message only if the attribute value is {@link http://www.php.net/empty empty}.
     *
     * @param string $attribute Name of an attribute on the model
     * @param string $msg The error message
     */
    
public function add_on_empty($attribute$msg)
    {
        if (empty(
$msg))
            
$msg self::$DEFAULT_ERROR_MESSAGES['empty'];

        if (empty(
$this->model->$attribute))
            
$this->add($attribute$msg);
    }

    
/**
     * Retrieve error message for an attribute.
     *
     * @param string $attribute Name of an attribute on the model
     * @return string
     */
    
public function __get($attribute)
    {
        if (!isset(
$this->errors[$attribute]))
            return 
null;

        return 
$this->errors[$attribute];
    }

    
/**
     * Adds the error message only if the attribute value was null or an empty string.
     *
     * @param string $attribute Name of an attribute on the model
     * @param string $msg The error message
     */
    
public function add_on_blank($attribute$msg)
    {
        if (!
$msg)
            
$msg self::$DEFAULT_ERROR_MESSAGES['blank'];

        if ((
$value $this->model->$attribute) === '' || $value === null)
            
$this->add($attribute$msg);
    }

    
/**
     * Returns true if the specified attribute had any error messages.
     *
     * @param string $attribute Name of an attribute on the model
     * @return boolean
     */
    
public function is_invalid($attribute)
    {
        return isset(
$this->errors[$attribute]);
    }

    
/**
     * Returns the error message for the specified attribute or null if none.
     *
     * @param string $attribute Name of an attribute on the model
     * @return string
     */
    
public function on($attribute)
    {
        if (!isset(
$this->errors[$attribute]))
            return 
null;

        
$errors $this->errors[$attribute];

        if (
null === $errors)
            return 
null;
        else
            return 
count($errors) == $errors[0] : $errors;
    }

    
/**
     * Returns all the error messages as an array.
     *
     * <code>
     * $model->errors->full_messages();
     *
     * # array(
     * #  "Name can't be blank",
     * #  "State is the wrong length (should be 2 chars)"
     * # )
     * </code>
     *
     * @param array $options Options for messages
     * @return array
     */
    
public function full_messages()
    {
        
$full_messages = array();

        if (
$this->errors)
        {
            foreach (
$this->errors as $attribute => $messages)
            {
                foreach (
$messages as $msg)
                {
                    if (
is_null($msg))
                        continue;

                    
$full_messages[] = Utils::human_attribute($attribute) . ' ' $msg;
                }
            }
        }
        return 
$full_messages;
    }

    
/**
     * Returns true if there are no error messages.
     * @return boolean
     */
    
public function is_empty()
    {
        return empty(
$this->errors);
    }

    
/**
     * Clears out all error messages.
     */
    
public function clear()
    {
        
$this->errors = array();
    }

    
/**
     * Returns the number of error messages there are.
     * @return int
     */
    
public function size()
    {
        if (
$this->is_empty())
            return 
0;

        
$count 0;

        foreach (
$this->errors as $attribute => $error)
            
$count += count($error);

        return 
$count;
    }

    
/**
     * Returns an iterator to the error messages.
     *
     * This will allow you to iterate over the {@link Errors} object using foreach.
     *
     * <code>
     * foreach ($model->errors as $msg)
     *   echo "$msgn";
     * </code>
     *
     * @return ArrayIterator
     */
    
public function getIterator()
    {
        return new 
ArrayIterator($this->full_messages());
    }
};
?>
Онлайн: 0
Реклама