* // create an ezcUrlConfiguration object
* $urlCfg = new ezcUrlConfiguration();
* // set the basedir and script values
* $urlCfg->basedir = 'mydir';
* $urlCfg->script = 'index.php';
*
* // define delimiters for unordered parameter names
* $urlCfg->unorderedDelimiters = array( '(', ')' );
*
* // define ordered parameters
* $urlCfg->addOrderedParameter( 'section' );
* $urlCfg->addOrderedParameter( 'group' );
* $urlCfg->addOrderedParameter( 'category' );
* $urlCfg->addOrderedParameter( 'subcategory' );
*
* // define unordered parameters
* $urlCfg->addUnorderedParameter( 'game', ezcUrlConfiguration::MULTIPLE_ARGUMENTS );
*
* // create a new ezcUrl object from a string URL and use the above $urlCfg
* $url = new ezcUrl( 'http://www.example.com/mydir/index.php/groups/Games/Adventure/Adult/(game)/Larry/7', $urlCfg );
*
* // to get the parameter values from the URL use $url->getParam():
* $section = $url->getParam( 'section' ); // will be "groups"
* $group = $url->getParam( 'group' ); // will be "Games"
* $category = $url->getParam( 'category' ); // will be "Adventure"
* $subcategory = $url->getParam( 'subcategory' ); // will be "Adult"
* $game = $url->getParam( 'game' ); // will be array( "Larry", "7" )
*
*
* Example of aggregating values for unordered parameters:
*
* $urlCfg = new ezcUrlConfiguration();
*
* $urlCfg->addUnorderedParameter( 'param1', ezcUrlConfiguration::AGGREGATE_ARGUMENTS );
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
*
* $param1 = $url->getParam( 'param1' ); // will be array( array( "x" ), array( "y", "z" ) )
*
*
* Unordered parameters can also be fetched as a flat array (useful if the
* URL doesn't have delimiters for the unordered parameter names). Example:
*
* $urlCfg = new ezcUrlConfiguration();
* $urlCfg->basedir = '/mydir/shop';
* $urlCfg->script = 'index.php';
* $urlCfg->addOrderedParameter( 'module' );
*
* $url = new ezcUrl( 'http://www.example.com/mydir/shop/index.php/order/Software/PHP/Version/5.2/Extension/XDebug/Extension/openssl', $urlCfg );
*
* $params = $url->getParams(); // will be array( 'Software', 'PHP', 'Version', '5.2', 'Extension', 'XDebug', 'Extension', 'openssl' )
*
*
* @property string $host
* Hostname or null
* @property array(string) $path
* Complete path as an array.
* @property string $user
* User or null.
* @property string $pass
* Password or null.
* @property string $port
* Port or null.
* @property string $scheme
* Protocol or null.
* @property array(string=>mixed) $query
* Complete query string as an associative array.
* @property string $fragment
* Anchor or null.
* @property array(string) $basedir
* Base directory (the part before the script name) or null.
* @property array(string) $script
* Script name (eg. 'index.php') or null.
* @property array(string) $params
* Complete ordered parameters as array.
* @property array(string=>mixed) $uparams
* Complete unordered parameters as associative array.
* @property ezcUrlConfiguration $configuration
* The URL configuration defined for this URL, or null.
*
* @package Url
* @version 1.2alpha2
* @mainclass
*/
class ezcUrl
{
/**
* Holds the properties of this class.
*
* @var array(string=>mixed)
*/
private $properties = array();
/**
* Constructs a new ezcUrl object from the string $url.
*
* If the $configuration parameter is provided, then it will apply the
* configuration to the URL by calling {@link applyConfiguration()}.
*
* @param string $url A string URL from which to construct the URL object
* @param ezcUrlConfiguration $configuration An optional URL configuration used when parsing and building the URL
*/
public function __construct( $url = null, ezcUrlConfiguration $configuration = null )
{
$this->parseUrl( $url );
$this->configuration = $configuration;
if ( $configuration != null )
{
$this->applyConfiguration( $configuration );
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name The name of the property to set
* @param mixed $value The new value of the property
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'host':
case 'path':
case 'user':
case 'pass':
case 'port':
case 'scheme':
case 'fragment':
case 'query':
case 'basedir':
case 'script':
case 'params':
case 'uparams':
$this->properties[$name] = $value;
break;
case 'configuration':
if ( $value === null || $value instanceof ezcUrlConfiguration )
{
$this->properties[$name] = $value;
}
else
{
throw new ezcBaseValueException( $name, $value, 'instance of ezcUrlConfiguration' );
}
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
break;
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name The name of the property for which to return the value
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'host':
case 'path':
case 'user':
case 'pass':
case 'port':
case 'scheme':
case 'fragment':
case 'query':
case 'basedir':
case 'script':
case 'params':
case 'uparams':
case 'configuration':
return $this->properties[$name];
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name The name of the property to test if it is set
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'host':
case 'path':
case 'user':
case 'pass':
case 'port':
case 'scheme':
case 'fragment':
case 'query':
case 'basedir':
case 'script':
case 'params':
case 'uparams':
case 'configuration':
return isset( $this->properties[$name] );
default:
return false;
}
}
/**
* Returns this URL as a string by calling {@link buildUrl()}.
*
* @return string
*/
public function __toString()
{
return $this->buildUrl();
}
/**
* Parses the string $url and sets the class properties.
*
* @param string $url A string URL to parse
*/
private function parseUrl( $url = null )
{
$urlArray = parse_url( $url );
$this->properties['host'] = isset( $urlArray['host'] ) ? $urlArray['host'] : null;
$this->properties['user'] = isset( $urlArray['user'] ) ? $urlArray['user'] : null;
$this->properties['pass'] = isset( $urlArray['pass'] ) ? $urlArray['pass'] : null;
$this->properties['port'] = isset( $urlArray['port'] ) ? $urlArray['port'] : null;
$this->properties['scheme'] = isset( $urlArray['scheme'] ) ? $urlArray['scheme'] : null;
$this->properties['fragment'] = isset( $urlArray['fragment'] ) ? $urlArray['fragment'] : null;
$this->properties['path'] = isset( $urlArray['path'] ) ? explode( '/', trim( $urlArray['path'], '/' ) ) : array();
$this->properties['basedir'] = array();
$this->properties['script'] = array();
$this->properties['params'] = array();
$this->properties['uparams'] = array();
if ( isset( $urlArray['query'] ) )
{
$this->properties['query'] = ezcUrlTools::parseQueryString( $urlArray['query'] );
}
else
{
$this->properties['query'] = array();
}
}
/**
* Applies the URL configuration $configuration to the current url.
*
* It fills the arrays $basedir, $script, $params and $uparams with values
* from $path.
*
* It also sets the property configuration to the value of $configuration.
*
* @param ezcUrlConfiguration $configuration An URL configuration used in parsing
*/
public function applyConfiguration( ezcUrlConfiguration $configuration )
{
$this->configuration = $configuration;
$this->basedir = $this->parsePathElement( $configuration->basedir, 0 );
$this->script = $this->parsePathElement( $configuration->script, count( $this->basedir ) );
$this->params = $this->parseOrderedParameters( $configuration->orderedParameters, count( $this->basedir ) + count( $this->script ) );
$this->uparams = $this->parseUnorderedParameters( $configuration->unorderedParameters, count( $this->basedir ) + count( $this->script ) + count( $this->params ) );
}
/**
* Parses $path based on the configuration $config, starting from $index.
*
* Returns the first few elements of $this->path matching $config,
* starting from $index.
*
* @param string $config A string which will be matched against the path part of the URL
* @param int $index The index in the URL path part from where to start the matching of $config
* @return array(string=>mixed)
*/
private function parsePathElement( $config, $index )
{
$config = trim( $config, '/' );
$paramParts = explode( '/', $config );
$pathElement = array();
foreach ( $paramParts as $part )
{
if ( isset( $this->path[$index] ) && $part == $this->path[$index] )
{
$pathElement[] = $part;
}
$index++;
}
return $pathElement;
}
/**
* Returns ordered parameters from the $path array.
*
* @param array(string) $config An array of ordered parameters names, from the URL configuration used in parsing
* @param int $index The index in the URL path part from where to start the matching of $config
* @return array(string=>mixed)
*/
public function parseOrderedParameters( $config, $index )
{
$result = array();
$pathCount = count( $this->path );
for ( $i = 0; $i < count( $config ); $i++ )
{
if ( isset( $this->path[$index + $i] ) )
{
$result[] = $this->path[$index + $i];
}
else
{
$result[] = null;
}
}
return $result;
}
/**
* Returns unordered parameters from the $path array.
*
* The format of the returned array is:
*
* array( param_name1 => array( 0 => array( value1, value2, ... ),
* 1 => array( value1, value2, ... ) ),
* param_name2 = array( 0 => array( value1, value2, ... ),
* 1 => array( value1, value2, ... ) ), ... )
*
* where 0, 1, etc are numbers meaning the nth encounter of each param_name
* in the url.
*
* For example, if the URL is 'http://www.example.com/(param1)/a/(param2)/x/(param2)/y/z'
* then the result of this function will be:
*
* array( 'param1' => array( 0 => array( 'a' ) ),
* 'param2' => array( 0 => array( 'x' ),
* 1 => array( 'y', 'z' ) ) );
*
*
* For the URL 'http://www.example.com/(param1)/x/(param1)/y/z', these
* methods can be employed to get the values of param1:
*
* $urlCfg = new ezcUrlConfiguration();
*
* // single parameter value
* $urlCfg->addUnorderedParameter( 'param1' ); // type is SINGLE_ARGUMENT by default
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return "y"
*
* // multiple parameter values
* $urlCfg->addUnorderedParameter( 'param1', ezcUrlConfiguration::MULTIPLE_ARGUMENTS );
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return array( "y", "z" )
*
* // multiple parameter values with aggregation
* $urlCfg->addUnorderedParameter( 'param1', ezcUrlConfiguration::AGGREGATE_ARGUMENTS );
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return array( array( "x" ), array( "y", "z" ) )
*
*
* @param array(string) $config An array of unordered parameters names, from the URL configuration used in parsing
* @param int $index The index in the URL path part from where to start the matching of $config
* @return array(string=>mixed)
*/
public function parseUnorderedParameters( $config, $index )
{
$result = array();
// holds how many times a parameter name is encountered in the URL.
// for example, for '/(param1)/a/(param2)/x/(param2)/y',
// $encounters = array( 'param1' => 1, 'param2' => 2 );
$encounters = array();
$urlCfg = $this->configuration;
$pathCount = count( $this->path );
if ( $pathCount == 0 || ( $pathCount == 1 && trim( $this->path[0] ) === "" ) )
{
// special case: a bug? in parse_url() which makes $this->path
// be array( "" ) if the provided URL is null or empty
return $result;
}
for ( $i = $index; $i < $pathCount; $i++ )
{
$param = $this->path[$i];
if ( $param{0} == $urlCfg->unorderedDelimiters[0] )
{
$param = trim( trim( $param, $urlCfg->unorderedDelimiters[0] ), $urlCfg->unorderedDelimiters[1] );
if ( isset( $encounters[$param] ) )
{
$encounters[$param]++;
}
else
{
$encounters[$param] = 0;
}
$result[$param][$encounters[$param]] = array();
$j = 1;
while ( ( $i + $j ) < $pathCount && $this->path[$i + $j]{0} != $urlCfg->unorderedDelimiters[0] )
{
$result[$param][$encounters[$param]][] = trim( trim( $this->path[$i + $j], $urlCfg->unorderedDelimiters[0] ), $urlCfg->unorderedDelimiters[1] );
$j++;
}
}
}
return $result;
}
/**
* Returns this URL as a string.
*
* The query part of the URL is build with http_build_query() which
* encodes the query in a similar way to urlencode().
*
* If $includeScriptName is true, then the script name (eg. 'index.php')
* will be included in the result. By default the script name is hidden (to
* ensure backwards compatibility).
*
* @apichange The default value for $includeScriptName might be changed to
* true in future versions
*
* @param bool $includeScriptName
* @return string
*/
public function buildUrl( $includeScriptName = false )
{
$url = '';
if ( $this->scheme )
{
$url .= $this->scheme . '://';
}
if ( $this->host )
{
if ( $this->user )
{
$url .= $this->user;
if ( $this->pass )
{
$url .= ':' . $this->pass;
}
$url .= '@';
}
$url .= $this->host;
if ( $this->port )
{
$url .= ':' . $this->port;
}
}
if ( $this->configuration != null )
{
if ( $this->basedir )
{
if ( !( count( $this->basedir ) == 0 || trim( $this->basedir[0] ) === "" ) )
{
$url .= '/' . implode( '/', $this->basedir );
}
}
if ( $includeScriptName && $this->script )
{
if ( !( count( $this->script ) == 0 || trim( $this->script[0] ) === "" ) )
{
$url .= '/' . implode( '/', $this->script );
}
}
if ( $this->params && count( $this->params ) != 0 )
{
$url .= '/' . implode( '/', $this->params );
}
if ( $this->uparams && count( $this->uparams ) != 0 )
{
foreach ( $this->properties['uparams'] as $key => $encounters )
{
foreach ( $encounters as $encounter => $values )
{
$url .= '/(' . $key . ')/' . implode( '/', $values );
}
}
}
}
else
{
if ( $this->path )
{
$url .= '/' . implode( '/', $this->path );
}
}
if ( $this->query )
{
$url .= '?' . http_build_query( $this->query );
}
if ( $this->fragment )
{
$url .= '#' . $this->fragment;
}
return $url;
}
/**
* Returns true if this URL is relative and false if the URL is absolute.
*
* @return bool
*/
public function isRelative()
{
if ( $this->host === null || $this->host == '' )
{
return true;
}
return false;
}
/**
* Returns the value of the specified parameter from the URL based on the
* active URL configuration.
*
* Unordered parameter examples:
*
* $urlCfg = new ezcUrlConfiguration();
*
* // single parameter value
* $urlCfg->addUnorderedParameter( 'param1' ); // type is SINGLE_ARGUMENT by default
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return "y"
*
* // multiple parameter values
* $urlCfg->addUnorderedParameter( 'param1', ezcUrlConfiguration::MULTIPLE_ARGUMENTS );
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return array( "y", "z" )
*
* // multiple parameter values with aggregation
* $urlCfg->addUnorderedParameter( 'param1', ezcUrlConfiguration::AGGREGATE_ARGUMENTS );
* $url = new ezcUrl( 'http://www.example.com/(param1)/x/(param1)/y/z', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return array( array( "x" ), array( "y", "z" ) )
*
*
* Ordered parameter examples:
*
* $urlCfg = new ezcUrlConfiguration();
*
* $urlCfg->addOrderedParameter( 'param1' );
* $urlCfg->addOrderedParameter( 'param2' );
* $url = new ezcUrl( 'http://www.example.com/x/y', $urlCfg );
* $param1 = $url->getParam( 'param1' ); // will return "x"
* $param2 = $url->getParam( 'param2' ); // will return "y"
*
*
* @throws ezcUrlNoConfigurationException
* if an URL configuration is not defined
* @throws ezcUrlInvalidParameterException
* if the specified parameter is not defined in the URL configuration
* @param string $name The name of the parameter for which to return the value
* @return mixed
*/
public function getParam( $name )
{
$urlCfg = $this->configuration;
if ( $urlCfg != null )
{
if ( !( isset( $urlCfg->orderedParameters[$name] ) ||
isset( $urlCfg->unorderedParameters[$name] ) ) )
{
throw new ezcUrlInvalidParameterException( $name );
}
$params = $this->params;
$uparams = $this->uparams;
if ( isset( $urlCfg->orderedParameters[$name] ) &&
isset( $params[$urlCfg->orderedParameters[$name]] ) )
{
return $params[$urlCfg->orderedParameters[$name]];
}
if ( isset( $urlCfg->unorderedParameters[$name] ) &&
isset( $uparams[$name][0] ) )
{
if ( $urlCfg->unorderedParameters[$name] === ezcUrlConfiguration::SINGLE_ARGUMENT )
{
if ( count( $uparams[$name][0] ) > 0 )
{
return $uparams[$name][count( $uparams[$name] ) - 1][0];
}
else
{
return null;
}
}
else
{
if ( $urlCfg->unorderedParameters[$name] === ezcUrlConfiguration::AGGREGATE_ARGUMENTS )
{
$result = $uparams[$name];
return $result;
}
else
{
return $uparams[$name][count( $uparams[$name] ) - 1];
}
}
}
return null;
}
else
{
throw new ezcUrlNoConfigurationException( $name );
}
}
/**
* Sets the specified parameter in the URL based on the URL configuration.
*
* For ordered parameters, the value cannot be an array, otherwise an
* ezcBaseValueException will be thrown.
*
* For unordered parameters, the value can be one of:
* - string
* - array(string)
* - array(array(string))
*
* Any of these values can be assigned to an unordered parameter, whatever the
* parameter type (SINGLE_ARGUMENT, MULTIPLE_ARGUMENTS, AGGREGATE_ARGUMENTS).
*
* If there are ordered and unordered parameters with the same name, only the
* ordered parameter value will be set.
*
* Examples:
*
* $urlCfg = new ezcUrlConfiguration();
* $urlCfg->addUnorderedParameter( 'param1' );
*
* $url = new ezcUrl( 'http://www.example.com' );
*
* $url->setParam( 'param1', 'x' );
* echo $url->buildUrl(); // will output http://www.example.com/(param1)/x
*
* $url->setParam( 'param1', array( 'x', 'y' ) );
* echo $url->buildUrl(); // will output http://www.example.com/(param1)/x/y
*
* $url->setParam( 'param1', array( array( 'x' ), array( 'y', 'z' ) ) );
* echo $url->buildUrl(); // will output http://www.example.com/(param1)/x/(param1)/y/z
*
*
* @throws ezcBaseValueException
* if trying to assign an array value to an ordered parameter
* @throws ezcUrlNoConfigurationException
* if an URL configuration is not defined
* @throws ezcUrlInvalidParameterException
* if the specified parameter is not defined in the URL configuration
* @param string $name The name of the parameter to set
* @param string|array(string=>mixed) $value The new value of the parameter
*/
public function setParam( $name, $value )
{
$urlCfg = $this->configuration;
if ( $urlCfg != null )
{
if ( !( isset( $urlCfg->orderedParameters[$name] ) ||
isset( $urlCfg->unorderedParameters[$name] ) ) )
{
throw new ezcUrlInvalidParameterException( $name );
}
if ( isset( $urlCfg->orderedParameters[$name] ) )
{
if ( !is_array( $value ) )
{
$this->properties['params'][$urlCfg->orderedParameters[$name]] = $value;
}
else
{
throw new ezcBaseValueException( $name, $value, 'string' );
}
return;
}
if ( isset( $urlCfg->unorderedParameters[$name] ) )
{
if ( !isset( $this->properties['uparams'][$name] ) )
{
$this->properties['uparams'][$name] = array();
}
if ( is_array( $value ) )
{
$multiple = false;
foreach ( $value as $part )
{
if ( is_array( $part ) )
{
$this->properties['uparams'][$name] = $value;
$multiple = true;
break;
}
}
if ( !$multiple )
{
$this->properties['uparams'][$name][count( $this->properties['uparams'][$name] ) - 1] = $value;
}
}
else
{
$this->properties['uparams'][$name][count( $this->properties['uparams'][$name] ) - 1] = array( $value );
}
}
return;
}
else
{
throw new ezcUrlNoConfigurationException( $name );
}
}
/**
* Returns the unordered parameters from the URL as a flat array.
*
* It takes into account the basedir, script and ordered parameters.
*
* It can be used for URLs which don't have delimiters for the unordered
* parameters.
*
* Example:
*
* $urlCfg = new ezcUrlConfiguration();
* $urlCfg->basedir = '/mydir/shop';
* $urlCfg->script = 'index.php';
* $urlCfg->addOrderedParameter( 'module' );
*
* $url = new ezcUrl( 'http://www.example.com/mydir/shop/index.php/order/Software/PHP/Version/5.2/Extension/XDebug/Extension/openssl', $urlCfg );
*
* $params = $url->getParams(); // will be array( 'Software', 'PHP', 'Version', '5.2', 'Extension', 'XDebug', 'Extension', 'openssl' )
*
*
* @return array(string)
*/
public function getParams()
{
return array_slice( $this->path, count( $this->basedir ) + count( $this->script ) + count( $this->params ) );
}
/**
* Returns the query elements as an associative array.
*
* Example:
* for 'http://www.example.com/mydir/shop?content=view&products=10'
* returns array( 'content' => 'view', 'products' => '10' )
*
* @return array(string=>mixed)
*/
public function getQuery()
{
return $this->query;
}
/**
* Set the query elements using the associative array provided.
*
* Example:
* for 'http://www.example.com/mydir/shop'
* and $query = array( 'content' => 'view', 'products' => '10' )
* then 'http://www.example.com/mydir/shop?content=view&products=10'
*
* @param array(string=>mixed) $query The new value of the query part
*/
public function setQuery( $query )
{
$this->query = $query;
}
}
?>