--- /dev/null
+<?php
+/**
+ * This file contains the Horde_Url class for manipulating URLs.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @author Michael Slusarz <slusarz@curecanti.org>
+ * @category Horde
+ * @package Horde_Url
+ */
+
+/**
+ * The Horde_Url class represents a single URL and provides methods for
+ * manipulating URLs.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @author Michael Slusarz <slusarz@curecanti.org>
+ * @category Horde
+ * @package Horde_Url
+ */
+class Horde_Url
+{
+ /**
+ * The basic URL, without query parameters.
+ *
+ * @var string
+ */
+ public $url;
+
+ /**
+ * The query parameters.
+ *
+ * The keys are paramter names, the values parameter values. Array values
+ * will be added to the URL using name[]=value notation.
+ *
+ * @var array
+ */
+ public $parameters;
+
+ /**
+ * Whether to output the URL in the raw URL format or HTML-encoded.
+ *
+ * @var boolean
+ */
+ public $raw;
+
+ /**
+ * Constructor.
+ *
+ * @param string $url The basic URL, with or without query parameters.
+ * @param boolean $raw Whether to output the URL in the raw URL format or
+ * HTML-encoded.
+ */
+ public function __construct($url, $raw = false)
+ {
+ if (strpos($url, '?') !== false) {
+ list($url, $query) = explode('?', $url);
+
+ /* Check if the argument separator has been already
+ * htmlentities-ized in the URL. */
+ if (preg_match('/=.*?&.*?=/', $query)) {
+ $query = html_entity_decode($query);
+ $raw = false;
+ } elseif (preg_match('/=.*?&.*?=/', $query)) {
+ $raw = true;
+ }
+ $pairs = explode('&', $query);
+ foreach ($pairs as $pair) {
+ @list($parameter, $value) = explode('=', urldecode($pair), 2);
+ $this->add($parameter, $value);
+ }
+ }
+
+ $this->url = $url;
+ $this->raw = $raw;
+ }
+
+ /**
+ * Adds one or more query parameters.
+ *
+ * @param mixed $parameters Either the name value or an array of
+ * name/value pairs.
+ * @param string $value If specified, the value part ($parameters is
+ * then assumed to just be the parameter name).
+ *
+ * @return Horde_Url This (modified) object, to allow chaining.
+ */
+ public function add($parameters, $value = null)
+ {
+ if (!is_array($parameters)) {
+ $parameters = array($parameters => $value);
+ }
+
+ foreach ($parameters as $parameter => $value) {
+ if (substr($parameter, -2) == '[]') {
+ $parameter = substr($parameter, 0, -2);
+ if (!isset($this->parameters[$parameter])) {
+ $this->parameters[$parameter] = array();
+ }
+ $this->parameters[$parameter][] = $value;
+ } else {
+ $this->parameters[$parameter] = $value;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Removes one ore more parameters.
+ *
+ * @param mixed $remove Either a single parameter to remove or an array
+ * of parameters to remove.
+ *
+ * @return Horde_Url This (modified) object, to allow chaining.
+ */
+ public function remove($parameters)
+ {
+ if (!is_array($parameters)) {
+ $parameters = array($parameters);
+ }
+
+ foreach ($parameters as $parameter) {
+ unset($this->parameters[$parameter]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Creates the full URL string.
+ *
+ * @return string The string representation of this object.
+ */
+ public function __toString()
+ {
+ $url_params = array();
+ foreach ($this->parameters as $parameter => $value) {
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ $url_params[] = rawurlencode($parameter) . '[]=' . rawurlencode($val);
+ }
+ } else {
+ if (strlen($value)) {
+ $url_params[] = rawurlencode($parameter) . '=' . rawurlencode($value);
+ } else {
+ $url_params[] = rawurlencode($parameter);
+ }
+ }
+ }
+
+ return count($url_params)
+ ? $this->url . '?' . implode($this->raw ? '&' : '&', $url_params)
+ : $this->url;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Url</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Url class</summary>
+ <description>This class represents a single URL and provides methods for manipulating URLs.</description>
+ <lead>
+ <name>Jan Schneider</name>
+ <user>jan</user>
+ <email>jan@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Michael Slusarz</name>
+ <user>slusarz</user>
+ <email>slusarz@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <date>2009-12-03</date>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial package.
+ </notes>
+ <contents>
+ <dir name="/">
+ <dir name="lib">
+ <dir name="Horde">
+ <file name="Url.php" role="php" />
+ </dir> <!-- /lib/Horde -->
+ </dir> <!-- /lib -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.5.4</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install name="lib/Horde/Url.php" as="Horde/Url.php" />
+ </filelist>
+ </phprelease>
+ <changelog/>
+</package>
--- /dev/null
+<?php
+/**
+ * @author Jan Schneider <jan@horde.org>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @category Horde
+ * @package Horde_Url
+ * @subpackage UnitTests
+ */
+
+class Horde_Url_AddTest extends PHPUnit_Framework_TestCase
+{
+ public function testAddSimple()
+ {
+ $url = new Horde_Url('test');
+ $url->add('foo', 1);
+ $this->assertEquals('test?foo=1', (string)$url);
+ $url->add('bar', 2);
+ $this->assertEquals('test?foo=1&bar=2', (string)$url);
+ $url->add('baz', 3);
+ $this->assertEquals('test?foo=1&bar=2&baz=3', (string)$url);
+ }
+
+ public function testAddArray()
+ {
+ $url = new Horde_Url('test');
+ $url->add(array('foo' => 1, 'bar' => 2));
+ $this->assertEquals('test?foo=1&bar=2', (string)$url);
+
+ $url = new Horde_Url('test?foo=1');
+ $url->add(array('bar' => 2, 'baz' => 3));
+ $this->assertEquals('test?foo=1&bar=2&baz=3', (string)$url);
+ }
+
+ public function testAddToExistingUrl()
+ {
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $url->add(array('baz' => 3));
+ $this->assertEquals('test?foo=1&bar=2&baz=3', (string)$url);
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $url->add(array('foo' => 1, 'bar' => 3));
+ $this->assertEquals('test?foo=1&bar=3', (string)$url);
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $url->add('baz', 3);
+ $this->assertEquals('test?foo=1&bar=2&baz=3', (string)$url);
+ }
+
+ public function testAddRaw()
+ {
+ $url = new Horde_Url('test');
+ $url->add('foo', 'bar&baz');
+ $this->assertEquals('test?foo=bar%26baz', (string)$url);
+ $url->add('x', 'y');
+ $this->assertEquals('test?foo=bar%26baz&x=y', (string)$url);
+ $url->raw = true;
+ $url->add('x', 'y');
+ $this->assertEquals('test?foo=bar%26baz&x=y', (string)$url);
+
+ $url = new Horde_Url('test');
+ $url->add('x', 'y')
+ ->add('foo', 'bar&baz');
+ $this->assertEquals('test?x=y&foo=bar%26baz', (string)$url);
+ }
+
+ public function testAddChaining()
+ {
+ $url = new Horde_Url('test');
+ $url->add('foo', 1)
+ ->add('bar', 2)
+ ->add('baz', 3);
+ $this->assertEquals('test?foo=1&bar=2&baz=3', (string)$url);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Horde_Url test suite
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @category Horde
+ * @package Horde_Url
+ * @subpackage UnitTests
+ */
+
+/**
+ * Define the main method
+ */
+if (!defined('PHPUnit_MAIN_METHOD')) {
+ define('PHPUnit_MAIN_METHOD', 'Horde_Url_AllTests::main');
+}
+
+/**
+ * Prepare the test setup.
+ */
+require_once 'Horde/Test/AllTests.php';
+
+/**
+ * @package Horde_Url
+ * @subpackage UnitTests
+ */
+class Horde_Url_AllTests extends Horde_Test_AllTests
+{
+}
+
+if (PHPUnit_MAIN_METHOD == 'Horde_Url_AllTests::main') {
+ Horde_Url_AllTests::main('Horde_Url', __FILE__);
+}
--- /dev/null
+<?php
+/**
+ * @author Jan Schneider <jan@horde.org>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @category Horde
+ * @package Horde_Url
+ * @subpackage UnitTests
+ */
+
+class Horde_Url_RemoveTest extends PHPUnit_Framework_TestCase
+{
+ public function testRemoveRaw()
+ {
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test?bar=2', (string)$url->remove('foo'));
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test?foo=1', (string)$url->remove('bar'));
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test', (string)$url->remove(array('foo', 'bar')));
+
+ $url = new Horde_Url('test?foo=1&bar=2&baz=3');
+ $this->assertEquals('test?bar=2&baz=3', (string)$url->remove('foo'));
+ }
+
+ public function testRemoveEncoded()
+ {
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test?bar=2', (string)$url->remove('foo'));
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test?foo=1', (string)$url->remove('bar'));
+
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test', (string)$url->remove(array('foo', 'bar')));
+
+ $url = new Horde_Url('test?foo=1&bar=2&baz=3');
+ $this->assertEquals('test?bar=2&baz=3', (string)$url->remove('foo'));
+ }
+
+ public function testRemoveChaining()
+ {
+ $url = new Horde_Url('test?foo=1&bar=2');
+ $this->assertEquals('test', (string)$url->remove('foo')->remove('bar'));
+ }
+}
return $url;
}
- $add = array();
- $arg = $encode ? '&' : '&';
-
- if (strpos($url, '?') !== false) {
- list($url, $query) = explode('?', $url);
-
- /* Check if the argument separator has been already
- * htmlentities-ized in the URL. */
- if (preg_match('/=.*?&.*?=/', $query)) {
- $query = html_entity_decode($query);
- $arg = '&';
- } elseif (preg_match('/=.*?&.*?=/', $query)) {
- $arg = '&';
- }
- $pairs = explode('&', $query);
- foreach ($pairs as $pair) {
- $pair = explode('=', urldecode($pair), 2);
- $pair_val = (count($pair) == 2) ? $pair[1] : '';
- if (substr($pair[0], -2) == '[]') {
- $name = substr($pair[0], 0, -2);
- if (!isset($add[$name])) {
- $add[$name] = array();
- }
- $add[$name][] = $pair_val;
- } else {
- $add[$pair[0]] = $pair_val;
- }
- }
- }
-
- if (is_array($parameter)) {
- $add = array_merge($add, $parameter);
- } else {
- $add[$parameter] = $value;
- }
-
- $url_params = array();
- foreach ($add as $parameter => $value) {
- if (is_array($value)) {
- foreach ($value as $val) {
- $url_params[] = rawurlencode($parameter) . '[]=' . rawurlencode($val);
- }
- } else {
- $url_params[] = rawurlencode($parameter) . '=' . rawurlencode($value);
- }
+ if ($url instanceof Horde_Url) {
+ $url->raw = !$encode;
+ return $url->add($parameter, $value);
}
- return count($url_params)
- ? $url . '?' . implode($arg, $url_params)
- : $url;
+ $horde_url = new Horde_Url($url, !$encode);
+ return $horde_url->add($parameter, $value);
}
/**
*/
static public function removeParameter($url, $remove)
{
- if (!is_array($remove)) {
- $remove = array($remove);
- }
-
- /* Return immediately if there are no parameters to remove. */
- if (($pos = strpos($url, '?')) === false) {
- return $url;
- }
-
- $entities = false;
- list($url, $query) = explode('?', $url, 2);
-
- /* Check if the argument separator has been already
- * htmlentities-ized in the URL. */
- if (preg_match('/=.*?&.*?=/', $query)) {
- $entities = true;
- $query = html_entity_decode($query);
- }
-
- /* Get the list of parameters. */
- $pairs = explode('&', $query);
- $params = array();
- foreach ($pairs as $pair) {
- $pair = explode('=', $pair, 2);
- $params[$pair[0]] = count($pair) == 2 ? $pair[1] : '';
- }
-
- /* Remove the parameters. */
- foreach ($remove as $param) {
- unset($params[$param]);
- }
-
- if (!count($params)) {
- return $url;
- }
-
- /* Flatten arrays.
- * FIXME: should handle more than one array level somehow. */
- $add = array();
- foreach ($params as $key => $val) {
- if (is_array($val)) {
- foreach ($val as $v) {
- $add[] = $key . '[]=' . $v;
- }
- } else {
- $add[] = $key . '=' . $val;
- }
- }
+ $horde_url = new Horde_Url($url);
- $query = implode('&', $add);
- if ($entities) {
- $query = htmlentities($query);
+ if ($url instanceof Horde_Url) {
+ return $url->remove($parameter);
}
- return $url . '?' . $query;
+ return $horde_url->remove($remove);
}
/**
--FILE--
<?php
+require_once 'Horde/Url.php';
require_once dirname(__FILE__) . '/../../../lib/Horde/Util.php';
$url = 'test';
--FILE--
<?php
+require_once 'Horde/Url.php';
require_once dirname(__FILE__) . '/../../../lib/Horde/Util.php';
$url = 'test?foo=1&bar=2';