2erlei/fl/dispatch/route.php

367 lines
7.6 KiB
PHP

<?php
/**
* Routen
*
* Routen dienen der Zuordnung einer Adresszeile
* zu einem Modul.
*
* @package federleicht
* @subpackage base
* @author Matthias Viehweger <kronn@kronn.de>
* @version 0.3
*/
class fl_route {
protected $route = '';
protected $regex = '';
protected $modul = '';
protected $defaults = array();
protected $priority = 1;
protected $language_key = '';
protected $default_regex = array();
protected $partial_regex = array();
/**
* Konstruktor
*
* @param string $route
* @param string $regex
*/
public function __construct($route, $regex='') {
$this->route = (string) $route;
$this->default_regex = array(
'normal_item'=>'[-_0-9a-z\.]+',
'last_item'=>'[-_/0-9a-zA-Z%\.]+'
);
if ( $route === 'regex' AND $regex != '' ) {
$this->regex = $regex;
} else {
$this->regex = $this->compile($route);
}
$this->set_priority( 1 );
$this->set_defaults( array() );
}
/**
* Vereinfachte Objekterzeugung
*
* @pattern facade
*
* @param string $route
* @param string $defaults
* @param int $priority
* @param array $partial_regex
* @return fl_route
*/
public static function get_instance($route, $defaults, $priority, array $partial_regex=array()) {
$route_object = new self($route);
$defaults = is_string($defaults)?
fl_converter::string_to_array($defaults):
$defaults;
$route_object->set_defaults($defaults);
$route_object->set_priority((int) $priority);
$route_object->set_language_key('lang');
foreach ( $partial_regex as $part ) {
$route_object->set_partial_regex($part['key'], $part['regex']);
}
return $route_object;
}
/**
* Route zu regulärem Asudruck umwandeln
*
* @param string $route
* @return $string
*/
protected function compile($route) {
$elements = explode('/', $route);
$group_count = 0;
$beginning = '@^/';
$route_regex = '';
$end = '$@';
foreach( $elements as $key => $value ) {
if ( empty($value) ) {
unset($elements[$key]);
}
}
$elements = array_values($elements);
foreach( $elements as $key => $value ) {
if ( empty($value) ) continue;
$is_last = ( $key === ( count($elements) - 1 ) )? TRUE: FALSE;
if ( strpos($value, ':') === 0 ) {
$name = substr($value, 1);
$transformed_route = '(?P<'.$name.'>';
$regex = $this->get_partial_regex($name, $is_last);
if ( !empty( $regex ) ) {
$transformed_route .= $regex;
} elseif ( $is_last ) {
$transformed_route .= $this->default_regex['last_item'];
} else {
$transformed_route .= $this->default_regex['normal_item'];
}
$transformed_route .= ')';
if ( $group_count > 0 ) {
$transformed_route = '(?('.$group_count.')' . $transformed_route . '?+)';
}
$group_count++;
} else {
$transformed_route = $value;
}
$route_regex .= $transformed_route;
if ( !$is_last ) {
$route_regex .= '(/)?';
$group_count++;
}
}
$regex = $beginning . $route_regex . $end;
return $regex;
}
/**
* Versuchen, Route in URL zu erkennen
*
* @param string $url
* @param boolean $last_route
* @return boolean
*/
public function try_route($url, $last_route=FALSE) {
$treffer = array();
$host = ( isset($_SERVER['HTTP_HOST']) )? $_SERVER['HTTP_HOST']: 'localhost';
$parsed_url = parse_url('http://'.$host.'/'.ltrim($url,'/'));
$url_path = $parsed_url['path'];
$result = preg_match($this->regex, $url_path, $treffer);
$request = $this->defaults;
$request['query'] = (isset($parsed_url['query']))? $parsed_url['query']:'';
foreach ( $treffer as $key => $value ) {
if ( is_numeric($key) ) continue;
if ( empty($value) ) continue;
$request[$key] = $value;
}
if ( $url === '/' AND $last_route === TRUE ) {
$result = 1;
}
if ( $this->modul === $this->defaults['controller'] ) {
$request['modul'] = $request['controller'];
}
$this->request = $request;
$route_success = ( intval($result) > 0 )? TRUE: FALSE;
return $route_success;
}
/**
* Defaultwerte setzen
*
* @param array $defaults
* @param string $modul
*/
public function set_defaults(array $defaults, $modul=NULL) {
$this->defaults = array_merge($this->defaults, $defaults);
if ( is_null($modul) AND isset($this->defaults['controller']) ) {
$modul = $this->defaults['controller'];
} else {
$modul = '';
}
$this->set_modul($modul);
}
/**
* Priorität der Route setzen
*
* @param integer $priority
*/
public function set_priority($priority) {
$this->priority = (integer) $priority;
}
/**
* Defaultkey für Sprachinformationen setzen
*
* @param string $key
*/
public function set_language_key($key) {
$this->language_key = (string) $key;
}
/**
* Teilregeln für einzelne Routenbestandteile setzen
*
* Nach dem setzen der Regeln wird die Route neu kompiliert.
*
* @param string $key
* @param string $regex
*/
public function set_partial_regex($key, $regex) {
$this->partial_regex[$key] = $regex;
$this->regex = $this->compile($this->route);
}
/**
* Modulzugehörigkeit setzen
*
* @param string $modul
*/
public function set_modul($modul) {
$this->modul = (string) $modul;
}
/**
* Request-Daten holen
*
* @return array
*/
public function get_request() {
$request = array_merge($this->request, array('modul'=>$this->modul));
return $request;
}
/**
* Priorität holen
*
* @return integer
*/
public function get_priority() {
return $this->priority;
}
/**
* Teilregel für eine Routenbestandteil holen
*
* @param string $key
* @param boolean $is_last
* @return string
*/
public function get_partial_regex($key, $is_last = false) {
if ( isset( $this->partial_regex[$key] ) ) {
$regex = $this->partial_regex[$key];
} elseif ( $is_last ) {
$regex = $this->default_regex['last_item'];
} else {
$regex = $this->default_regex['normal_item'];
}
return $regex;
}
/**
* Sprachschlüssel holen
*
* Entweder ist der Sprachschlüssel mit $this->set_language_key
* gesetzt worden, oder es wird der letzte Eintrag der Default-
* Werte verwendet.
*
* @return string
*/
public function get_language_key() {
if ( isset( $this->language_key ) AND !empty($this->language_key) ) {
return $this->language_key;
} else {
return array_pop(array_keys($this->defaults));
}
}
/**
* URL erzeugen, die der aktuellen Route entspricht
*
* @param string $route
* @param array $parts
* @return string
*/
public function make_url($route, array $parts) {
$elements = explode('/', $route);
$url = '';
foreach( $elements as $key => $value ) {
if ( empty($value) ) {
unset($elements[$key]);
}
}
$elements = array_values($elements);
foreach( $elements as $key => $value ) {
if ( empty($value) ) continue;
if ( strpos($value, ':') === 0 ) {
$name = substr($value, 1);
$part = $parts[$name];
if ( !empty( $part ) ) {
$url .= $part;
} else {
$url .= $this->defaults[$name];
}
} else {
$url = $value;
}
$url .= '/';
}
return $url;
}
/**
* URL zurückgeben, die zur aktuellen Route passt
*
* @return string
*/
public function get_current_url() {
return $this->make_url($this->route, $this->request);
}
/**
* Vergleichsfunktion zur Sortierung von Routen
*
* Kann mit usort($array_of_routes, array('route', 'compare_routes');
* verwendet werden.
*
* @param fl_route $a
* @param fl_route $b
* @return integer
*/
public function compare_routes(fl_route $a, fl_route $b) {
$ap = $a->get_priority();
$bp = $b->get_priority();
if ( $ap == $bp ) {
$result = 0;
} else {
$result = ( $ap > $bp )? 1: -1;
}
return $result;
}
}