, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Rule.php 299706 2010-05-24 18:32:37Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Abstract base class for HTML_QuickForm2 rules * * This class provides methods that allow chaining several rules together. * Its validate() method executes the whole rule chain starting from this rule. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ abstract class HTML_QuickForm2_Rule { /** * Constant showing that validation should be run server-side * @see HTML_QuickForm2_Node::addRule() */ const RUNAT_SERVER = 1; /** * Constant showing that validation should be run client-side * @see HTML_QuickForm2_Node::addRule() */ const RUNAT_CLIENT = 2; /** * An element whose value will be validated by this rule * @var HTML_QuickForm2_Node */ protected $owner; /** * An error message to display if validation fails * @var string */ protected $message; /** * Configuration data for the rule * @var mixed */ protected $config; /** * Rules chained to this via "and" and "or" operators * * The contents can be described as "disjunctive normal form", where an outer * array represents a disjunction of conjunctive clauses represented by inner * arrays. * * @var array */ protected $chainedRules = array(array()); /** * Class constructor * * @param HTML_QuickForm2_Node Element to validate * @param string Error message to display if validation fails * @param mixed Configuration data for the rule */ public function __construct(HTML_QuickForm2_Node $owner, $message = '', $config = null) { $this->setOwner($owner); $this->setMessage($message); $this->setConfig($config); } /** * Merges local configuration with that provided for registerRule() * * Default behaviour is for global config to override local one, different * Rules may implement more complex merging behaviours. * * @param mixed Local configuration * @param mixed Global configuration, usually provided to {@link HTML_QuickForm2_Factory::registerRule()} * @return mixed Merged configuration */ public static function mergeConfig($localConfig, $globalConfig) { return is_null($globalConfig)? $localConfig: $globalConfig; } /** * Sets configuration data for the rule * * @param mixed Rule configuration data (specific for a Rule) * @return HTML_QuickForm2_Rule * @throws HTML_QuickForm2_InvalidArgumentException in case of invalid * configuration data */ public function setConfig($config) { $this->config = $config; return $this; } /** * Returns the rule's configuration data * * @return mixed Configuration data (specific for a Rule) */ public function getConfig() { return $this->config; } /** * Sets the error message output by the rule * * @param string Error message to display if validation fails * @return HTML_QuickForm2_Rule */ public function setMessage($message) { $this->message = (string)$message; return $this; } /** * Returns the error message output by the rule * * @return string Error message */ public function getMessage() { return $this->message; } /** * Sets the element that will be validated by this rule * * @param HTML_QuickForm2_Node Element to validate */ public function setOwner(HTML_QuickForm2_Node $owner) { if (null !== $this->owner) { $this->owner->removeRule($this); } $this->owner = $owner; } /** * Adds a rule to the chain with an "and" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns false. The method is named this way because "and" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function and_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'and_(): Cannot add a "required" rule' ); } $this->chainedRules[count($this->chainedRules) - 1][] = $next; return $this; } /** * Adds a rule to the chain with an "or" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns true. The method is named this way because "or" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function or_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'or_(): Cannot add a "required" rule' ); } $this->chainedRules[] = array($next); return $this; } /** * Performs validation * * The whole rule chain is executed. Note that the side effect of this * method is setting the error message on element if validation fails * * @return boolean Whether the element is valid */ public function validate() { $globalValid = false; $localValid = $this->validateOwner(); foreach ($this->chainedRules as $item) { foreach ($item as $multiplier) { if (!($localValid = $localValid && $multiplier->validate())) { break; } } if ($globalValid = $globalValid || $localValid) { break; } $localValid = true; } $globalValid or $this->setOwnerError(); return $globalValid; } /** * Validates the owner element * * @return bool Whether owner element is valid according to the rule */ abstract protected function validateOwner(); /** * Sets the error message on the owner element */ protected function setOwnerError() { if (strlen($this->getMessage()) && !$this->owner->getError()) { $this->owner->setError($this->getMessage()); } } /** * Returns the client-side validation callback * * This essentially builds a Javascript version of validateOwner() method, * with element ID and Rule configuration hardcoded. * * @return string Javascript function to validate the element's value * @throws HTML_QuickForm2_Exception if Rule can only be run server-side */ protected function getJavascriptCallback() { throw new HTML_QuickForm2_Exception( get_class($this) . ' does not implement javascript validation' ); } /** * Returns the client-side representation of the Rule * * The Javascript object returned contains the following fields: * - callback: {@see getJavascriptCallback()} * - elementId: element ID to set error for if validation fails * - errorMessage: error message to set if validation fails * - chained: chained rules, array of arrays like in $chainedRules property * * @return string * @throws HTML_QuickForm2_Exception if Rule or its chained Rules can only * be run server-side */ public function getJavascript() { $js = "{\n\tcallback: " . $this->getJavascriptCallback() . ",\n" . "\telementId: '" . $this->owner->getId() . "',\n" . "\terrorMessage: '" . strtr($this->getMessage(), array( "\r" => '\r', "\n" => '\n', "\t" => '\t', "'" => "\\'", '"' => '\"', '\\' => '\\\\' )) . "',\n\tchained: ["; $chained = array(); foreach ($this->chainedRules as $item) { $multipliers = array(); foreach ($item as $multiplier) { $multipliers[] = $multiplier->getJavascript(); } $chained[] = '[' . implode(",\n", $multipliers) . ']'; } $js .= implode(",\n", $chained) . "]\n}"; return $js; } } ?>