/** * @param string presenter name * @return string class name * @throws InvalidPresenterException */ public function getPresenterClass(&$name) { if (isset($this->cache[$name])) { list($class, $name) = $this->cache[$name]; return $class; } if (!is_string($name) || !String::match($name, "#^[a-zA-Z-ÿ][a-zA-Z0-9-ÿ:]*\$#")) { throw new InvalidPresenterException("Presenter name must be alphanumeric string, '{$name}' is invalid."); } $class = $this->formatPresenterClass($name); if (!class_exists($class)) { // internal autoloading $file = $this->formatPresenterFile($name); if (is_file($file) && is_readable($file)) { LimitedScope::load($file); } if (!class_exists($class)) { throw new InvalidPresenterException("Cannot load presenter '{$name}', class '{$class}' was not found in '{$file}'."); } } $reflection = new ClassReflection($class); $class = $reflection->getName(); if (!$reflection->implementsInterface('IPresenter')) { throw new InvalidPresenterException("Cannot load presenter '{$name}', class '{$class}' is not Nette\\Application\\IPresenter implementor."); } if ($reflection->isAbstract()) { throw new InvalidPresenterException("Cannot load presenter '{$name}', class '{$class}' is abstract."); } // canonicalize presenter name $realName = $this->unformatPresenterClass($class); if ($name !== $realName) { if ($this->caseSensitive) { throw new InvalidPresenterException("Cannot load presenter '{$name}', case mismatch. Real name is '{$realName}'."); } else { $this->cache[$name] = array($class, $realName); $name = $realName; } } else { $this->cache[$name] = array($class, $realName); } return $class; }
/** * Maps HTTP request to a PresenterRequest object. * @param IHttpRequest * @return PresenterRequest|NULL */ public function match(IHttpRequest $httpRequest) { // combine with precedence: mask (params in URL-path), fixity, query, (post,) defaults // 1) URL MASK $uri = $httpRequest->getUri(); if ($this->type === self::HOST) { $path = '//' . $uri->getHost() . $uri->getPath(); } elseif ($this->type === self::RELATIVE) { $basePath = $uri->getBasePath(); if (strncmp($uri->getPath(), $basePath, strlen($basePath)) !== 0) { return NULL; } $path = (string) substr($uri->getPath(), strlen($basePath)); } else { $path = $uri->getPath(); } if ($path !== '') { $path = rtrim($path, '/') . '/'; } if (!($matches = String::match($path, $this->re))) { // stop, not matched return NULL; } // deletes numeric keys, restore '-' chars $params = array(); foreach ($matches as $k => $v) { if (is_string($k) && $v !== '') { $params[str_replace('___', '-', $k)] = $v; // trick } } // 2) CONSTANT FIXITY foreach ($this->metadata as $name => $meta) { if (isset($params[$name])) { //$params[$name] = $this->flags & self::CASE_SENSITIVE === 0 ? strtolower($params[$name]) : */$params[$name]; // strtolower damages UTF-8 } elseif (isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) { $params[$name] = NULL; // cannot be overwriten in 3) and detected by isset() in 4) } } // 3) QUERY if ($this->xlat) { $params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat)); } else { $params += $httpRequest->getQuery(); } // 4) APPLY FILTERS & FIXITY foreach ($this->metadata as $name => $meta) { if (isset($params[$name])) { if (!is_scalar($params[$name])) { } elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) { // applyies filterTable only to scalar parameters $params[$name] = $meta[self::FILTER_TABLE][$params[$name]]; } elseif (isset($meta[self::FILTER_IN])) { // applyies filterIn only to scalar parameters $params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]); if ($params[$name] === NULL && !isset($meta['fixity'])) { return NULL; // rejected by filter } } } elseif (isset($meta['fixity'])) { $params[$name] = $meta[self::VALUE]; } } // 5) BUILD PresenterRequest if (!isset($params[self::PRESENTER_KEY])) { throw new InvalidStateException('Missing presenter in route definition.'); } if (isset($this->metadata[self::MODULE_KEY])) { if (!isset($params[self::MODULE_KEY])) { throw new InvalidStateException('Missing module in route definition.'); } $presenter = $params[self::MODULE_KEY] . ':' . $params[self::PRESENTER_KEY]; unset($params[self::MODULE_KEY], $params[self::PRESENTER_KEY]); } else { $presenter = $params[self::PRESENTER_KEY]; unset($params[self::PRESENTER_KEY]); } return new PresenterRequest($presenter, $httpRequest->getMethod(), $params, $httpRequest->getPost(), $httpRequest->getFiles(), array(PresenterRequest::SECURED => $httpRequest->isSecured())); }
/** * Parses PHP file. * @param string * @return void */ private static function parseScript($file) { $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE; $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR; $s = file_get_contents($file); if (String::match($s, '#//nette' . 'loader=(\\S*)#')) { return; // TODO: allways ignore? } $expected = $namespace = $class = $docComment = NULL; $level = $classLevel = 0; foreach (token_get_all($s) as $token) { if (is_array($token)) { switch ($token[0]) { case T_DOC_COMMENT: $docComment = $token[1]; case T_WHITESPACE: case T_COMMENT: continue 2; case T_STRING: case $T_NS_SEPARATOR: case T_VARIABLE: if ($expected) { $name .= $token[1]; } continue 2; case T_FUNCTION: case T_VAR: case T_PUBLIC: case T_PROTECTED: case $T_NAMESPACE: case T_CLASS: case T_INTERFACE: $expected = $token[0]; $name = NULL; continue 2; case T_STATIC: case T_ABSTRACT: case T_FINAL: continue 2; // ignore in expectation // ignore in expectation case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: $level++; } } if ($expected) { switch ($expected) { case T_CLASS: case T_INTERFACE: $class = $namespace . $name; $classLevel = $level; $name = ''; // break intentionally omitted // break intentionally omitted case T_FUNCTION: if ($token === '&') { continue 2; } // ignore // ignore case T_VAR: case T_PUBLIC: case T_PROTECTED: if ($class && $name !== NULL && $docComment) { self::$cache[$class][$name] = self::parseComment($docComment); } break; case $T_NAMESPACE: $namespace = $name . '\\'; } $expected = $docComment = NULL; } if ($token === ';') { $docComment = NULL; } elseif ($token === '{') { $docComment = NULL; $level++; } elseif ($token === '}') { $level--; if ($level === $classLevel) { $class = NULL; } } } }
/** * Changes current action. Only alphanumeric characters are allowed. * @param string * @return void */ public function changeAction($action) { if (String::match($action, "#^[a-zA-Z0-9][a-zA-Z0-9_-ÿ]*\$#")) { $this->action = $action; $this->view = $action; } else { throw new BadRequestException("Action name '{$action}' is not alphanumeric string."); } }
/** * Analyse PHP file. * @param string * @return void */ private function scanScript($file) { $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE; $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR; $expected = FALSE; $namespace = ''; $level = 0; $time = filemtime($file); $s = file_get_contents($file); if ($matches = String::match($s, '#//nette' . 'loader=(\\S*)#')) { foreach (explode(',', $matches[1]) as $name) { $this->addClass($name, $file, $time); } return; } foreach (token_get_all($s) as $token) { if (is_array($token)) { switch ($token[0]) { case T_COMMENT: case T_DOC_COMMENT: case T_WHITESPACE: continue 2; case $T_NS_SEPARATOR: case T_STRING: if ($expected) { $name .= $token[1]; } continue 2; case $T_NAMESPACE: case T_CLASS: case T_INTERFACE: $expected = $token[0]; $name = ''; continue 2; case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: $level++; } } if ($expected) { switch ($expected) { case T_CLASS: case T_INTERFACE: if ($level === 0) { $this->addClass($namespace . $name, $file, $time); } break; case $T_NAMESPACE: $namespace = $name . '\\'; } $expected = NULL; } if ($token === '{') { $level++; } elseif ($token === '}') { $level--; } } }
$a->split()->each(function ($value, $key) { echo "{$key}: {$value}\n"; }); // it works in foreach too. // this is gives the same output as above foreach ($a->split() as $key => $value) { echo "{$key}: {$value}\n"; } // regex too! // output: /* Number found: 9 Number found: 1000 */ $b = new String('This 9 word sentence is less than 1000 letters long.'); $b->match("/([0-9]+)/")->each(function ($value) { echo "Number found: {$value}\n"; }); // replaces all numbers with NUMBER. Third arg must be true because last arg is a regular expression // output: This NUMBER word sentence is less than NUMBER letters long. echo $b->replace("/([0-9]+)/", 'NUMBER', true) . "\n"; // not using regular expressions, and lowercase // output: this9wordsentenceislessthan1000letterslong. echo $b->replace(' ', '')->toLower() . "\n"; // something a bit more complex // strips the trailing 0's and 9's and splits it into an array, adds an element, reverse sorts it and turns it into an array // output: /* Array ( [8] => hi there!
/** * Builds HTML content. * @return void */ protected function buildHtml() { if ($this->html instanceof ITemplate) { $this->html->mail = $this; if ($this->basePath === NULL && $this->html instanceof IFileTemplate) { $this->basePath = dirname($this->html->getFile()); } $this->html = $this->html->__toString(TRUE); } if ($this->basePath !== FALSE) { $cids = array(); $matches = String::matchAll($this->html, '#(src\\s*=\\s*|background\\s*=\\s*|url\\()(["\'])(?![a-z]+:|[/\\#])(.+?)\\2#i', PREG_OFFSET_CAPTURE); foreach (array_reverse($matches) as $m) { $file = rtrim($this->basePath, '/\\') . '/' . $m[3][0]; $cid = isset($cids[$file]) ? $cids[$file] : ($cids[$file] = substr($this->addEmbeddedFile($file)->getHeader("Content-ID"), 1, -1)); $this->html = substr_replace($this->html, "{$m[1][0]}{$m[2][0]}cid:{$cid}{$m[2][0]}", $m[0][1], strlen($m[0][0])); } } if (!$this->getSubject() && ($matches = String::match($this->html, '#<title>(.+?)</title>#is'))) { $this->setSubject(html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8')); } }
/** * {widget ...} */ public function macroWidget($content) { $pair = LatteFilter::fetchToken($content); // widget[:method] if ($pair === NULL) { throw new InvalidStateException("Missing widget name in {widget} on line {$this->filter->line}."); } $pair = explode(':', $pair, 2); $widget = LatteFilter::formatString($pair[0]); $method = isset($pair[1]) ? ucfirst($pair[1]) : ''; $method = String::match($method, '#^(' . LatteFilter::RE_IDENTIFIER . '|)$#') ? "render{$method}" : "{\"render{$method}\"}"; $param = LatteFilter::formatArray($content); if (strpos($content, '=>') === FALSE) { $param = substr($param, 6, -1); } // removes array() return ($widget[0] === '$' ? "if (is_object({$widget})) {$widget}->{$method}({$param}); else " : '') . "\$control->getWidget({$widget})->{$method}({$param})"; }
/** * Float validator: is a control's value float number? * @param TextBase * @return bool */ public static function validateFloat(TextBase $control) { return (bool) String::match($control->getValue(), '/^-?[0-9]*[.,]?[0-9]+$/'); }
/** * Reads single token (optionally delimited by comma) from string. * @param string * @return string */ public static function fetchToken(&$s) { if ($matches = String::match($s, '#^((?>' . self::RE_STRING . '|[^\'"\\s,]+)+)\\s*,?\\s*(.*)$#')) { // token [,] tail $s = $matches[2]; return $matches[1]; } return NULL; }