public function getPresenterClass(&$name) { if (isset($this->cache[$name])) { list($className, $name) = $this->cache[$name]; return $className; } 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."); } $classNameBase = str_replace(':', 'Module\\', $name) . 'Presenter'; $classNames = array_map(function ($namespace) use($classNameBase) { return ($namespace ? $namespace . "\\" : "") . $classNameBase; }, $this->namespaces); foreach ($classNames as $className) { if (!class_exists($className)) { continue; } $reflection = new ClassReflection($className); if (!$reflection->implementsInterface('Nette\\Application\\IPresenter')) { throw new InvalidPresenterException("Cannot load presenter '{$name}', class '{$className}' is not Nette\\Application\\IPresenter implementor."); } if ($reflection->isAbstract()) { throw new InvalidPresenterException("Cannot load presenter '{$name}', class '{$className}' is abstract."); } return $className; } throw new InvalidPresenterException("Cannot load presenter {$name}, class " . implode(" nor ", $classNames) . " does not exist"); }
/** * Processes given request. * * @author Jan Tvrdík * @param PresenterRequest * @return void * @throws Nette\Applicationy\AbortException|BadRequestException */ public function processRequest(PresenterRequest $request) { $params = $request->getParams(); if (!isset($params['page'])) { throw new BadRequestException('Invalid request. Parameter \'page\' is required.'); } $this->page = $params['page']; if (!Nette\String::match($this->page, self::PAGE_REGEXP)) { throw new BadRequestException('Parameter \'page\' contains illegal characters.'); } $this->sendTemplate(); }
/** * @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) || !Nette\String::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) { 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)) { Nette\Loaders\LimitedScope::load($file); } if (!class_exists($class)) { throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'."); } } $reflection = new Nette\Reflection\ClassReflection($class); $class = $reflection->getName(); if (!$reflection->implementsInterface('Nette\Application\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; }
function tokenize($input) { $this->input = $input; if ($this->names) { $this->tokens = String::matchAll($input, $this->re); $len = 0; foreach ($this->tokens as & $match) { $name = NULL; for ($i = 1; $i < count($this->names); $i++) { if (!isset($match[$i])) { break; } elseif ($match[$i] != NULL) { $name = $this->names[$i - 1]; break; } } $match = array($match[0], $name); $len += strlen($match[0]); } if ($len !== strlen($input)) { $errorOffset = $len; } } else { $this->tokens = String::split($input, $this->re, PREG_SPLIT_NO_EMPTY); if ($this->tokens && !String::match(end($this->tokens), $this->re)) { $tmp = String::split($this->input, $this->re, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); list(, $errorOffset) = end($tmp); } } if (isset($errorOffset)) { $line = $errorOffset ? substr_count($this->input, "\n", 0, $errorOffset) + 1 : 1; $col = $errorOffset - strrpos(substr($this->input, 0, $errorOffset), "\n") + 1; $token = str_replace("\n", '\n', substr($input, $errorOffset, 10)); throw new TokenizerException("Unexpected '$token' on line $line, column $col."); } return $this; }
/** * Matches next token. * @param string * @return array */ private function match($re) { if ($matches = String::match($this->input, $re, PREG_OFFSET_CAPTURE, $this->offset)) { $this->output .= substr($this->input, $this->offset, $matches[0][1] - $this->offset); $this->offset = $matches[0][1] + strlen($matches[0][0]); foreach ($matches as $k => $v) $matches[$k] = $v[0]; } return $matches; }
/** * 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; } } } }
/** * @param string $header * @return string */ public static function getContentType($header, $default = NULL) { $match = String::match($header, self::CONTENT_TYPE); return isset($match['type']) ? $match['type'] : $default; }
/** * 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]+$/'); }
/** * Maps HTTP request to a PresenterRequest object. * @param Nette\Web\IHttpRequest * @return PresenterRequest|NULL */ public function match(Nette\Web\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())); }
/** * Builds HTML content. * @return void */ protected function buildHtml() { if ($this->html instanceof Nette\Templates\ITemplate) { $this->html->mail = $this; if ($this->basePath === NULL && $this->html instanceof Nette\Templates\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})"; }
/** * @return string */ public function getClassName() { return ($tmp = Nette\String::match($this, '#>\\s+([a-z0-9_\\\\]+)#i')) ? $tmp[1] : NULL; }
/** * 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; }
/** * Reads single token (optionally delimited by comma) from string. * @param string * @return string */ public function fetchToken(& $s) { if ($matches = String::match($s, '#^((?>'.LatteFilter::RE_STRING.'|[^\'"\s,]+)+)\s*,?\s*(.*)$#s')) { // token [,] tail $s = $matches[2]; return $matches[1]; } return NULL; }
/** * Changes current action. Only alphanumeric characters are allowed. * @param string * @return void */ public function changeAction($action) { if (Nette\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--; } } }
private function parseCallback() { $line = $this->currentToken[2]; if(String::match($this->currentToken[1], "/::/")) { $this->onOutput(self::OI_POSSIBLE_CALLBACK, array( self::FILE => $this->currentFile, self::LINE => $line, self::NAMESPACE_NAME => $this->namespace, self::VALUE => $this->currentToken[1], )); } }