555 lines
10 KiB
PHP
555 lines
10 KiB
PHP
<?php defined("SYSPATH") or die("No direct script access.");
|
|
/**
|
|
* FORGE base input library.
|
|
*
|
|
* $Id: Form_Input.php 3326 2008-08-09 21:24:30Z Shadowhand $
|
|
*
|
|
* @package Forge
|
|
* @author Kohana Team
|
|
* @copyright (c) 2007-2008 Kohana Team
|
|
* @license http://kohanaphp.com/license.html
|
|
*/
|
|
class Form_Input_Core {
|
|
|
|
// Input method
|
|
public $method;
|
|
|
|
// Element data
|
|
protected $data = array
|
|
(
|
|
'type' => 'text',
|
|
'class' => 'textbox',
|
|
'value' => ''
|
|
);
|
|
|
|
// Protected data keys
|
|
protected $protect = array();
|
|
|
|
// Validation rules, matches, and callbacks
|
|
protected $rules = array();
|
|
protected $matches = array();
|
|
protected $callbacks = array();
|
|
|
|
// Validation check
|
|
protected $is_valid;
|
|
|
|
// Errors
|
|
protected $errors = array();
|
|
protected $error_messages = array();
|
|
|
|
/**
|
|
* Sets the input element name.
|
|
*/
|
|
public function __construct($name)
|
|
{
|
|
$this->data['name'] = $name;
|
|
}
|
|
|
|
/**
|
|
* Sets form attributes, or return rules.
|
|
*/
|
|
public function __call($method, $args)
|
|
{
|
|
if ($method == 'rules')
|
|
{
|
|
if (empty($args))
|
|
return $this->rules;
|
|
|
|
// Set rules and action
|
|
$rules = $args[0];
|
|
$action = substr($rules, 0, 1);
|
|
|
|
if (in_array($action, array('-', '+', '=')))
|
|
{
|
|
// Remove the action from the rules
|
|
$rules = substr($rules, 1);
|
|
}
|
|
else
|
|
{
|
|
// Default action is append
|
|
$action = '';
|
|
}
|
|
|
|
$this->add_rules(explode('|', $rules), $action);
|
|
}
|
|
elseif ($method == 'name')
|
|
{
|
|
// Do nothing. The name should stay static once it is set.
|
|
}
|
|
else
|
|
{
|
|
$this->data[$method] = $args[0];
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Returns form attributes.
|
|
*
|
|
* @param string attribute name
|
|
* @return string
|
|
*/
|
|
public function __get($key)
|
|
{
|
|
if (isset($this->data[$key]))
|
|
{
|
|
return $this->data[$key];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a form element that this element must match the value of.
|
|
*
|
|
* @chainable
|
|
* @param object another Forge input
|
|
* @return object
|
|
*/
|
|
public function matches($input)
|
|
{
|
|
if ( ! in_array($input, $this->matches, TRUE))
|
|
{
|
|
$this->matches[] = $input;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets a callback method as a rule for this input.
|
|
*
|
|
* @chainable
|
|
* @param callback
|
|
* @return object
|
|
*/
|
|
public function callback($callback)
|
|
{
|
|
if ( ! in_array($callback, $this->callbacks, TRUE))
|
|
{
|
|
$this->callbacks[] = $callback;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets or returns the input label.
|
|
*
|
|
* @chainable
|
|
* @param string label to set
|
|
* @return string|object
|
|
*/
|
|
public function label($val = NULL)
|
|
{
|
|
if ($val === NULL)
|
|
{
|
|
if (isset($this->data['name']) AND isset($this->data['label']))
|
|
{
|
|
return form::label($this->data['name'], $this->data['label']);
|
|
}
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
$this->data['label'] = ($val === TRUE) ? utf8::ucwords(inflector::humanize($this->name)) : $val;
|
|
return $this;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set or return the error message.
|
|
*
|
|
* @chainable
|
|
* @param string error message
|
|
* @return strong|object
|
|
*/
|
|
public function message($val = NULL)
|
|
{
|
|
if ($val === NULL)
|
|
{
|
|
if (isset($this->data['message']))
|
|
return $this->data['message'];
|
|
}
|
|
else
|
|
{
|
|
$this->data['message'] = $val;
|
|
return $this;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs validation and returns the element HTML.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function render()
|
|
{
|
|
// Make sure validation runs
|
|
$this->validate();
|
|
|
|
return $this->html_element();
|
|
}
|
|
|
|
/**
|
|
* Returns the form input HTML.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function html_element()
|
|
{
|
|
$data = $this->data;
|
|
|
|
unset($data['label']);
|
|
unset($data['message']);
|
|
|
|
return form::input($data);
|
|
}
|
|
|
|
/**
|
|
* Replace, remove, or append rules.
|
|
*
|
|
* @param array rules to change
|
|
* @param string action to use: replace, remove, append
|
|
*/
|
|
protected function add_rules( array $rules, $action)
|
|
{
|
|
if ($action === '=')
|
|
{
|
|
// Just replace the rules
|
|
$this->rules = $rules;
|
|
return;
|
|
}
|
|
|
|
foreach ($rules as $rule)
|
|
{
|
|
if ($action === '-')
|
|
{
|
|
if (($key = array_search($rule, $this->rules)) !== FALSE)
|
|
{
|
|
// Remove the rule
|
|
unset($this->rules[$key]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ! in_array($rule, $this->rules))
|
|
{
|
|
if ($action == '+')
|
|
{
|
|
array_unshift($this->rules, $rule);
|
|
}
|
|
else
|
|
{
|
|
$this->rules[] = $rule;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an error to the input.
|
|
*
|
|
* @chainable
|
|
* @return object
|
|
*/
|
|
public function add_error($key, $val)
|
|
{
|
|
if ( ! isset($this->errors[$key]))
|
|
{
|
|
$this->errors[$key] = $val;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set or return the error messages.
|
|
*
|
|
* @chainable
|
|
* @param string|array failed validation function, or an array of messages
|
|
* @param string error message
|
|
* @return object|array
|
|
*/
|
|
public function error_messages($func = NULL, $message = NULL)
|
|
{
|
|
// Set custom error messages
|
|
if ( ! empty($func))
|
|
{
|
|
if (is_array($func))
|
|
{
|
|
// Replace all
|
|
$this->error_messages = $func;
|
|
}
|
|
else
|
|
{
|
|
if (empty($message))
|
|
{
|
|
// Single error, replaces all others
|
|
$this->error_messages = $func;
|
|
}
|
|
else
|
|
{
|
|
// Add custom error
|
|
$this->error_messages[$func] = $message;
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
// Make sure validation runs
|
|
is_null($this->is_valid) and $this->validate();
|
|
|
|
// Return single error
|
|
if ( ! is_array($this->error_messages) AND ! empty($this->errors))
|
|
return array($this->error_messages);
|
|
|
|
$messages = array();
|
|
foreach ($this->errors as $func => $args)
|
|
{
|
|
if (is_string($args))
|
|
{
|
|
$error = $args;
|
|
}
|
|
else
|
|
{
|
|
// Force args to be an array
|
|
$args = is_array($args) ? $args : array();
|
|
|
|
// Add the label or name to the beginning of the args
|
|
array_unshift($args, $this->label ? mb_strtolower($this->label) : $this->name);
|
|
|
|
if (isset($this->error_messages[$func]))
|
|
{
|
|
// Use custom error message
|
|
$error = vsprintf($this->error_messages[$func], $args);
|
|
}
|
|
else
|
|
{
|
|
// Get the proper i18n entry, very hacky but it works
|
|
switch ($func)
|
|
{
|
|
case 'valid_url':
|
|
case 'valid_email':
|
|
case 'valid_ip':
|
|
// Fetch an i18n error message
|
|
$error = 'validation.'.$func;
|
|
break;
|
|
case substr($func, 0, 6) === 'valid_':
|
|
// Strip 'valid_' from func name
|
|
$func = (substr($func, 0, 6) === 'valid_') ? substr($func, 6) : $func;
|
|
case 'alpha':
|
|
case 'alpha_dash':
|
|
case 'digit':
|
|
case 'numeric':
|
|
// i18n strings have to be inserted into valid_type
|
|
$args[] = 'validation.'.$func;
|
|
$error = 'validation.valid_type';
|
|
break;
|
|
default:
|
|
$error = 'validation.'.$func;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add error to list
|
|
$messages[] = $error;
|
|
}
|
|
|
|
return $messages;
|
|
}
|
|
|
|
/**
|
|
* Get the global input value.
|
|
*
|
|
* @return string|bool
|
|
*/
|
|
protected function input_value($name = array())
|
|
{
|
|
// Get the Input instance
|
|
$input = Input::instance();
|
|
|
|
// Fetch the method for this object
|
|
$method = $this->method;
|
|
|
|
return $input->$method($name, NULL);
|
|
}
|
|
|
|
/**
|
|
* Load the value of the input, if form data is present.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function load_value()
|
|
{
|
|
if (is_bool($this->is_valid))
|
|
return;
|
|
|
|
if ($name = $this->name)
|
|
{
|
|
// Load POSTed value, but only for named inputs
|
|
$this->data['value'] = $this->input_value($name);
|
|
}
|
|
|
|
if (is_string($this->data['value']))
|
|
{
|
|
// Trim string values
|
|
$this->data['value'] = trim($this->data['value']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate this input based on the set rules.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function validate()
|
|
{
|
|
// Validation has already run
|
|
if (is_bool($this->is_valid))
|
|
return $this->is_valid;
|
|
|
|
// No data to validate
|
|
if ($this->input_value() == FALSE)
|
|
return $this->is_valid = FALSE;
|
|
|
|
// Load the submitted value
|
|
$this->load_value();
|
|
|
|
// No rules to validate
|
|
if (count($this->rules) == 0 AND count($this->matches) == 0 AND count($this->callbacks) == 0)
|
|
return $this->is_valid = TRUE;
|
|
|
|
if ( ! empty($this->rules))
|
|
{
|
|
foreach ($this->rules as $rule)
|
|
{
|
|
if (($offset = strpos($rule, '[')) !== FALSE)
|
|
{
|
|
// Get the args
|
|
$args = preg_split('/, ?/', trim(substr($rule, $offset), '[]'));
|
|
|
|
// Remove the args from the rule
|
|
$rule = substr($rule, 0, $offset);
|
|
}
|
|
|
|
if (substr($rule, 0, 6) === 'valid_' AND method_exists('valid', substr($rule, 6)))
|
|
{
|
|
$func = substr($rule, 6);
|
|
|
|
if ($this->value AND ! valid::$func($this->value))
|
|
{
|
|
$this->errors[$rule] = TRUE;
|
|
}
|
|
}
|
|
elseif (method_exists($this, 'rule_'.$rule))
|
|
{
|
|
// The rule function is always prefixed with rule_
|
|
$rule = 'rule_'.$rule;
|
|
|
|
if (isset($args))
|
|
{
|
|
// Manually call up to 2 args for speed
|
|
switch (count($args))
|
|
{
|
|
case 1:
|
|
$this->$rule($args[0]);
|
|
break;
|
|
case 2:
|
|
$this->$rule($args[0], $args[1]);
|
|
break;
|
|
default:
|
|
call_user_func_array(array($this, $rule), $args);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Just call the rule
|
|
$this->$rule();
|
|
}
|
|
|
|
// Prevent args from being re-used
|
|
unset($args);
|
|
}
|
|
else
|
|
{
|
|
throw new Kohana_Exception('validation.invalid_rule', $rule);
|
|
}
|
|
|
|
// Stop when an error occurs
|
|
if ( ! empty($this->errors))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ! empty($this->matches))
|
|
{
|
|
foreach ($this->matches as $input)
|
|
{
|
|
if ($this->value != $input->value)
|
|
{
|
|
// Field does not match
|
|
$this->errors['matches'] = array($input->label ? mb_strtolower($input->label) : $input->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! empty($this->callbacks))
|
|
{
|
|
foreach ($this->callbacks as $callback)
|
|
{
|
|
call_user_func($callback, $this);
|
|
|
|
// Stop when an error occurs
|
|
if ( ! empty($this->errors))
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there are errors, validation failed
|
|
return $this->is_valid = empty($this->errors);
|
|
}
|
|
|
|
/**
|
|
* Validate required.
|
|
*/
|
|
protected function rule_required()
|
|
{
|
|
if ($this->value === '' OR $this->value === NULL)
|
|
{
|
|
$this->errors['required'] = TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate length.
|
|
*/
|
|
protected function rule_length($min, $max = NULL)
|
|
{
|
|
// Get the length, return if zero
|
|
if (($length = mb_strlen($this->value)) === 0)
|
|
return;
|
|
|
|
if ($max == NULL)
|
|
{
|
|
if ($length != $min)
|
|
{
|
|
$this->errors['exact_length'] = array($min);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ($length < $min)
|
|
{
|
|
$this->errors['min_length'] = array($min);
|
|
}
|
|
elseif ($length > $max)
|
|
{
|
|
$this->errors['max_length'] = array($max);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End Form Input
|