1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: namespace Jyxo\Input;
15:
16: /**
17: * Chain of filters a validators for a single variable.
18: *
19: * @category Jyxo
20: * @package Jyxo\Input
21: * @subpackage Chain
22: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
23: * @license https://github.com/jyxo/php/blob/master/license.txt
24: * @author Jakub Tománek
25: */
26: class Chain implements \Jyxo\Input\ValidatorInterface
27: {
28: /**
29: * Filter identifier.
30: *
31: * @var string
32: */
33: const FILTER = 'filter';
34:
35: /**
36: * Validator identifier.
37: *
38: * @var string
39: */
40: const VALIDATOR = 'validator';
41:
42: /**
43: * Array walk identifier.
44: *
45: * @var string
46: */
47: const WALK = 'walk';
48:
49: /**
50: * Condition identifier.
51: *
52: * @var string
53: */
54: const CONDITION = 'condition';
55:
56: /**
57: * Subchain closing identifier.
58: *
59: * @var string
60: */
61: const CLOSE = 'close';
62:
63: /**
64: * Chain.
65: *
66: * @var array
67: */
68: private $chain = array();
69:
70: /**
71: * Parent chain reference.
72: *
73: * @var \Jyxo\Input\Chain
74: */
75: private $parent = null;
76:
77: /**
78: * Actual variable value.
79: *
80: * @var mixed
81: */
82: protected $value;
83:
84: /**
85: * Validation errors.
86: *
87: * @var array
88: */
89: private $errors = array();
90:
91: /**
92: * Adds a validator to the chain.
93: *
94: * @param \Jyxo\Input\ValidatorInterface $validator Validator
95: * @param string $errorMessage Validation error message
96: * @return \Jyxo\Input\Chain
97: */
98: public function addValidator(\Jyxo\Input\ValidatorInterface $validator, $errorMessage = null)
99: {
100: $this->chain[] = array(self::VALIDATOR, $validator, $errorMessage);
101: return $this;
102: }
103:
104: /**
105: * Adds a filter to the chain.
106: *
107: * @param \Jyxo\Input\FilterInterface $filter Filter
108: * @return \Jyxo\Input\Chain
109: */
110: public function addFilter(\Jyxo\Input\FilterInterface $filter)
111: {
112: $this->chain[] = array(self::FILTER, $filter);
113: return $this;
114: }
115:
116: /**
117: * Adds a new subchain and returns its instance.
118: *
119: * @return \Jyxo\Input\Chain
120: */
121: public function addWalk()
122: {
123: $chain = new self();
124: $chain->setParent($this);
125: $this->chain[] = array(self::WALK, $chain);
126: return $chain;
127: }
128:
129: /**
130: * Adds a new conditional subchain and returns its instance.
131: *
132: * @param \Jyxo\Input\Chain\Conditional $chain
133: * @return \Jyxo\Input\Chain\Conditional
134: */
135: public function addCondition(\Jyxo\Input\Chain\Conditional $chain)
136: {
137: $chain->setParent($this);
138: $this->chain[] = array(self::CONDITION, $chain);
139: return $chain;
140: }
141:
142: /**
143: * In case of a subchain returns its parent, the chain itself otherwise.
144: *
145: * @return \Jyxo\Input\Chain
146: */
147: public function close()
148: {
149: if (null === $this->getParent()) {
150: return $this;
151: }
152: return $this->getParent();
153: }
154:
155: /**
156: * Starts filtering and validation.
157: *
158: * @param mixed $value Input value
159: * @return boolean
160: */
161: private function run(&$value)
162: {
163: foreach ($this->chain as $item) {
164: if (self::FILTER === $item[0]) {
165: $filter = $item[1];
166: /* @var $filter \Jyxo\Input\FilterInterface */
167: $value = $filter->filter($value);
168: } elseif (self::VALIDATOR === $item[0]) {
169: $validator = $item[1];
170: /* @var $validator \Jyxo\Input\ValidatorInterface */
171: if (!$validator->isValid($value)) {
172: if ($validator instanceof \Jyxo\Input\Validator\ErrorMessage) {
173: $this->errors[] = $validator->getError();
174: } elseif (isset($item[2])) {
175: $this->errors[] = $item[2];
176: }
177: return false;
178: }
179: } elseif (self::CONDITION === $item[0]) {
180: $chain = $item[1];
181: /* @var $chain \Jyxo\Input\Chain\Conditional */
182: if ($chain->isValid($value)) {
183: $value = $chain->getValue();
184: } else {
185: $this->errors = array_merge($this->errors, $chain->getErrors());
186: return false;
187: }
188: } elseif (self::WALK === $item[0]) {
189: $chain = $item[1];
190: /* @var $chain \Jyxo\Input\Chain */
191: foreach ($value as &$sub) {
192: if ($chain->isValid($sub)) {
193: $sub = $chain->getValue($sub);
194: } else {
195: $this->errors = array_merge($this->errors, $chain->getErrors());
196: return false;
197: }
198: }
199: }
200: }
201:
202: return true;
203: }
204:
205: /**
206: * Returns if the chain contains any rules.
207: *
208: * @return boolean
209: */
210: public function isEmpty()
211: {
212: return empty($this->chain);
213: }
214:
215: /**
216: * Returns if the value is valid.
217: *
218: * @param mixed $value Input value
219: * @return boolean
220: */
221: public function isValid($value)
222: {
223: $success = $this->run($value);
224: // $value passed by reference
225: $this->value = $value;
226: return $success;
227: }
228:
229: /**
230: * Returns a filtered variable value.
231: *
232: * @return mixed
233: */
234: public function &getValue()
235: {
236: return $this->value;
237: }
238:
239: /**
240: * Returns a list of validation errors.
241: *
242: * @return array
243: */
244: public function getErrors()
245: {
246: return $this->errors;
247: }
248:
249: /**
250: * Returns the parent chain.
251: *
252: * @return \Jyxo\Input\Chain
253: */
254: public function getParent()
255: {
256: return $this->parent;
257: }
258:
259: /**
260: * Sets the parent chain.
261: *
262: * @param \Jyxo\Input\Chain $parent Parent chain
263: * @return \Jyxo\Input\Chain
264: */
265: public function setParent(\Jyxo\Input\Chain $parent)
266: {
267: $this->parent = $parent;
268: return $this;
269: }
270: }
271: