1<?php
2/**
3 * Rule comparing the value of the field with some other value
4 *
5 * PHP version 5
6 *
7 * LICENSE:
8 *
9 * Copyright (c) 2006-2010, Alexey Borzov <avb@php.net>,
10 *                          Bertrand Mansion <golgote@mamasam.com>
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 *    * Redistributions of source code must retain the above copyright
18 *      notice, this list of conditions and the following disclaimer.
19 *    * Redistributions in binary form must reproduce the above copyright
20 *      notice, this list of conditions and the following disclaimer in the
21 *      documentation and/or other materials provided with the distribution.
22 *    * The names of the authors may not be used to endorse or promote products
23 *      derived from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
33 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * @category   HTML
38 * @package    HTML_QuickForm2
39 * @author     Alexey Borzov <avb@php.net>
40 * @author     Bertrand Mansion <golgote@mamasam.com>
41 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
42 * @version    SVN: $Id: Compare.php 299480 2010-05-19 06:55:03Z avb $
43 * @link       http://pear.php.net/package/HTML_QuickForm2
44 */
45
46/**
47 * Base class for HTML_QuickForm2 rules
48 */
49// require_once 'HTML/QuickForm2/Rule.php';
50
51/**
52 * Rule comparing the value of the field with some other value
53 *
54 * The Rule needs two configuration parameters for its work
55 *  - comparison operator (defaults to equality)
56 *  - operand to compare with; this can be either a constant or another form
57 *    element (its value will be used)
58 * See {@link mergeConfig()} for description of possible ways to pass
59 * configuration parameters.
60 *
61 * Note that 'less than [or equal]' and 'greater than [or equal]' operators
62 * compare the operands numerically, since this is considered as more useful
63 * approach by the authors.
64 *
65 * For convenience, this Rule is already registered in the Factory with the
66 * names 'eq', 'neq', 'lt', 'gt', 'lte', 'gte' corresponding to the relevant
67 * operators:
68 * <code>
69 * $password->addRule('eq', 'Passwords do not match', $passwordRepeat);
70 * $orderQty->addRule('lte', 'Should not order more than 10 of these', 10);
71 * </code>
72 *
73 * @category   HTML
74 * @package    HTML_QuickForm2
75 * @author     Alexey Borzov <avb@php.net>
76 * @author     Bertrand Mansion <golgote@mamasam.com>
77 * @version    Release: @package_version@
78 */
79class HTML_QuickForm2_Rule_Compare extends HTML_QuickForm2_Rule
80{
81   /**
82    * Possible comparison operators
83    * @var array
84    */
85    protected $operators = array('==', '!=', '===', '!==', '<', '<=', '>', '>=');
86
87    protected function doOperation($a, $b, $operator)
88    {
89        switch ($operator) {
90            case "==": return $a == $b;
91            case "!=": return $a != $b;
92            case "===": return $a === $b;
93            case "!==": return $a !== $b;
94            case ">": return $a > $b;
95            case "<=": return $a <= $b;
96            case "<": return $a < $b;
97            case ">=": return $a >= $b;
98            default: return true;
99        }
100    }
101
102   /**
103    * Validates the owner element
104    *
105    * @return   bool    whether (element_value operator operand) expression is true
106    */
107    protected function validateOwner()
108    {
109        $value  = $this->owner->getValue();
110        $config = $this->getConfig();
111
112        if ($config['operand'] instanceof HTML_QuickForm2_Node) {
113            $b = $config['operand']->getValue();
114        } else {
115            $b = $config['operand'];
116        }
117
118        if (!in_array($config['operator'], array('===', '!=='))) {
119            $a = floatval($value);
120            $b = floatval($b);
121        } else {
122            $a = strval($value);
123            $b = strval($b);
124        }
125
126        return $this->doOperation($a, $b, $config['operator']);
127    }
128
129    protected function getJavascriptCallback()
130    {
131        $config   = $this->getConfig();
132        $operand1 = $this->owner->getJavascriptValue();
133        $operand2 = $config['operand'] instanceof HTML_QuickForm2_Node
134                    ? $config['operand']->getJavascriptValue()
135                    : "'" . strtr($config['operand'], array(
136                                "\r" => '\r',
137                                "\n" => '\n',
138                                "\t" => '\t',
139                                "'"  => "\\'",
140                                '"'  => '\"',
141                                '\\' => '\\\\'
142                            )) . "'";
143
144        if (!in_array($config['operator'], array('===', '!=='))) {
145            $check = "Number({$operand1}) {$config['operator']} Number({$operand2})";
146        } else {
147            $check = "String({$operand1}) {$config['operator']} String({$operand2})";
148        }
149
150        return "function () { return {$check}; }";
151    }
152
153   /**
154    * Merges local configuration with that provided for registerRule()
155    *
156    * "Global" configuration may be passed to
157    * {@link HTML_QuickForm2_Factory::registerRule()} in
158    * either of the following formats
159    *  - operator
160    *  - array(operator[, operand])
161    *  - array(['operator' => operator, ]['operand' => operand])
162
163    * "Local" configuration may be passed to the constructor in either of
164    * the following formats
165    *  - operand
166    *  - array([operator, ]operand)
167    *  - array(['operator' => operator, ]['operand' => operand])
168    *
169    * As usual, global configuration overrides local one.
170    *
171    * @param    mixed   Local configuration
172    * @param    mixed   Global configuration
173    * @return   mixed   Merged configuration
174    */
175    public static function mergeConfig($localConfig, $globalConfig)
176    {
177        $config = null;
178        if (is_array($globalConfig) && 0 < count($globalConfig)) {
179            $config = self::toCanonicalForm($globalConfig, 'operator');
180        }
181        if (is_array($localConfig) && 0 < count($localConfig)) {
182            $config = (isset($config)? $config: array())
183                      + self::toCanonicalForm($localConfig);
184        }
185        return $config;
186    }
187
188   /**
189    * Converts configuration data to a canonical associative array form
190    *
191    * @param    mixed   Configuration data
192    * @param    string  Array key to assign $config to if it is scalar
193    * @return   array   Associative array that may contain 'operand' and 'operator' keys
194    */
195    protected static function toCanonicalForm($config, $key = 'operand')
196    {
197        if (!is_array($config)) {
198            return array($key => $config);
199
200        } elseif (array_key_exists('operator', $config)
201                  || array_key_exists('operand', $config)
202        ) {
203            return $config;
204
205        } elseif (1 == count($config)) {
206            return array($key => end($config));
207
208        } else {
209            return array('operator' => reset($config), 'operand' => end($config));
210        }
211    }
212
213   /**
214    * Sets the comparison operator and operand to compare to
215    *
216    * $config can be either of the following
217    *  - operand
218    *  - array([operator, ]operand)
219    *  - array(['operator' => operator, ]['operand' => operand])
220    * If operator is missing it will default to '==='
221    *
222    * @param    mixed   Configuration data
223    * @return   HTML_QuickForm2_Rule
224    * @throws   HTML_QuickForm2_InvalidArgumentException if a bogus comparison
225    *           operator is used for configuration, if an operand is missing
226    */
227    public function setConfig($config)
228    {
229        if (0 == count($config)) {
230            throw new HTML_QuickForm2_InvalidArgumentException(
231                'Compare Rule requires an argument to compare with'
232            );
233        }
234        $config = self::toCanonicalForm($config);
235
236        $config += array('operator' => '===');
237        if (!in_array($config['operator'], $this->operators)) {
238            throw new HTML_QuickForm2_InvalidArgumentException(
239                'Compare Rule requires a valid comparison operator, ' .
240                preg_replace('/\s+/', ' ', var_export($config['operator'], true)) . ' given'
241            );
242        }
243        if (in_array($config['operator'], array('==', '!='))) {
244            $config['operator'] .= '=';
245        }
246
247        return parent::setConfig($config);
248    }
249}
250?>
251