Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /*
- * Aktuální verzi, popis a příklady použití najdete na adrese: http://projekty.vize.name/router/
- */
- /**
- * Představuje jednu routu.
- * Zadané reguláry pro jednotlivé části routy implicitně rozlišují velikost písmen.
- *
- * @author Jakub Kulhan (původní verze routeru), http://bukaj.netuje.cz/blog/jednoduchy-routing-v-php
- * @author Viktorie Halasu (rozšíření), http://projekty.vize.name/router/
- * @property-read array $redirParams Pole parametrů pro vytvoření URL, na kterou se tato routa přesměrovává.
- * @property-read array $parsedUrl Pole parametrů z URL, která byla rozebrána podle této routy.
- * @property-read string $lastCreatedUrl Poslední vytvořená URL.
- * @package Router
- * @version 1.3
- */
- class Route {
- /** @var int Modifikátor. Určuje, že z této routy se nebude dělat polotovar. */
- const FIXED = 1;
- /** @var int Modifikátor. Určuje, že tato routa nerozlišuje velká a malá písmena. */
- const CI = 2;
- /** @var int Modifikátor. Požadavek odpovídající této routě se má ihned přesměrovat na novou URL. */
- const REDIR = 4;
- /** @var int Modifikátor. Zadaná routa je pro URL z parametrů (a má se zpracovat třídou SimpleRoute). */
- const SIMPLE = 8;
- /** @var string Výchozí regulár pro jednotlivé části routy (písmena bez diakritiky, číslice, podtržítko, tečka, pomlčka). */
- const DEFAULT_REGEX = '[A-Za-z0-9_.-]+';
- /** @var string Hodnota, kterou zachytí výchozí regulár. */
- const DEFAULT_VALUE = 'a';
- /** @var string Textová podoba této routy tak, jak byla zadaná. */
- protected $source = '';
- /** @var bool Má se tato routa přesměrovávat? */
- protected $isRedirected = false;
- /** @var int Modifikátory použité pro tuto routu (bitmask). */
- protected $flags = 0;
- /** @var array Parametry z rozebrané routy. */
- protected $params;
- /** @var array URL rozebraná podle této routy. */
- protected $parsedUrl = array();
- /** @var string Regulár pro tuto routu. */
- protected $regex;
- /** @var array Přesměrování (pole parametrů pro vytvoření URL.) */
- protected $redirParams;
- /** @var callback Callback pro přesměrování. */
- protected $redirCallback;
- /** @var string Formátovací řetězec pro URL. */
- protected $urlFormat;
- /** @var array Parametry, ze kterých byla naposled vytvořena URL. */
- protected $lastUrlParams;
- /** @var string Poslední vytvořená URL. */
- protected $lastCreatedUrl = '';
- /**
- * Parsuje zadání routy.
- * @param string $route
- * @param array $defaults Pole výchozích hodnot.
- * @param int $flags Modifikátory routy (bitmask). FIXED | CI | REDIR
- * @param mixed $redir Pokud se přesměrovávají nepoužívané URL na nové (flag REDIR), musí obsahovat buď pole hodnot
- * pro vytvoření nové URL, anebo callback, který toto pole vrací.
- * Callback funkce dostane jediný parametr - parsovanou routu (ze starého URL). Pokud je použité pole
- * a některý prvek má hodnotu Router::COPY_OLD_VALUE, zkopíruje se do něj hodnota ze stejnojmenného prvku
- * ve staré URL (např. "controller").
- */
- public function __construct($route, array $defaults = array(), $flags = 0, $redir = null) {
- $this->source = $route;
- if (is_int($flags)) {
- $this->flags = $flags;
- }
- $this->params = array(
- 'order' => array(), /* pořadí parametrů v routě (podle toho se složí výstupní URL): název => pořadí */
- 'fromRoute' => array(), /* jen parametry z routy s doplněnými výchozími hodnotami: název => [value => null|hodnota, optional => true|false] */
- 'allValues' => array(), /* parametry z routy i výchozí par. */
- );
- $this->urlFormat = '';
- $this->regex = '~^';
- $orderIndex = 0;
- foreach ($this->parse($this->source) as $i => $part) {
- $optional = false;
- if ($i % 2 === 0) {
- $this->urlFormat .= $part;
- $this->regex .= preg_quote($part, '~');
- } else {
- list($name, $partRegex) = explode("\037", $part);
- /* escapování oddělovače, zpětné lomítko je potřeba escapovat dvakrát */
- $partRegex = preg_replace('#(?<!\\\\)~#', '\\~', $partRegex);
- $this->urlFormat .= '%s';
- /* Je tato část routy nepovinná? */
- if ($name{0} === '?') {
- $optional = true;
- if (substr($this->regex, -1) === '/') {
- $this->regex .= '?';
- }
- $name = substr($name, 1);
- }
- /* Pokud není definovaný regulár pro tuto část routy, použije se výchozí */
- $this->regex .= ($optional ? '(?:' : '') . '(?<' . $name . '>' .
- (!empty($partRegex) ? $partRegex : self::DEFAULT_REGEX) .
- ')' . ($optional ? ')?' : '');
- /* Uloží se jednotlivé části routy a jejich vlastnosti. */
- $this->params['order'][$name] = $orderIndex;
- $this->params['fromRoute'][$name] = array(
- 'value' => null,
- 'optional' => $optional,
- );
- $this->params['allValues'][$name] = null;
- }
- $orderIndex++;
- }
- /* PCRE modif.: Unicode vždy, case-insensitive volitelně */
- $this->regex .= '$~u' . ($this->hasFlag(self::CI) ? 'i' : '');
- /* Přidají se výchozí hodnoty nebo parametry, pokud byly zadány. */
- if (!empty($defaults)) {
- foreach ($defaults as $key => $val) {
- if (!isset($this->params['fromRoute'][$key])) {
- $this->params['fromRoute'][$key]['value'] = $val;
- $this->params['fromRoute'][$key]['optional'] = false;
- }
- $this->params['allValues'][$key] = $val;
- }
- }
- if ($this->hasFlag(self::REDIR)) {
- $this->resolveRedirection($redir);
- }
- }
- /**
- * Pokusí se rozebrat zadanou URL podle této routy.
- * @param string $url
- * @return bool
- */
- public function matchUrl($url) {
- if (preg_match($this->regex, $url, $urlParams)) {
- $this->parsedUrl = $this->kmerge($this->params['allValues'], $urlParams);
- return true;
- }
- $this->parsedUrl = array();
- return false;
- }
- /**
- * Podle této routy zkusí ze zadaných parametrů vytvořit URL. Parametry mohou být v libovolném pořadí, nezávisle na pořadí v routě.
- * @param array $params Parametry. Musí být uvedeny i výchozí hodnoty (2.param konstruktoru). Nepovinné části routy nebo výchozí parametry pro celý router (1.param Router::__construct) lze vynechat.
- * @return bool
- */
- public function createUrlFromParams(array $params) {
- $vsparams = array();
- foreach ($this->params['fromRoute'] as $parName => $parProps) {
- /* Pokud je v routě víc povinných částí, než bylo zadáno parametrů, máme špatnou routu. */
- if (empty($params) && !$parProps['optional']) {
- return false;
- }
- if (isset($params[$parName])) {
- /* Výchozí parametry musí mít stejnou hodnotu. */
- if ($parProps['value'] !== null && $params[$parName] != $parProps['value']) {
- return false;
- }
- /* Pokud parametr patří do routy (= není výchozí), použije se. */
- if (isset($this->params['order'][$parName])) {
- $vsparams[$this->params['order'][$parName]] = $params[$parName];
- }
- unset($params[$parName]);
- }
- /* Parametr nebyl zadaný, patří do routy, ale je nepovinný: použije se prázdná hodnota. */
- elseif ($parProps['optional'] === false) {
- $vsparams[$this->params['order'][$parName]] = '';
- }
- }
- /* Pokud zbyly parametry, které nejsou v routě, máme špatnou routu. */
- if (!empty($params)) {
- $this->lastUrlParams = array();
- $this->lastCreatedUrl = '';
- return false;
- }
- ksort($vsparams);
- /* Pro vsprintf: počet zástupných znaků v this->urlFormat a vložených hodnot se musí shodovat. */
- $diff = count($this->params['order']) - count($vsparams);
- if ($diff > 0) {
- $vsparams = array_merge($vsparams, array_fill(0, $diff, ''));
- }
- /* Ověření hotové URL proti reguláru této routy. */
- $url = rtrim(vsprintf($this->urlFormat, $vsparams), '/');
- if (preg_match($this->regex, $url)) {
- $this->lastUrlParams = $vsparams;
- $this->lastCreatedUrl = $url;
- return true;
- } else {
- $this->lastUrlParams = array();
- $this->lastCreatedUrl = '';
- return false;
- }
- }
- /**
- * Změní hodnotu některých částí u poslední vytvořené URL a vrátí novou URL (pro šablony URL).
- * @param array $variables Proměnné části.
- *
- * @throws LogicException
- */
- public function addVariableUrlParts(array $variables) {
- if (empty($this->lastUrlParams)) {
- throw new LogicException("Nelze nastavit proměnné parametry routy. Nejdřív se musí vytvořit URL podle routy.");
- }
- foreach ($variables as $name => $value) {
- if (isset($this->lastUrlParams[$this->params['order'][$name]])) {
- $this->lastUrlParams[$this->params['order'][$name]] = $value;
- }
- }
- $this->lastCreatedUrl = rtrim(vsprintf($this->urlFormat, $this->lastUrlParams), '/');
- }
- /**
- * Zjistí, jestli má routa nastaven zadaný modifikátor
- * @param int $flag
- * @return bool
- */
- final public function hasFlag($flag) {
- return (is_int($flag) && $this->flags & $flag);
- }
- /**
- * Vrací pole parametrů pro vytvoření URL (u přesměrovávané routy)
- * @return array
- */
- final public function getRedirParams() {
- if (empty($this->redirParams)) {
- $this->redirParams = $this->invokeRedirCallback();
- }
- return $this->redirParams;
- }
- /**
- * Vrací rozebranou URL.
- * @return array
- */
- final public function getParsedUrl() {
- return $this->parsedUrl;
- }
- /**
- * Vrací naposled vytvořenou URL.
- * @return string
- */
- final public function getLastCreatedUrl() {
- return $this->lastCreatedUrl;
- }
- /**
- * Zjistí, jestli se tato routa má přesměrovávat.
- * @return bool
- */
- final public function isRedirected() {
- return $this->isRedirected;
- }
- /**
- * Parsuje zadanou routu.
- * @param string $route
- * @return array
- */
- protected function parse($route) {
- return explode("\036", trim(
- preg_replace(
- '~:(\??[A-Za-z0-9_]+)(?:<(.+?)>)?~',
- "\036\$1\037\$2\036",
- $route
- ), "\036")
- );
- }
- /**
- * Sloučí dvě asoc. pole. Podobné jako array_merge, ale nepřepisuje hodnotu v prvním poli prázdným řetězcem ani NULL.
- * @param array $arr1
- * @param array $arr2
- * @return array
- */
- final protected function kmerge($arr1, $arr2) {
- foreach ($arr2 as $key => $val) {
- if (!isset($arr1[$key])) {
- $arr1[$key] = $val;
- } elseif ($val !== '' && $val !== null) {
- $arr1[$key] = $val;
- }
- }
- return $arr1;
- }
- /**
- * Nastaví přesměrování
- * @param mixed $redir (Pole nebo callback).
- *
- * @throws InvalidArgumentException Pokud chybí cíl, nebo je špatného dat.typu
- */
- protected function resolveRedirection($redir = null) {
- if (empty($redir)) {
- throw new InvalidArgumentException("Routa se má přesměrovávat, ale chybí cíl (4. parametr).");
- }
- if (is_array($redir) && !is_int(key($redir))) {
- $this->redirParams = $redir;
- } else {
- /* ověření pomocí is_callable() se pro úsporu prostředků provádí až v invokeRedirCallback() */
- $this->redirCallback = $redir;
- }
- $this->isRedirected = true;
- }
- /**
- * Volá callback pro přesměrovávanou routu a vrací jeho výsledek.
- * @return array
- *
- * @throws RuntimeException Pokud je callback neplatný
- * @throws LogicException
- * @throws UnexpectedValueException Pokud callback nevrací pole.
- */
- protected function invokeRedirCallback() {
- if (!is_callable($this->redirCallback)) {
- throw new RuntimeException("Routa se má přesměrovávat, ale zadaný callback je neplatný.");
- } elseif (empty($this->parsedUrl)) {
- throw new LogicException("Přesměrování routy callbackem nelze volat dříve, než byla parsovaná URL.");
- }
- $redirParams = call_user_func($this->redirCallback, $this->parsedUrl);
- if (!is_array($redirParams)) {
- throw new UnexpectedValueException("Callback pro přesměrování routy musí vracet pole.");
- }
- return $redirParams;
- }
- /**
- * Vrací textové zadání této routy.
- * @return string
- */
- public function toSource() {
- return $this->source;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement