Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/*
4
 * This file is part of the symfony package.
5
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
 
11
/**
12
 * sfValidatorFromDescription converts a string to a validator.
13
 *
14
 * @package    symfony
15
 * @subpackage validator
16
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17
 * @version    SVN: $Id: sfValidatorFromDescription.class.php 28700 2010-03-23 11:57:16Z fabien $
18
 */
19
class sfValidatorFromDescription extends sfValidatorDecorator
20
{
21
  protected
22
    $tokens = array(),
23
    $string = '';
24
 
25
  /**
26
   * @see sfValidatorBase
27
   */
28
  public function __construct($string, $options = array(), $messages = array())
29
  {
30
    $this->string = $string;
31
    $this->tokens = $this->tokenize($string);
32
 
33
    parent::__construct($options, $messages);
34
  }
35
 
36
  /**
37
   * Returns a PHP representation for the validator.
38
   *
39
   * This PHP representation can be evaled to return the object validator.
40
   *
41
   * This is mainly useful to cache the result of the validator string parsing.
42
   *
43
   * @return string The PHP representation for the validator
44
   */
45
  public function asPhp()
46
  {
47
    return $this->reduceTokens($this->tokens, 'asPhp');
48
  }
49
 
50
  /**
51
   * @see sfValidatorDecorator
52
   */
53
  public function getValidator()
54
  {
55
    if (null === $this->validator)
56
    {
57
      $this->validator = $this->reduceTokens($this->tokens, 'getValidator');
58
    }
59
 
60
    return $this->validator;
61
  }
62
 
63
  /**
64
   * Tokenizes a validator string to a list of tokens in RPN.
65
   *
66
   * @param  string $string  A validator string
67
   *
68
   * @return array  An array of tokens
69
   */
70
  protected function tokenize($string)
71
  {
72
    $tokens = array();
73
    $len = strlen($string);
74
    $i = 0;
75
    while ($i < $len)
76
    {
77
      if (preg_match('/^([a-z0-9_\-]+)\s*(<=|>=|<|>|==|!=)/i', substr($string, $i), $match))
78
      {
79
        // schema compare validator
80
        $i += strlen($match[0]);
81
 
82
        $leftField = $match[1];
83
        $operator = $match[2];
84
 
85
        // arguments (optional)
86
        $arguments = $this->parseArguments($string, $i);
87
 
88
        // rightField
89
        if (!preg_match('/\s*([a-z0-9_\-]+)/', substr($string, $i), $match))
90
        {
91
          throw new DomainException('Parsing problem.');
92
        }
93
 
94
        $i += strlen($match[0]);
95
        $rightField = $match[1];
96
 
97
        $tokens[] = new sfValidatorFDToken('sfValidatorSchemaCompare', array($leftField, $operator, $rightField, $arguments[0], isset($arguments[1]) ? $arguments[1] : array()));
98
      }
99
      else if (preg_match('/^(and|or)/i', substr($string, $i), $match))
100
      {
101
        // all, any validador
102
        $i += strlen($match[0]);
103
 
104
        // arguments (optional)
105
        $arguments = $this->parseArguments($string, $i);
106
 
107
        $tokens[] = new sfValidatorFDTokenOperator(strtolower($match[1]), $arguments);
108
      }
109
      else if (preg_match('/^(?:([a-z0-9_\-]+)\:)?([a-z0-9_\-]+)/i', substr($string, $i), $match))
110
      {
111
        // single validator (optionally filtered)
112
        $i += strlen($match[0]);
113
 
114
        $class = 'sfValidator'.$match[2];
115
        $arguments = $this->parseArguments($string, $i);
116
        $token = new sfValidatorFDToken($class, array($arguments[0], isset($arguments[1]) ? $arguments[1] : array()));
117
        if ($match[1])
118
        {
119
          $token = new sfValidatorFDTokenFilter($match[1], $token);
120
        }
121
 
122
        $tokens[] = $token;
123
      }
124
      else if ('(' == $string[$i])
125
      {
126
        $tokens[] = new sfValidatorFDTokenLeftBracket();
127
        ++$i;
128
      }
129
      else if (')' == $string[$i])
130
      {
131
        $tokens[] = new sfValidatorFDTokenRightBracket();
132
        ++$i;
133
      }
134
      else if (in_array($string[$i], array(' ', "\t", "\r", "\n")))
135
      {
136
        ++$i;
137
      }
138
      else
139
      {
140
        throw new DomainException(sprintf('Unable to parse string (%s).', $string));
141
      }
142
    }
143
 
144
    return $this->convertInfixToRpn($tokens);
145
  }
146
 
147
  /**
148
   * Parses validator arguments.
149
   *
150
   * @param  string  $string  The string to parse
151
   * @param  integer $i       The indice to start the parsing
152
   *
153
   * @return array   An array of parameters
154
   */
155
  protected function parseArguments($string, &$i)
156
  {
157
    $len = strlen($string);
158
 
159
    if ($i + 1 > $len || '(' != $string[$i])
160
    {
161
      return array(array(), array());
162
    }
163
 
164
    ++$i;
165
 
166
    $args = '';
167
    $opened = 0;
168
    while ($i < $len)
169
    {
170
      if ('(' == $string[$i])
171
      {
172
        ++$opened;
173
      }
174
      else if (')' == $string[$i])
175
      {
176
        if (!$opened)
177
        {
178
          break;
179
        }
180
 
181
        --$opened;
182
      }
183
 
184
      $args .= $string[$i++];
185
    }
186
 
187
    ++$i;
188
 
189
    return sfYamlInline::load('['.(!$args ? '{}' : $args).']');
190
  }
191
 
192
  /**
193
   * Converts a token array from an infix notation to a RPN.
194
   *
195
   * @param  array $tokens  An array of tokens in infix notation
196
   *
197
   * @return array An array of token in RPN
198
   */
199
  protected function convertInfixToRpn($tokens)
200
  {
201
    $outputStack = array();
202
    $operatorStack = array();
203
    $precedences = array('and' => 2, 'or' => 1, '(' => 0);
204
 
205
    // based on the shunting yard algorithm
206
    foreach ($tokens as $token)
207
    {
208
      switch (get_class($token))
209
      {
210
        case 'sfValidatorFDToken':
211
          $outputStack[] = $token;
212
          break;
213
        case 'sfValidatorFDTokenLeftBracket':
214
          $operatorStack[] = $token;
215
          break;
216
        case 'sfValidatorFDTokenRightBracket':
217
          while (!$operatorStack[count($operatorStack) - 1] instanceof sfValidatorFDTokenLeftBracket)
218
          {
219
            $outputStack[] = array_pop($operatorStack);
220
          }
221
          array_pop($operatorStack);
222
          break;
223
        case 'sfValidatorFDTokenOperator':
224
          while (count($operatorStack) && $precedences[$token->__toString()] <= $precedences[$operatorStack[count($operatorStack) - 1]->__toString()])
225
          {
226
            $outputStack[] = array_pop($operatorStack);
227
          }
228
          $operatorStack[] = $token;
229
          break;
230
        default:
231
          $outputStack[] = $token;
232
      }
233
    }
234
 
235
    while (count($operatorStack))
236
    {
237
      $token = array_pop($operatorStack);
238
      if ($token instanceof sfValidatorFDTokenLeftBracket || $token instanceof sfValidatorFDTokenRightBracket)
239
      {
240
        throw new DomainException(sprintf('Uneven parenthesis in string (%s).', $this->string));
241
      }
242
 
243
      $outputStack[] = $token;
244
    }
245
 
246
    return $outputStack;
247
  }
248
 
249
  /**
250
   * Reduces tokens to a single token and convert it with the given method.
251
   *
252
   * @param  array  $tokens  An array of tokens
253
   * @param  string $method  The method name to execute on each token
254
   *
255
   * @return mixed  A single validator representation
256
   */
257
  protected function reduceTokens($tokens, $method)
258
  {
259
    if (1 == count($tokens))
260
    {
261
      return $tokens[0]->$method();
262
    }
263
 
264
    // reduce to a single validator
265
    while (count($tokens) > 1)
266
    {
267
      $i = 0;
268
      while (isset($tokens[$i]) && !$tokens[$i] instanceof sfValidatorFDTokenOperator)
269
      {
270
        $i++;
271
      }
272
 
273
      $tokens[$i] = $tokens[$i]->$method($tokens[$i - 2], $tokens[$i - 1]);
274
      unset($tokens[$i - 1], $tokens[$i - 2]);
275
 
276
      $tokens = array_values($tokens);
277
    }
278
 
279
    return $tokens[0];
280
  }
281
}
282
 
283
class sfValidatorFDToken
284
{
285
  protected
286
    $class,
287
    $arguments;
288
 
289
  public function __construct($class, $arguments = array())
290
  {
291
    $this->class = $class;
292
    $this->arguments = $arguments;
293
  }
294
 
295
  public function asPhp()
296
  {
297
    return sprintf('new %s(%s)', $this->class, implode(', ', array_map(create_function('$a', 'return var_export($a, true);'), $this->arguments)));
298
  }
299
 
300
  public function getValidator()
301
  {
302
    $reflection = new ReflectionClass($this->class);
303
 
304
    return $reflection->newInstanceArgs($this->arguments);
305
  }
306
}
307
 
308
class sfValidatorFDTokenFilter
309
{
310
  protected
311
    $field,
312
    $token;
313
 
314
  public function __construct($field, sfValidatorFDToken $token)
315
  {
316
    $this->field = $field;
317
    $this->token = $token;
318
  }
319
 
320
  public function asPhp()
321
  {
322
    return sprintf('new sfValidatorSchemaFilter(\'%s\', %s)', $this->field, $this->token->asPhp());
323
  }
324
 
325
  public function getValidator()
326
  {
327
    return new sfValidatorSchemaFilter($this->field, $this->token->getValidator());
328
  }
329
}
330
 
331
class sfValidatorFDTokenOperator
332
{
333
  protected
334
    $class,
335
    $operator,
336
    $token;
337
 
338
  public function __construct($operator, $arguments = array())
339
  {
340
    $this->operator = $operator;
341
    $this->arguments = $arguments;
342
    $this->class = 'or' == $operator ? 'sfValidatorOr' : 'sfValidatorAnd';
343
  }
344
 
345
  public function __toString()
346
  {
347
    return $this->operator;
348
  }
349
 
350
  public function asPhp($tokenLeft, $tokenRight)
351
  {
352
    return sprintf('new %s(array(%s, %s), %s)',
353
      $this->class,
354
      is_object($tokenLeft) && in_array(get_class($tokenLeft), array('sfValidatorFDToken', 'sfValidatorFDTokenFilter')) ? $tokenLeft->asPhp() : $tokenLeft,
355
      is_object($tokenRight) && in_array(get_class($tokenRight), array('sfValidatorFDToken', 'sfValidatorFDTokenFilter')) ? $tokenRight->asPhp() : $tokenRight,
356
      implode(', ', array_map(create_function('$a', 'return var_export($a, true);'), $this->arguments))
357
    );
358
  }
359
 
360
  public function getValidator($tokenLeft, $tokenRight)
361
  {
362
    $reflection = new ReflectionClass($this->class);
363
 
364
    $validators = array(
365
      in_array(get_class($tokenLeft), array('sfValidatorFDToken', 'sfValidatorFDTokenFilter')) ? $tokenLeft->getValidator() : $tokenLeft,
366
      in_array(get_class($tokenRight), array('sfValidatorFDToken', 'sfValidatorFDTokenFilter')) ? $tokenRight->getValidator() : $tokenRight,
367
    );
368
 
369
    return $reflection->newInstanceArgs(array_merge(array($validators), $this->arguments));
370
  }
371
}
372
 
373
class sfValidatorFDTokenLeftBracket
374
{
375
  public function __toString()
376
  {
377
    return '(';
378
  }
379
}
380
 
381
class sfValidatorFDTokenRightBracket
382
{
383
  public function __toString()
384
  {
385
    return ')';
386
  }
387
}