forked from GNUsocial/gnu-social
324 lines
9.3 KiB
PHP
324 lines
9.3 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* URL parser and mapper
|
||
|
*
|
||
|
* PHP version 5
|
||
|
*
|
||
|
* LICENSE:
|
||
|
*
|
||
|
* Copyright (c) 2006, Bertrand Mansion <golgote@mamasam.com>
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
*
|
||
|
* * Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* * The names of the authors may not be used to endorse or promote products
|
||
|
* derived from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* @category Net
|
||
|
* @package Net_URL_Mapper
|
||
|
* @author Bertrand Mansion <golgote@mamasam.com>
|
||
|
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||
|
* @version CVS: $Id: Mapper.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
|
||
|
* @link http://pear.php.net/package/Net_URL_Mapper
|
||
|
*/
|
||
|
|
||
|
require_once 'Net/URL/Mapper/Path.php';
|
||
|
require_once 'Net/URL/Mapper/Exception.php';
|
||
|
|
||
|
/**
|
||
|
* URL parser and mapper class
|
||
|
*
|
||
|
* This class takes an URL and a configuration and returns formatted data
|
||
|
* about the request according to a configuration parameter
|
||
|
*
|
||
|
* @category Net
|
||
|
* @package Net_URL_Mapper
|
||
|
* @author Bertrand Mansion <golgote@mamasam.com>
|
||
|
* @version Release: @package_version@
|
||
|
*/
|
||
|
class Net_URL_Mapper
|
||
|
{
|
||
|
/**
|
||
|
* Array of Net_URL_Mapper instances
|
||
|
* @var array
|
||
|
*/
|
||
|
private static $instances = array();
|
||
|
|
||
|
/**
|
||
|
* Mapped paths collection
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $paths = array();
|
||
|
|
||
|
/**
|
||
|
* Prefix used for url mapping
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $prefix = '';
|
||
|
|
||
|
/**
|
||
|
* Optional scriptname if mod_rewrite is not available
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $scriptname = '';
|
||
|
|
||
|
/**
|
||
|
* Mapper instance id
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $id = '__default__';
|
||
|
|
||
|
/**
|
||
|
* Class constructor
|
||
|
* Constructor is private, you should use getInstance() instead.
|
||
|
*/
|
||
|
private function __construct() { }
|
||
|
|
||
|
/**
|
||
|
* Returns a singleton object corresponding to the requested instance id
|
||
|
* @param string Requested instance name
|
||
|
* @return Object Net_URL_Mapper Singleton
|
||
|
*/
|
||
|
public static function getInstance($id = '__default__')
|
||
|
{
|
||
|
if (!isset(self::$instances[$id])) {
|
||
|
$m = new Net_URL_Mapper();
|
||
|
$m->id = $id;
|
||
|
self::$instances[$id] = $m;
|
||
|
}
|
||
|
return self::$instances[$id];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the instance id
|
||
|
* @return string Mapper instance id
|
||
|
*/
|
||
|
public function getId()
|
||
|
{
|
||
|
return $this->id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a path and creates a connection
|
||
|
* @param string The path to connect
|
||
|
* @param array Default values for path parts
|
||
|
* @param array Regular expressions for path parts
|
||
|
* @return object Net_URL_Mapper_Path
|
||
|
*/
|
||
|
public function connect($path, $defaults = array(), $rules = array())
|
||
|
{
|
||
|
$pathObj = new Net_URL_Mapper_Path($path, $defaults, $rules);
|
||
|
$this->addPath($pathObj);
|
||
|
return $pathObj;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the url prefix if needed
|
||
|
*
|
||
|
* Example: using the prefix to differenciate mapper instances
|
||
|
* <code>
|
||
|
* $fr = Net_URL_Mapper::getInstance('fr');
|
||
|
* $fr->setPrefix('/fr');
|
||
|
* $en = Net_URL_Mapper::getInstance('en');
|
||
|
* $en->setPrefix('/en');
|
||
|
* </code>
|
||
|
*
|
||
|
* @param string URL prefix
|
||
|
*/
|
||
|
public function setPrefix($prefix)
|
||
|
{
|
||
|
$this->prefix = '/'.trim($prefix, '/');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the scriptname if mod_rewrite not available
|
||
|
*
|
||
|
* Example: will match and generate url like
|
||
|
* - index.php/view/product/1
|
||
|
* <code>
|
||
|
* $m = Net_URL_Mapper::getInstance();
|
||
|
* $m->setScriptname('index.php');
|
||
|
* </code>
|
||
|
* @param string URL prefix
|
||
|
*/
|
||
|
public function setScriptname($scriptname)
|
||
|
{
|
||
|
$this->scriptname = $scriptname;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Will attempt to match an url with a defined path
|
||
|
*
|
||
|
* If an url corresponds to a path, the resulting values are returned
|
||
|
* in an array. If none is found, null is returned. In case an url is
|
||
|
* matched but its content doesn't validate the path rules, an exception is
|
||
|
* thrown.
|
||
|
*
|
||
|
* @param string URL
|
||
|
* @return array|null array if match found, null otherwise
|
||
|
* @throws Net_URL_Mapper_InvalidException
|
||
|
*/
|
||
|
public function match($url)
|
||
|
{
|
||
|
$nurl = '/'.trim($url, '/');
|
||
|
|
||
|
// Remove scriptname if needed
|
||
|
|
||
|
if (!empty($this->scriptname) &&
|
||
|
strpos($nurl, $this->scriptname) === 0) {
|
||
|
$nurl = substr($nurl, strlen($this->scriptname));
|
||
|
if (empty($nurl)) {
|
||
|
$nurl = '/';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove prefix
|
||
|
|
||
|
if (!empty($this->prefix)) {
|
||
|
if (strpos($nurl, $this->prefix) !== 0) {
|
||
|
return null;
|
||
|
}
|
||
|
$nurl = substr($nurl, strlen($this->prefix));
|
||
|
if (empty($nurl)) {
|
||
|
$nurl = '/';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove query string
|
||
|
|
||
|
if (($pos = strpos($nurl, '?')) !== false) {
|
||
|
$nurl = substr($nurl, 0, $pos);
|
||
|
}
|
||
|
|
||
|
$paths = array();
|
||
|
$values = null;
|
||
|
|
||
|
// Make a list of paths that conform to route format
|
||
|
|
||
|
foreach ($this->paths as $path) {
|
||
|
$regex = $path->getFormat();
|
||
|
if (preg_match($regex, $nurl)) {
|
||
|
$paths[] = $path;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make sure one of the paths found is valid
|
||
|
|
||
|
foreach ($paths as $path) {
|
||
|
$regex = $path->getRule();
|
||
|
if (preg_match($regex, $nurl, $matches)) {
|
||
|
$values = $path->getDefaults();
|
||
|
array_shift($matches);
|
||
|
$clean = array();
|
||
|
foreach ($matches as $k => $v) {
|
||
|
$v = trim($v, '/');
|
||
|
if (!is_int($k) && $v !== '') {
|
||
|
$values[$k] = $v;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// A path conforms but does not validate
|
||
|
|
||
|
if (is_null($values) && !empty($paths)) {
|
||
|
$e = new Net_URL_Mapper_InvalidException('A path was found but is invalid.');
|
||
|
$e->setPath($paths[0]);
|
||
|
$e->setUrl($url);
|
||
|
throw $e;
|
||
|
}
|
||
|
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generate an url based on given parameters
|
||
|
*
|
||
|
* Will attempt to find a path definition that matches the given parameters and
|
||
|
* will generate an url based on this path.
|
||
|
*
|
||
|
* @param array Values to be used for the url generation
|
||
|
* @param array Key/value pairs for query string if needed
|
||
|
* @param string Anchor (fragment) if needed
|
||
|
* @return string|false String if a rule was found, false otherwise
|
||
|
*/
|
||
|
public function generate($values = array(), $qstring = array(), $anchor = '')
|
||
|
{
|
||
|
// Use root path if any
|
||
|
|
||
|
if (empty($values) && isset($this->paths['/'])) {
|
||
|
return $this->scriptname.$this->prefix.$this->paths['/']->generate($values, $qstring, $anchor);
|
||
|
}
|
||
|
|
||
|
foreach ($this->paths as $path) {
|
||
|
$set = array();
|
||
|
foreach ($values as $k => $v) {
|
||
|
if ($path->hasKey($k, $v)) {
|
||
|
$set[$k] = $v;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count($set) == count($values) &&
|
||
|
count($set) <= $path->getMaxKeys()) {
|
||
|
|
||
|
$req = $path->getRequired();
|
||
|
if (count(array_intersect(array_keys($set), $req)) != count($req)) {
|
||
|
continue;
|
||
|
}
|
||
|
$gen = $path->generate($set, $qstring, $anchor);
|
||
|
return $this->scriptname.$this->prefix.$gen;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns defined paths
|
||
|
* @return array Array of paths
|
||
|
*/
|
||
|
public function getPaths()
|
||
|
{
|
||
|
return $this->paths;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset all paths
|
||
|
* This is probably only useful for testing
|
||
|
*/
|
||
|
public function reset()
|
||
|
{
|
||
|
$this->paths = array();
|
||
|
$this->prefix = '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a new path to the mapper
|
||
|
* @param object Net_URL_Mapper_Path object
|
||
|
*/
|
||
|
public function addPath(Net_URL_Mapper_Path $path)
|
||
|
{
|
||
|
$this->paths[$path->getPath()] = $path;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
?>
|