<?php
/**
* PHP library for working with URI's. Requires
* PHP 5.3.7 or later. Replaces and extends PHP's
* parse_url()
*
* Based on P Guardiario's original work
*
* @author Nicholas Jordon
* @copyright 2014 Nicholas Jordon - All Rights Reserved
* @license http://opensource.org/licenses/MIT
* @version 0.1.0
*/
/**
* PHP URI
*/
class uri {
/*** Variables ***/
public $input;
public $scheme;
public $protocol;
public $scheme_name;
public $user;
public $username;
public $pass;
public $password;
public $host;
public $fqdn;
public $port;
public $authority;
public $path;
public $query;
public $fragment;
public $error;
public $error_msg;
/*** Methods ***/
public function __construct($input) {
$t = $this;
$t->input = $input;
$t->error = FALSE;
$t->protocol = &$this->scheme;
$t->username = &$this->user;
$t->password = &$this->pass;
$t->fqdn = &$this->host;
if (!is_string($input)) {
$t->error = TRUE;
$t->error_msg = 'Input was not a string!';
$t->scheme = FALSE;
$t->scheme_name = FALSE;
$t->user = FALSE;
$t->pass = FALSE;
$t->host = FALSE;
$t->port = FALSE;
$t->authority = FALSE;
$t->path = FALSE;
$t->query = FALSE;
$t->fragment = FALSE;
} else {
$this->parse($input);
}
}
protected function parse($uri) {
if ($this->error) {
return FALSE;
}
$t = $this;
$parsed = $t->_parse((string) $uri);
if (empty($parsed)) {
$t->error = TRUE;
$t->error = 'Could not parse the input as a URI';
return $parsed;
}
$defaults = array(
'scheme' => '',
'scheme_name' => '',
'user' => '',
'pass' => '',
'host' => '',
'port' => '',
'authority' => '',
'path' => '',
'query' => '',
'fragment' => ''
);
$values = $parsed + $defaults;
if (!empty($values['scheme'])) {
$t->scheme = $values['scheme'].'://';
} else {
$t->scheme = '';
}
$t->scheme_name = $values['scheme'];
$t->user = $values['user'];
$t->pass = $values['pass'];
$t->host = $values['host'];
$t->port = $values['port'];
$t->path = $values['path'];
$t->query = $values['query'];
$t->fragment = $values['fragment'];
$t->gen_authority();
}
private function _parse($uri) {
$uri = (string) $uri;
if (!version_compare(PHP_VERSION, '5.4.7') >= 0) {
if ($uri[0] == '/') {
unset($uri[0]);
}
if ($uri[0] == '/') {
unset($uri[0]);
}
}
return parse_url((string) $uri);
}
private function gen_authority() {
$t = $this;
$authority = '';
if (!empty($t->user)) {
$authority .= $t->user;
if (empty($t->pass)) {
$authority .= '@';
} else {
$authority .= ':';
}
}
if (!empty($t->pass)) {
$authority .= $t->pass.'@';
}
if (!empty($t->host)) {
$authority .= $t->host;
}
if (!empty($t->port)) {
$authority .= ':'.$t->port;
}
$t->authority = $authority;
}
public function arr() {
if ($this->error) {
return FALSE;
}
return array(
'scheme' => $this->scheme,
'user' => $this->user,
'pass' => $this->pass,
'host' => $this->host,
'port' => $this->port,
'authority' => $this->authority,
'path' => $this->path,
'query' => $this->query,
'fragment' => $this->fragment
);
}
public function str() {
if ($this->error) {
return FALSE;
}
$t = $this;
$str = '';
if (!empty($t->scheme)) {
$str .= $t->scheme;
}
if (!empty($t->user)) {
$str .= $t->user;
if (empty($t->pass)) {
$str .= '@';
} else {
$str .= ':';
$str .= $t->pass.'@';
}
}
if (!empty($t->host)) {
$str .= $t->host;
}
if (!empty($t->port)) {
$str .= ':'.$t->port;
}
if (!empty($t->path)) {
$str .= $t->path;
}
if (!empty($t->query)) {
$str .= '?'.$t->query;
}
if (!empty($t->fragment)) {
$str .= '#'.$t->fragment;
}
return $str;
}
public function p_str() {
if ($this->error) {
return FALSE;
}
echo $this->str();
}
public function path_info() {
if ($this->error) {
return FALSE;
}
$info = pathinfo($this->path);
$arr = explode('/',$this->path);
$last = count($arr) - 1;
if ($arr[$last] == '') {
unset($arr[$last]);
}
if ($arr[0] == '') {
array_shift($arr);
}
$info['array'] = $arr;
return $info;
}
public function query_arr() {
if ($this->error) {
return FALSE;
}
parse_str($this->query, $return);
return $return;
}
public function append($section, $str, $disable_safety = FALSE) {
if ($this->error) {
return FALSE;
}
$section = strtolower($section);
if (!isset($this->$section)) {
return FALSE;
}
if ($disable_safety) {
$this->$section = $this->$section.$str;
} else {
$test = $this->$section.$str;
$safety = $this->safety($section, $test);
if ($safety != FALSE) {
$this->$section = $safety;
} else {
return FALSE;
}
}
$this->gen_authority();
return $this->str();
}
public function prepend($section, $str, $disable_safety = FALSE) {
if ($this->error) {
return FALSE;
}
$section = strtolower($section);
if (!isset($this->$section)) {
return FALSE;
}
if ($disable_safety) {
$this->$section = $str.$this->$section;
} else {
$test = $str.$this->$section;
$safety = $this->safety($section, $test);
if ($safety != FALSE) {
$this->$section = $safety;
} else {
return FALSE;
}
}
$this->gen_authority();
return $this->str();
}
public function replace($section, $str, $disable_safety = FALSE) {
if ($this->error) {
return FALSE;
}
$section = strtolower($section);
if (!isset($this->$section)) {
return FALSE;
}
if ($disable_safety) {
$this->$section = $str;
} else {
$safety = $this->safety($section, $str);
if ($safety != FALSE) {
$this->$section = $safety;
} else {
return FALSE;
}
}
$this->gen_authority();
return $this->str();
}
protected function safety($type, $str) {
$type = strtoupper((string) $type);
if ($type != 'QUERY') {
$str = trim((string) $str);
}
$err = 0;
switch ($type) {
case 'SCHEME_NAME':
if (!preg_match('/\A[a-z]{1,10}\Z/', $str)) {
$err++;
}
break;
case 'SCHEME':
if (strpos($str, '\\') !== FALSE) {
$str = str_replace('\\', '/', $str);
}
if (strpos($str, '//') === FALSE && stripos($str, ':') === FALSE) {
if (!empty($str)) {
$str = $str.'://'; // assume it is generic
} else {
break; // there is nothing to check
}
}
$str = strtolower($str);
if (!stripos($str, '://') === FALSE) { // explicit generic
if (!preg_match('/\A[a-z]{1,10}:\/\/(\/)?\Z/', $str)) {
$err++;
}
} elseif(stripos($str, ':') === FALSE) { // explicit pipe
if (!preg_match('/\A[a-z]{1,10}:\Z/', $str)) {
$err++;
}
} elseif(stripos($str, '//') === FALSE) { // inherit
if ($str != '//') {
$err++;
}
}
break;
case 'USER':
$str = rawurlencode($str);
break;
case 'PASS':
$str = rawurlencode($str);
break;
case 'HOST':
$str = strtolower($str);
if (
(
!preg_match('/\A(([a-z0-9_]([a-z0-9\-_]+)?)\.)+[a-z0-9]([a-z0-9\-]+)?\Z/', $str) // fqdn
&&
!preg_match('/\A([0-9]\.){3}[0-9]\Z/', $str) // ip
)
||
strlen($str) > 255
) {
$err++;
}
break;
case 'PORT':
if ($str[0] == ':') {
$str = substr($str, 1);
}
if (!preg_match('/\A[0-9]{0,5}\Z/', $str)) {
$err++;
}
break;
case 'PATH':
$str = str_replace(array('//', '\\'), '/', $str); // common mistakes
$path_arr = explode('/', $str);
$safe_arr = array();
foreach ($path_arr as $path_part) {
$safe_arr[] = rawurlencode($path_part);
}
$str = implode('/', $safe_arr);
break;
case 'QUERY':
if (is_array($str)) {
$str = http_build_query($str);
}
if ($str[0] == '?') {
$str = substr($str, 1);
}
$frag_loc = strpos($str, '#');
if ($frag_loc) {
$str = substr($str, 0, ($frag_loc - 1));
} elseif ($str[0] == '#') {
$str = '';
}
break;
case 'FRAGMENT':
if ($str[0] == '#') {
unset($str[0]);
}
$str = urlencode($str);
break;
default:
return FALSE;
break;
}
if ($err) {
return FALSE;
}
return $str;
}
public function reset() {
$this->__construct($this->input);
}
}
$uri = new uri('http://example.com/path/to/file.ext');
$uri->replace('QUERY', array('rand', '4'));
$uri->replace('PATH', '/foo/bar');
$uri->append('PATH', '.baz');
$new = $uri->prepend('HOST', 'www.');
$uri->reset();
$original = $uri->str();
$uri->replace('FRAGMENT', 'Checkout');
$secure = $uri->replace('SCHEME', 'https');
echo $new.PHP_EOL;
echo $original.PHP_EOL;
echo $secure.PHP_EOL;
- Output for 5.3.0 - 5.3.29, 5.4.0 - 5.4.45, 5.5.0 - 5.5.38, 5.6.0 - 5.6.40, 7.0.0 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33, 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.6
- http://www.example.com/foo/bar.baz?0=rand&1=4
http://example.com/path/to/file.ext
https://example.com/path/to/file.ext#Checkout
preferences:
267.57 ms | 404 KiB | 406 Q