Пример #1
0
 /**
  * Parse mask and array of default values; initializes object.
  * @param  string
  * @param  array
  * @return void
  */
 private function setMask($mask, array $metadata)
 {
     $this->mask = $mask;
     // detect '//host/path' vs. '/abs. path' vs. 'relative path'
     if (substr($mask, 0, 2) === '//') {
         $this->type = self::HOST;
     } elseif (substr($mask, 0, 1) === '/') {
         $this->type = self::PATH;
     } else {
         $this->type = self::RELATIVE;
     }
     foreach ($metadata as $name => $meta) {
         if (!is_array($meta)) {
             $metadata[$name] = array(self::VALUE => $meta, 'fixity' => self::CONSTANT);
         } elseif (array_key_exists(self::VALUE, $meta)) {
             $metadata[$name]['fixity'] = self::CONSTANT;
         }
     }
     // PARSE MASK
     // <parameter-name[=default] [pattern] [#class]> or [ or ] or ?...
     $parts = Strings::split($mask, '/<([^>#= ]+)(=[^># ]*)? *([^>#]*)(#?[^>\\[\\]]*)>|(\\[!?|\\]|\\s*\\?.*)/');
     $this->xlat = array();
     $i = count($parts) - 1;
     // PARSE QUERY PART OF MASK
     if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') {
         // name=<parameter-name [pattern][#class]>
         $matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^># ]+) *([^>#]*)(#?[^>]*)>/');
         foreach ($matches as $match) {
             list(, $param, $name, $pattern, $class) = $match;
             // $pattern is not used
             if ($class !== '') {
                 if (!isset(self::$styles[$class])) {
                     throw new InvalidStateException("Parameter '{$name}' has '{$class}' flag, but Route::\$styles['{$class}'] is not set.");
                 }
                 $meta = self::$styles[$class];
             } elseif (isset(self::$styles['?' . $name])) {
                 $meta = self::$styles['?' . $name];
             } else {
                 $meta = self::$styles['?#'];
             }
             if (isset($metadata[$name])) {
                 $meta = $metadata[$name] + $meta;
             }
             if (array_key_exists(self::VALUE, $meta)) {
                 $meta['fixity'] = self::OPTIONAL;
             }
             unset($meta['pattern']);
             $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]);
             $metadata[$name] = $meta;
             if ($param !== '') {
                 $this->xlat[$name] = $param;
             }
         }
         $i -= 6;
     }
     // PARSE PATH PART OF MASK
     $brackets = 0;
     // optional level
     $re = '';
     $sequence = array();
     $autoOptional = TRUE;
     do {
         array_unshift($sequence, $parts[$i]);
         $re = preg_quote($parts[$i], '#') . $re;
         if ($i === 0) {
             break;
         }
         $i--;
         $part = $parts[$i];
         // [ or ]
         if ($part === '[' || $part === ']' || $part === '[!') {
             $brackets += $part[0] === '[' ? -1 : 1;
             if ($brackets < 0) {
                 throw new InvalidArgumentException("Unexpected '{$part}' in mask '{$mask}'.");
             }
             array_unshift($sequence, $part);
             $re = ($part[0] === '[' ? '(?:' : ')?') . $re;
             $i -= 5;
             continue;
         }
         $class = $parts[$i];
         $i--;
         // validation class
         $pattern = trim($parts[$i]);
         $i--;
         // validation condition (as regexp)
         $default = $parts[$i];
         $i--;
         // default value
         $name = $parts[$i];
         $i--;
         // parameter name
         array_unshift($sequence, $name);
         if ($name[0] === '?') {
             // "foo" parameter
             $re = '(?:' . preg_quote(substr($name, 1), '#') . '|' . $pattern . ')' . $re;
             $sequence[1] = substr($name, 1) . $sequence[1];
             continue;
         }
         // check name (limitation by regexp)
         if (preg_match('#[^a-z0-9_-]#i', $name)) {
             throw new InvalidArgumentException("Parameter name must be alphanumeric string due to limitations of PCRE, '{$name}' given.");
         }
         // pattern, condition & metadata
         if ($class !== '') {
             if (!isset(self::$styles[$class])) {
                 throw new InvalidStateException("Parameter '{$name}' has '{$class}' flag, but Route::\$styles['{$class}'] is not set.");
             }
             $meta = self::$styles[$class];
         } elseif (isset(self::$styles[$name])) {
             $meta = self::$styles[$name];
         } else {
             $meta = self::$styles['#'];
         }
         if (isset($metadata[$name])) {
             $meta = $metadata[$name] + $meta;
         }
         if ($pattern == '' && isset($meta[self::PATTERN])) {
             $pattern = $meta[self::PATTERN];
         }
         if ($default !== '') {
             $meta[self::VALUE] = (string) substr($default, 1);
             $meta['fixity'] = self::PATH_OPTIONAL;
         }
         $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]);
         if (array_key_exists(self::VALUE, $meta)) {
             if (isset($meta['filterTable2'][$meta[self::VALUE]])) {
                 $meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]];
             } elseif (isset($meta[self::FILTER_OUT])) {
                 $meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]);
             } else {
                 $meta['defOut'] = $meta[self::VALUE];
             }
         }
         $meta[self::PATTERN] = "#(?:{$pattern})\$#A" . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu');
         // include in expression
         $re = '(?P<' . str_replace('-', '___', $name) . '>(?U)' . $pattern . ')' . $re;
         // str_replace is dirty trick to enable '-' in parameter name
         if ($brackets) {
             // is in brackets?
             if (!isset($meta[self::VALUE])) {
                 $meta[self::VALUE] = $meta['defOut'] = NULL;
             }
             $meta['fixity'] = self::PATH_OPTIONAL;
         } elseif (!$autoOptional) {
             unset($meta['fixity']);
         } elseif (isset($meta['fixity'])) {
             // auto-optional
             $re = '(?:' . $re . ')?';
             $meta['fixity'] = self::PATH_OPTIONAL;
         } else {
             $autoOptional = FALSE;
         }
         $metadata[$name] = $meta;
     } while (TRUE);
     if ($brackets) {
         throw new InvalidArgumentException("Missing closing ']' in mask '{$mask}'.");
     }
     $this->re = '#' . $re . '/?$#A' . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu');
     $this->metadata = $metadata;
     $this->sequence = $sequence;
 }
Пример #2
0
 /**
  * Returns position of token in input string.
  * @param  int token number
  * @return array [offset, line, column]
  */
 public function getOffset($i)
 {
     $tokens = Strings::split($this->input, $this->re, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
     $offset = isset($tokens[$i]) ? $tokens[$i][1] : strlen($this->input);
     return array($offset, $offset ? substr_count($this->input, "\n", 0, $offset) + 1 : 1, $offset - strrpos(substr($this->input, 0, $offset), "\n"));
 }