<?php
/**
* dbscript for PHP 4 & 5 - restful crud framework
* @version 0.3.0 -- 10-Jun-2007
* @author Brian Hendrickson <hide@address.com>
* @link http://dbscript.net/
* @copyright Copyright 2007 Brian Hendrickson
* @package dbscript
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* URI Mapper
*
* connects the current URI to a Route,
* establishing the request variable names
* e.g. my_domain/:resource/:id would map
* values into $req->resource and $req->id
*
* Usage:
* <code>
* $req = new Mapper();
* </code>
*
* More info...
* {@link http://dbscript.net/mapper}
*
* @package dbscript
* @author Brian Hendrickson <hide@address.com>
* @access public
* @return object
* @version 0.3.0
*/
class Mapper {
/**
* current URI
* @var string
*/
var $uri;
/**
* domain in URI
* @var string
*/
var $domain;
/**
* path after domain in URI
* @var string
*/
var $path;
/**
* base URI
* @var string
*/
var $base;
/**
* unmolested regex parts of the URI
* @var string[]
*/
var $values;
/**
* URI parameter names and values
* @var string[]
*/
var $params;
/**
* matched Route object
* @var Route
*/
var $activeroute;
/**
* list of connected Route objects
* @var Route[]
*/
var $routes;
/**
* list of Groups
* @var string[]
*/
var $groups;
/**
* list of public methods
* @var string[]
*/
var $allowed_methods;
/**
* parameters to (silently) propagate
* @var string[]
*/
var $persisted_vars;
/**
* path to views
* @var string
*/
var $template_path;
/**
* path to layouts
* @var string
*/
var $layout_path;
/**
* id of encrypted Cookie owner
* @var string
*/
var $userid;
/**
* true if an error has been raised
* @var boolean
*/
var $error;
/**
* openid status
* @var boolean
*/
var $openid_complete;
/**
* contents of error message
* @var string
*/
var $error_string;
/**
* database Record object for the current session
* @var string
*/
var $DbSession;
function Mapper() {
$this->uri = $this->composite_uri();
preg_match( "/^(https?:\/\/)([^\/]+)\/?[^\?]+?[\??]([\w\/\.]+)?/i", $this->uri, $this->values );
if (!($this->values))
preg_match( "/^(https?:\/\/)([^\/]+)\/?(([^\?]+))?/i", $this->uri, $this->values );
if ( isset( $this->values[3] ) )
$this->params = explode( '/', $this->values[3] );
$qp = strpos($this->uri,"?");
$end = 0 - (strlen($this->uri) - $qp);
$lenbase = strlen($this->values[1]) + strlen($this->values[2]);
$this->path = substr($this->uri, $lenbase, $end);
$this->domain = $this->values[2];
$this->base = $this->uri;
$this->routes = array();
$this->persisted_vars = array();
$this->allowed_methods = array();
$this->groups = array();
$this->template_path = '';
$this->layout_path = '';
$this->error = false;
$this->openid_complete = false;
$this->userid = 0;
}
function handle_error( $errstr ) {
$this->error = true;
$this->params['error'] .= $errstr . "\n";
trigger_before( 'handle_error', $this, $errstr );
}
function url_for( $params, $altparams = NULL ) {
$match = false;
$route_match = NULL;
if ( is_string( $params ) ) {
// first var is a route name (or a URL)
if (strstr($params,"http")) {
header( "Location:" . $params );
exit;
}
$routename = $params;
$params = $altparams;
}
foreach ( $this->routes as $r ) {
$vars = array();
foreach ( $r->patterns as $pos => $str ) {
if ( substr( $str, 0, 1 ) == ':' ) {
$vars[substr( $str, 1 )] = $pos;
}
}
if ( isset( $routename ) ) {
if ( $routename == $r->name ) {
// a named route was found
if ($altparams == NULL)
$params = $r->defaults;
return $r->build_url( $params, $this->base );
}
} elseif ( count( array_intersect( array_keys($vars), array_keys($params) ) ) == count( $vars ) && count($vars) == count($params) ) {
// every pattern in the route exists in the requested params
return $r->build_url( $params, $this->base );
} else {
// eh
}
}
foreach ( $this->params as $paramkey=>$paramval ) {
if ( is_integer( $paramkey ) )
continue;
$params[$paramkey] = $paramval;
foreach ( $this->routes as $r ) {
$vars = array();
foreach ( $r->patterns as $pos => $str ) {
if ( substr( $str, 0, 1 ) == ':' ) {
$vars[substr( $str, 1 )] = $pos;
}
}
if ( count( array_intersect( array_keys($vars), array_keys($params) ) ) == count( $vars ) && count($vars) == count($params) ) {
return $r->build_url( $params, $this->base );
}
} // end foreach routes
} // end foreach params
}
function link_to( $params, $altparams = NULL ) {
$url = $this->url_for( $params, $altparams );
return "<a href=\"$url\">$url</a>";
}
function redirect_to( $params, $altparams = NULL ) {
header( "Location: " . $this->url_for($params, $altparams) );
}
function breadcrumbs() {
$controller = $this->params['resource'];
$links = array();
$html = "";
$links[] = '<a href="'. $this->base .'">Contents</a>';
if ( isset( $this->resource ) && ( $this->resource != 'introspection' ))
$links[] = '<a href="'. $this->base .'?'.$this->resource.'">'.ucwords($this->resource).'</a>';
if ( ($this->id != 0) && isset( $this->resource ) && ( $this->resource != 'introspection' ))
$links[] = '<a href="'.$this->entry_url($this->id).'">Entry '.ucwords($this->id).'</a>';
elseif ( isset( $this->resource ) && $this->new_url())
$links[] = '<a href="'.$this->new_url().'">New '.classify($this->resource).'</a>';
$html = "<span>";
foreach ($links as $key=>$val) {
if ($key > 0) {
$html .= " | ";
}
$html .= $val;
}
$html .= "</span>";
return $html;
}
function set_persisted_vars($arr) {
if (is_array($arr))
$this->persisted_vars = $arr;
}
function set_filter( $name, $func, $when = 'after' ) {
aspect_join_functions( $func, $name, $when );
}
function set_action( $method ) {
$this->allowed_methods[] = $method;
}
function set_param( $param, $value ) {
$this->params[$param] = $value;
$this->$param =& $this->params[$param];
}
function set_layout_path( $path ) {
$this->layout_path = $path;
}
function set_template_path( $path ) {
$this->template_path = $path;
}
function feed_url() {
$result = false;
if ( $this->action=='login' )
return $result;
if (isset($this->resource))
$result = is_file( $this->template_path . $this->resource . DIRECTORY_SEPARATOR. '_index.atom' );
if ($result)
return $this->url_for( array('resource'=>$this->resource, 'action'=>'index.atom'));
return $result;
}
function entry_url( $id = NULL ) {
$result = false;
if (isset($this->resource))
$result = is_file( $this->template_path . $this->resource . DIRECTORY_SEPARATOR. '_entry.json' );
if (!$result)
$result = is_file( $this->template_path . $this->resource . DIRECTORY_SEPARATOR. '_entry.html' );
if ($result && ($id != NULL))
return $this->url_for( array('resource'=>$this->resource, 'action'=>'entry', 'id'=>$id));
if ($result)
return $this->url_for( array('resource'=>$this->resource, 'action'=>'entry'));
return $result;
}
function new_url() {
$result = false;
if (isset($this->resource))
$result = is_file( $this->template_path . $this->resource . DIRECTORY_SEPARATOR. '_new.json' );
if (!$result)
$result = is_file( $this->template_path . $this->resource . DIRECTORY_SEPARATOR. '_new.html' );
if ($result)
return $this->url_for( array('resource'=>$this->resource, 'action'=>'new'));
return $result;
}
function get_template_path( $ext, $template = null ) {
if (isset($this->params['resource']))
$resource = $this->params['resource'] . DIRECTORY_SEPARATOR;
else
$resource = "";
if ($template == null) {
$partial = false;
$template = $this->params['action'];
} else {
$partial = true;
$template = "_" . $template;
}
if ($template == 'get')
$template = 'index';
if (isset($this->params['client_wants']))
$ext = $this->params['client_wants'];
$view = $this->template_path . $resource . $template . "." . $ext;
if (!(is_file($view)))
$view = $this->template_path . $template . "." . $ext;
if (!$partial && !(is_file($view)))
$view = $this->template_path . $resource . 'index' . "." . $ext;
if (!$partial && !(is_file($view)))
$view = $this->template_path . 'index' . "." . $ext;
if (!$partial) {
if ($this->action == 'get')
$action = 'index';
else
$action = $this->action;
if ((!(file_exists($this->template_path . $resource . "_" . $action . "." . $ext)))
&& (!(file_exists($this->template_path . "_" . $action . "." . $ext))))
return false;
}
if (is_file($view))
return $view;
else
return false;
}
function is_allowed( $method ) {
return in_array( $method, $this->allowed_methods, true );
}
function connect() {
// connect a Route to the Mapper
$r = new Route();
$args = func_get_args();
foreach ( $args as $idx => $arg ) {
if ( is_string( $arg ) ) {
$r->patterns = explode( '/', $arg );
if ( count( $r->patterns ) == 1 && $idx == 0 )
$r->name = $r->patterns[0];
} elseif ( is_array( $arg ) ) {
foreach ( $arg as $key => $val ) {
if ( $key == 'requirements' ) {
$i = 0;
foreach ( $r->patterns as $pos => $str ) {
if ( substr( $str, 0, 1 ) == ':' ) {
$r->requirements[$pos] = $val[$i];
$i++;
}
}
} else {
$r->defaults[$key] = $val;
}
}
}
}
$this->routes[] = $r;
}
function generate( $controller='index.php', $action='get' ) {
// Generate a route from a set of keywords and return the url
}
function routematch( $url = NULL ) {
// Match a URL against against one of the routes contained.
$return = false;
trigger_before( 'routematch', $this, $this->activeroute );
if ($url === NULL) $url = $this->uri;
foreach ( $this->routes as $route ) {
if ($this->match( $url, $route )) {
break;
$return = true;
}
}
if ( isset( $this->params['method'] ) ) $this->action = $this->method;
if ( isset( $this->params['forward_to'] ) ) $this->controller = $this->forward_to;
if ( isset( $this->action )) {
if (!(strpos($this->action,".") === false)) { // check for period
$actionsplit = split("\.", $this->action);
$this->set_param( 'action', $actionsplit[0]);
$this->set_param( 'client_wants', $actionsplit[1] );
}
}
if (isset($this->resource)) {
if (!(strpos($this->resource,".") === false)) { // check for period
$actionsplit = split("\.", $this->resource);
$this->set_param( 'resource', $actionsplit[0]);
$this->set_param( 'client_wants', $actionsplit[1] );
}
}
trigger_after( 'routematch', $this, $this->activeroute );
return $return;
}
function match( $url, $r ) {
foreach ( $r->patterns as $idx => $value ) {
if ( !( isset( $this->params[$idx] ) ) ) {
return false;
}
}
$i = 0;
$regx = array();
foreach ( $r->patterns as $pos => $str ) {
if ( substr( $str, 0, 1 ) == ':' ) {
if ( isset( $r->requirements[$pos] ) ) {
$regx[] = $r->requirements[$pos];
} else {
$regx[] = '(.+)';
}
$i++;
} else {
$regx[] = $str;
}
}
$params = $this->params;
while ( count( $params ) > count( $regx ) ) {
array_shift( $params );
}
if ( count( $r->patterns ) == 0 ) {
$r->match = true;
$pmatches = array();
} elseif ( preg_match( "/\/" . implode( "\/", $regx ) . "/i", "/" .implode( "/", $params ), $pmatches ) ) {
$this->base = substr( $this->uri, 0, -strlen($pmatches[0]));
$r->match = true;
}
if ($r->match) {
$this->activeroute =& $r;
$this->params = array_merge( $_GET, $_POST, $r->defaults, $this->params );
foreach ( $this->params as $p=>$v ) {
if ( !( isset( $this->$p ) ) )
$this->$p =& $this->params[$p];
}
foreach ( $r->patterns as $idx => $val ) {
if ( substr( $val, 0, 1 ) == ':' ) {
$val = substr( $val, 1);
if ( isset( $params[$idx] ) ) $this->params[$val] = $params[$idx];
}
}
}
return $r->match;
}
function composite_uri() {
// cross platform URI code by Angsuman Chakraborty
$port = "";
if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS']=='on' ) {
$_SERVER['FULL_URL'] = 'https://';
if ( $_SERVER['SERVER_PORT']!='443' ) {
$port = ':' . $_SERVER['SERVER_PORT'];
}
} else {
$_SERVER['FULL_URL'] = 'http://';
if ( $_SERVER['SERVER_PORT']!='80' ) {
$port = ':' . $_SERVER['SERVER_PORT'];
}
}
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
$script = $_SERVER['REQUEST_URI'];
} else {
$script = $_SERVER['PHP_SELF'];
if ( $_SERVER['QUERY_STRING']>' ' ) {
$script .= '?'.$_SERVER['QUERY_STRING'];
}
}
if ( isset( $_SERVER['HTTP_HOST'] ) ) {
$_SERVER['FULL_URL'] .= $_SERVER['HTTP_HOST'] . $port . $script;
} else {
$_SERVER['FULL_URL'] .= $_SERVER['SERVER_NAME'] . $port . $script;
}
return $_SERVER['FULL_URL'];
}
function hasErrors() {
if ( $this->error === true )
return true;
return false;
}
function propagate() {
$allowed = $this->persisted_vars;
$_SESSION['params'] = array();
foreach( $this->params as $param=>$val ) {
if (in_array($param, $allowed)) {
$_SESSION['params'][$param] = $val;
}
}
}
function restore() {
if (!(isset($_SESSION['params']))) return false;
foreach( $_SESSION['params'] as $param=>$val ) {
$this->params[$param] = $val;
$this->$param =& $this->params[$param];
}
}
}
?>