Code

< 40 %
40-60 %
> 60 %
1
<?php
2
/**
3
 * PHP URI Library
4
 * 
5
 * A PHP library for working with URI's, that is designed around the URI
6
 * standard. Requires PHP 5.4 or later. This library replaces and extends all
7
 * of PHP's parse_url() features, and even has some handy aliases.
8
 * 
9
 * Originally inspired by P Guardiario's work.
10
 * 
11
 * @author    Nicholas Jordon
12
 * @link      https://github.com/ProjectCleverWeb/PHP-URI
13
 * @copyright 2014 Nicholas Jordon - All Rights Reserved
14
 * @version   1.0.0
15
 * @license   http://opensource.org/licenses/MIT
16
 * @see       http://en.wikipedia.org/wiki/URI_scheme
17
 */
18
19
namespace {
20
	/**
21
	 * URI Class
22
	 * 
23
	 * This class acts as a callable alias to the \uri\main abstract class.
24
	 */
25
	class uri extends \uri\main {}
26
}
27
28
namespace uri {
29
	
30
	/**
31
	 * Main URI Class
32
	 * 
33
	 * This class parses URI string into a dynamic and easy to use object. In
34
	 * many ways, this class simply acts as an extension of a PHP string. Calling
35
	 * this class as if it were a string will result in the current URI string
36
	 * being used throughout PHP.
37
	 */
38
	abstract class main {
39
		/*** Variables ***/
40
		
41
		public $error;
42
		public $input;
43
		
44
		/*
45
		"Ghost" Variables
46
		=================
47
		These variables can be accesed from within the class, but as far as the rest
48
		of PHP is concerned, these variables simply don't exist.
49
		*/
50
		public $object;
51
		private $chain;
52
		
53
		/*
54
		Sudo-Private Variables
55
		======================
56
		These variables can be accessed just like normal public variables, and can
57
		even be changed like public variables. This implementation of private
58
		variables combined with the __get(), __set(), __isset(), & __unset() magic
59
		constants allow each variable to stay in sync with the group, and still be
60
		accessable.
61
		*/
62
		private $authority;
63
		private $domain;
64
		private $fqdn;
65
		private $fragment;
66
		private $host;
67
		private $protocol;
68
		private $pass;
69
		private $password;
70
		private $path;
71
		private $port;
72
		private $query;
73
		private $scheme;
74
		private $scheme_name;
75
		private $scheme_symbols;
76
		private $user;
77
		private $username;
78
		
79
		
80
		
81
		/*** Magic Methods ***/
82
		
83
		/**
84
		 * Parses the input as a URI and populates the variables. Fails if input is
85
		 * not a string or if the string cannot be parsed as a URI.
86
		 * 
87
		 * @param string $input The URI to parse.
88
		 */
89 38
		public function __construct($input) {
90 38
			$this->input  = $input;
91 38
			$this->object = \uri\parser::parse($input);
92
			
93 38
			if (!empty($this->object->host)) {
94 37
				\uri\generate::authority($this->object);
95 37
				\uri\generate::aliases($this->object);
96
				
97
				// Enable Chain Events
98 37
				$this->chain = new \uri\chain($this);
99
				
100
				// References required for Sudo-Private Variables
101 37
				$this->authority      = &$this->object->authority;
102 37
				$this->domain         = &$this->object->domain;
103 37
				$this->fqdn           = &$this->object->fqdn;
104 37
				$this->fragment       = &$this->object->fragment;
105 37
				$this->host           = &$this->object->host;
106 37
				$this->protocol       = &$this->object->protocol;
107 37
				$this->pass           = &$this->object->pass;
108 37
				$this->password       = &$this->object->password;
109 37
				$this->path           = &$this->object->path;
110 37
				$this->port           = &$this->object->port;
111 37
				$this->query          = &$this->object->query;
112 37
				$this->scheme         = &$this->object->scheme;
113 37
				$this->scheme_name    = &$this->object->scheme_name;
114 37
				$this->scheme_symbols = &$this->object->scheme_symbols;
115 37
				$this->user           = &$this->object->user;
116 37
				$this->username       = &$this->object->username;
117 37
			} else {
118 1
				$this->error = 'Input could not be parsed as a URI';
119
			}
120 38
		}
121
		
122
		/**
123
		 * In the event this class is called as or converted to a string, it will
124
		 * return the current URI string, and NOT cause any errors.
125
		 * 
126
		 * @return string The current URI as a string
127
		 */
128 1
		public function __toString() {
129 1
			return \uri\generate::string($this->object);
130
		}
131
		
132
		/**
133
		 * Invoked? just return the current URI as a string, nothing fancy.
134
		 * 
135
		 * @return string The current URI as a string
136
		 */
137 1
		public function __invoke() {
138 1
			return \uri\generate::string($this->object);
139
		}
140
		
141
		/**
142
		 * Because of how references are created within this class, cloning doesn't
143
		 * work as expected. This magic method warn's people until the issue can be
144
		 * correctly addressed. (It may be impossible to resolve this issue, with
145
		 * the current configuration)
146
		 * 
147
		 * @return void
148
		 */
149 1
		public function __clone() {
150 1
			$this->_err('CLONE', debug_backtrace(), 'clone');
151 1
		}
152
		
153
		/**
154
		 * Allows access to the different parts of the URI to be synchronized. This
155
		 * means that what is returned should always be accurate. Triggers a notice
156
		 * if the variable cannot be accessed.
157
		 * 
158
		 * @param  string $name The requested variable
159
		 * @return string|null  The value of the variable, or NULL if it can't be accessed
160
		 */
161 6 View Code Duplication
		public function __get($name) {
162 6
			if (isset($this->object->$name)) {
163 6
				\uri\generate::scheme($this->object);
164 6
				\uri\generate::authority($this->object);
165 6
				return $this->object->$name;
166
			} else {
167 1
				$this->_err('UNDEFINED', debug_backtrace(), $name);
168 1
				return NULL;
169
			}
170
		}
171
		
172
		/**
173
		 * Allows access to the different parts of the URI to be synchronized. This
174
		 * means that what is returned should always be accurate. Triggers a notice
175
		 * if the variable cannot be accessed.
176
		 * 
177
		 * @param  string $name  The requested variable
178
		 * @param  string $value The new value for the variable
179
		 * @return string|null   The new value of the variable, or NULL if it can't be accessed
180
		 */
181 3
		public function __set($name, $value) {
182 3
			if (isset($this->object->$name) && $name != 'authority') {
183 3
				\uri\actions::modify($this->object, 'replace', $name, $value);
184 3
				return $value;
185
			} else {
186 1
				$this->_err('FORBIDDEN', debug_backtrace(), $name);
187 1
				return NULL;
188
			}
189
		}
190
		
191
		/**
192
		 * Allows access to the different parts of the URI to be synchronized. This
193
		 * means that what is returned should always be accurate.
194
		 * 
195
		 * @param  string  $name The requested variable
196
		 * @return boolean       Returns TRUE if the variable is not empty, FALSE otherwise
197
		 */
198 1 View Code Duplication
		public function __isset($name) {
199 1
			\uri\generate::scheme($this->object);
200 1
			\uri\generate::authority($this->object);
201 1
			if (isset($this->object->$name)) {
202 1
				return !empty($this->object->$name);
203
			}
204 1
			return FALSE;
205
		}
206
		
207
		/**
208
		 * Allows access to the different parts of the URI to be synchronized. This
209
		 * means that what is returned should always be accurate. Triggers a notice
210
		 * if the variable cannot be accessed.
211
		 * 
212
		 * @param  string $name The requested variable
213
		 * @return boolean      Returns TRUE if the varaible was successfully emptied, FALSE otherwise.
214
		 */
215 1
		public function __unset($name) {
216 1
			if (isset($this->object->$name) && $name != 'host' && $name != 'authority') {
217 1
				\uri\actions::modify($this->object, 'replace', $name, '');
218 1
				return TRUE;
219 1
			} elseif (isset($this->object->$name)) {
220 1
				$this->_err('FORBIDDEN', debug_backtrace(), $name);
221 1
			} else {
222 1
				$this->_err('UNDEFINED', debug_backtrace(), $name);
223
			}
224 1
			return FALSE;
225
		}
226
		
227
		
228
		
229
		/*** Methods ***/
230
		
231
		/**
232
		 * Returns the current URI as a string.
233
		 * 
234
		 * @return string The current URI as a string
235
		 */
236 12
		public function str() {
237 12
			return \uri\generate::string($this->object);
238
		}
239
		
240
		/**
241
		 * Alias of str()
242
		 * 
243
		 * @return string The current URI as a string
244
		 */
245 1
		public function to_string() {
246 1
			return \uri\generate::string($this->object);
247
		}
248
		
249
		/**
250
		 * Prints the current URI as a string
251
		 * 
252
		 * @param  string $prepend The string to prepend to the output
253
		 * @param  string $append  The string to append to the output
254
		 * @return void
255
		 */
256 1
		public function p_str($prepend = '', $append = '') {
257 1
			echo $prepend.\uri\generate::string($this->object).$append;
258 1
		}
259
		
260
		/**
261
		 * Returns the current URI as an array
262
		 * 
263
		 * @return array The current URI as an array
264
		 */
265 2
		public function arr() {
266 2
			return \uri\generate::to_array($this->object);
267
		}
268
		
269
		/**
270
		 * Alias of arr()
271
		 * 
272
		 * @return array The current URI as an array
273
		 */
274 1
		public function to_array() {
275 1
			return \uri\generate::to_array($this->object);
276
		}
277
		
278
		/**
279
		 * The path broken down into dirname, basename, extension, filename, & array
280
		 * 
281
		 * @return array The information array
282
		 */
283 1
		public function path_info() {
284 1
			return \uri\generate::path_info($this->object);
285
		}
286
		
287
		/**
288
		 * The query parsed into an array
289
		 * 
290
		 * @return array The query array
291
		 */
292 1
		public function query_arr() {
293 1
			return \uri\generate::query_array($this->object);
294
		}
295
		
296
		/**
297
		 * Replaces $section of the URI with $str, given $str is a valid replacement
298
		 * 
299
		 * @param  string $section The section to replace
300
		 * @param  string $str     The string to replace the section with
301
		 * @return string|false    The resulting URI if the modification is valid, FALSE otherwise
302
		 */
303 16
		public function replace($section, $str) {
304 16
			return \uri\actions::modify($this->object, 'replace', $section, $str);
305
		}
306
		
307
		/**
308
		 * Prepends $section of the URI with $str, given the $section is still valid
309
		 * once $str is in place
310
		 * 
311
		 * @param  string $section The section to prepend
312
		 * @param  string $str     The string to prepend the section with
313
		 * @return string|false    The resulting URI if the modification is valid, FALSE otherwise
314
		 */
315 11
		public function prepend($section, $str) {
316 11
			return \uri\actions::modify($this->object, 'prepend', $section, $str);
317
		}
318
		
319
		/**
320
		 * Appends $section of the URI with $str, given $section is still valid
321
		 * once $str is in place
322
		 * 
323
		 * @param  string $section The section to append
324
		 * @param  string $str     The string to append the section with
325
		 * @return string|false    The resulting URI if the modification is valid, FALSE otherwise
326
		 */
327 11
		public function append($section, $str) {
328 11
			return \uri\actions::modify($this->object, 'append', $section, $str);
329
		}
330
		
331
		/**
332
		 * Adds query var to the query string if it is not already set and returns
333
		 * TRUE. Otherwise it returns FALSE
334
		 * 
335
		 * @param  string $key   The key to add
336
		 * @param  mixed  $value The value of $key
337
		 * @return boolean       TRUE on success, FALSE otherwise
338
		 */
339 1
		public function query_add($key, $value) {
340 1
			return \uri\query::add($this->object, $key, $value);
341
		}
342
		
343
		/**
344
		 * Adds query var to the query string regardless if it already set or not
345
		 * 
346
		 * @param  string $key   The key to replace
347
		 * @param  mixed  $value The value of $key
348
		 * @return void
349
		 */
350 1
		public function query_replace($key, $value) {
351 1
			\uri\query::replace($this->object, $key, $value);
352 1
		}
353
		
354
		/**
355
		 * Removes $key from the query if it exists
356
		 * 
357
		 * @param  string $key The key to remove
358
		 * @return void
359
		 */
360 1
		public function query_remove($key) {
361 1
			\uri\query::remove($this->object, $key);
362 1
		}
363
		
364
		/**
365
		 * Checks if $key exists in the query
366
		 * 
367
		 * @param  string $key The key to search for
368
		 * @return boolean     TRUE if the $key exists, FALSE otherwise
369
		 */
370 1
		public function query_exists($key) {
371 1
			return \uri\query::exists($this->object, $key);
372
		}
373
		
374
		/**
375
		 * Gets a specific var's value from the query. It is HIGHLY recommended
376
		 * that you use query_arr() instead, when fetching multiple values from
377
		 * the same query string. Returns NULL if $key does not exist.
378
		 * 
379
		 * @param  string $key The key to get
380
		 * @return mixed|null  The value of $key, or NULL if it does not exist.
381
		 */
382 1
		public function query_get($key) {
383 1
			return \uri\query::get($this->object, $key);
384
		}
385
		
386
		/**
387
		 * Renames a specific $key within the query. If the key exists within query
388
		 * string and is successfully renamed, the TRUE is returned. Otherwise
389
		 * FALSE is returned.
390
		 * 
391
		 * @param  string $key     The key to rename
392
		 * @param  string $new_key The new name of $key
393
		 * @return boolean         TRUE on success, FALSE otherwise
394
		 */
395 1
		public function query_rename($key, $new_key) {
396 1
			return \uri\query::rename($this->object, $key, $new_key);
397
		}
398
		
399
		/**
400
		 * Returns the chain class, which allows events to be chained together
401
		 * rather than the reference being called several times. see \uri\chain
402
		 * 
403
		 * @return object The chain class
404
		 */
405 4
		public function chain() {
406 4
			return $this->chain;
407
		}
408
		
409
		/**
410
		 * Resets the current object to its initial state
411
		 * 
412
		 * @return void
413
		 */
414 2
		public function reset() {
415 2
			$this->__construct($this->input);
416 2
		}
417
		
418
		/**
419
		 * A unknown/forbidden property has been called. trigger an error
420
		 * 
421
		 * @param  string $type  Type of error
422
		 * @param  array  $trace The output from debug_backtrace()
423
		 * @param  string $name  Property name
424
		 * @return void
425
		 */
426 2
		private function _err($type, $trace, $name) {
427 2
			$fmt = 'Undifined property via <code>%1$s::%2$s()</code>: Property <code>%3$s</code> cannot be unset in <b>%4$s</b> on line <b>%5$s</b>. Error triggered';
428 2
			if ($type == 'FORBIDDEN') {
429 1
				$fmt = 'Forbidden property via <code>%1$s::%2$s()</code>: Property <code>%3$s</code> cannot be unset in <b>%4$s</b> on line <b>%5$s</b>. Error triggered';
430 2
			} elseif($type == 'CLONE') {
431 1
				$fmt = 'Invalid clone in <b>%4$s</b> on line <b>%5$s</b>. Because of how cloning works, and how references are configured within the class, extensions of %1$s cannot be cloned. Please make a new instance instead, like so: <code>$clone = new \\uri($original->str()); $clone->input = $original->input;</code>. Error triggered';
432 1
			}
433
			
434 2
			trigger_error(
435 2
				sprintf(
436 2
					$fmt,
437 2
					$trace[0]['class'],
438 2
					$trace[0]['function'],
439 2
					$name,
440 2
					$trace[0]['file'],
441 2
					$trace[0]['line']
442 2
				),
443
				E_USER_NOTICE
444 2
			);
445 2
		}
446
	}
447
	
448
	
449
	
450
	/**
451
	 * The Parser Class
452
	 * 
453
	 * This class controls how the initial input is parsed. This class is
454
	 * designed to be easily upgraded to use different types of parsing. should
455
	 * it be desired.
456
	 */
457
	class parser {
458
		/*** Constants ***/
459
		
460
		// This regex is broken down to be readable in regex_parse()
461
		const REGEX = '/^(([a-z]+)?(\:\/\/|\:|\/\/))?(?:([a-z0-9$_\.\+!\*\'\(\),;&=\-]+)(?:\:([a-z0-9$_\.\+!\*\'\(\),;&=\-]*))?@)?((?:\d{3}.\d{3}.\d{3}.\d{3})|(?:[a-z0-9\-_]+(?:\.[a-z0-9\-_]+)*))(?:\:([0-9]+))?((?:\:|\/)[a-z0-9\-_\/\.]+)?(?:\?([a-z0-9$_\.\+!\*\'\(\),;:@&=\-%]*))?(?:#([a-z0-9\-_]*))?/i';
462
		
463
		/*** Methods ***/
464
		
465
		/**
466
		 * Wrapper function for parsing a string into a URI object
467
		 * 
468
		 * @param  string $uri  The input to be parsed as a URI
469
		 * @return object       If the input can be correctly parsed, then it returns an object with at least the 'host' populated
470
		 */
471 38
		public static function parse($uri) {
472 38
			if (!is_string($uri)) {
473 1
				return FALSE;
474
			}
475
			
476 38
			$parsed = self::regex_parse($uri);
477
			
478
			// Could not be parsed correctly
479 38
			if (empty($parsed)) {
480 1
				$parsed = array_fill(1, 10, '');
481 1
			}
482
			
483
			return (object) array(
484 38
				'scheme'         => $parsed[1],
485 38
				'scheme_name'    => $parsed[2],
486 38
				'scheme_symbols' => $parsed[3],
487 38
				'user'           => $parsed[4],
488 38
				'pass'           => $parsed[5],
489 38
				'host'           => $parsed[6],
490 38
				'port'           => $parsed[7],
491 38
				'path'           => $parsed[8],
492 38
				'query'          => $parsed[9],
493 38
				'fragment'       => $parsed[10],
494 38
			);
495
		}
496
		
497
		/**
498
		 * Parses a URI string into a usable array.
499
		 * 
500
		 * @param  string $uri The URI to be parsed
501
		 * @return array|false Returns an array if the sting could be correctly parsed, FALSE otherwise
502
		 */
503 38
		private static function regex_parse($uri) {
504
			// $regex = (
505
			//   '/'.
506
			//   '^(([a-z]+)?(\:\/\/|\:|\/\/))?'.              // Scheme, Scheme Name, & Scheme Symbols
507
			//   '(?:'.                                        // Auth Start
508
			//     '([a-z0-9$_\.\+!\*\'\(\),;&=\-]+)'.         // Username
509
			//     '(?:\:([a-z0-9$_\.\+!\*\'\(\),;&=\-]*))?'.  // Password
510
			//   '@)?'.                                        // Auth End
511
			//   '('.                                          // Host Start
512
			//     '(?:\d{3}.\d{3}.\d{3}.\d{3})'.              // IP Address
513
			//     '|'.                                        // -OR-
514
			//     '(?:[a-z0-9\-_]+(?:\.[a-z0-9\-_]+)*)'.      // Domain Name
515
			//   ')'.                                          // Host End
516
			//   '(?:\:([0-9]+))?'.                            // Port
517
			//   '((?:\:|\/)[a-z0-9\-_\/\.]+)?'.               // Path
518
			//   '(?:\?([a-z0-9$_\.\+!\*\'\(\),;:@&=\-%]*))?'. // Query
519
			//   '(?:#([a-z0-9\-_]*))?'.                       // Fragment
520
			//   '/i'
521
			// );
522 38
			preg_match_all(self::REGEX, $uri, $parsed, PREG_SET_ORDER);
523
			
524
			// Host is required
525 38
			if (!isset($parsed[0][6])) {
526 1
				return FALSE;
527
			}
528
			
529
			// Return what was parsed, but make sure that each offset is set regardless
530 37
			return $parsed[0] + array_fill(0, 11, '');
531
		}
532
	}
533
	
534
	
535
	
536
	/**
537
	 * The Modifier Class
538
	 * 
539
	 * This class is in charge of making user-based changes.
540
	 */
541
	class modify {
542
		/*** Methods ***/
543
		
544
		/**
545
		 * Modfies the Scheme Name
546
		 * 
547
		 * @param  object $object  The object to modify
548
		 * @param  string $action  The action to take
549
		 * @param  string $str     The modfication
550
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
551
		 */
552 3
		public static function scheme_name(&$object, $action, $str) {
553 3
			$org = $object->scheme_name;
554 3
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
555 3
			if (!(preg_match('/\A[a-z]{1,10}\Z/', $object->scheme_name) || empty($str))) {
556 1
				$object->scheme_name = $org;
557 1
				return FALSE;
558 2
			} elseif (empty($object->scheme_symbols)) {
559 2
				$object->scheme_symbols = '://';
560 2
			}
561
			
562 2
			return \uri\generate::string($object);
563
		}
564
		
565
		/**
566
		 * Modfies the Scheme Symbols
567
		 * 
568
		 * @param  object $object  The object to modify
569
		 * @param  string $action  The action to take
570
		 * @param  string $str     The modfication
571
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
572
		 */
573 3 View Code Duplication
		public static function scheme_symbols(&$object, $action, $str) {
574 3
			$org = $object->scheme_symbols;
575 3
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
576 3
			if (!(preg_match('/\A(:)?([\/]{2,3})?\Z/', $object->scheme_symbols) || empty($str))) {
577 1
				$object->scheme_symbols = $org;
578 1
				return FALSE;
579
			}
580
			
581 2
			return \uri\generate::string($object);
582
		}
583
		
584
		/**
585
		 * Modfies the Scheme
586
		 * 
587
		 * @param  object $object  The object to modify
588
		 * @param  string $action  The action to take
589
		 * @param  string $str     The modfication
590
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
591
		 */
592 5
		public static function scheme(&$object, $action, $str) {
593 5
			$org = array($object->scheme, $object->scheme_name, $object->scheme_symbols);
594 5
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
595 5
			if (empty($object->scheme)) {
596 1
				$object->scheme = $object->scheme_name = $object->scheme_symbols = '';
597 1
			} else {
598 5
				preg_match('/\A([a-z]{1,10})?(\:|:\/\/|\/\/|:\/\/\/)\Z/i', $object->scheme, $matches);
599 5
				if (empty($matches[1]) && empty($matches[2])) {
600
					// restore values
601 2
					$object->scheme         = $org[0];
602 2
					$object->scheme_name    = $org[1];
603 2
					$object->scheme_symbols = $org[2];
604 2
					return FALSE;
605
				} else {
606
					// apply changes
607 3
					$matches                = $matches + array('', '', '');
608 3
					$object->scheme         = $matches[0];
609 3
					$object->scheme_name    = $matches[1];
610 3
					$object->scheme_symbols = $matches[2];
611
				}
612
			}
613
			
614 3
			return \uri\generate::string($object);
615
		}
616
		
617
		/**
618
		 * Alias of scheme()
619
		 * 
620
		 * @param  object $object  The object to modify
621
		 * @param  string $action  The action to take
622
		 * @param  string $str     The modfication
623
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
624
		 */
625 1
		public static function protocol(&$object, $action, $str) {
626 1
			return self::scheme($object, $action, $str);
627
		}
628
		
629
		/**
630
		 * Modfies the Username
631
		 * 
632
		 * @param  object $object  The object to modify
633
		 * @param  string $action  The action to take
634
		 * @param  string $str     The modfication
635
		 * @return string          Returns the resulting URI on success, FALSE otherwise
636
		 */
637 2
		public static function user(&$object, $action, $str) {
638 2
			$str = rawurlencode($str);
639
			
640 2
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
641 2
			return \uri\generate::string($object);
642
		}
643
		
644
		/**
645
		 * Alias of user()
646
		 * 
647
		 * @param  object $object  The object to modify
648
		 * @param  string $action  The action to take
649
		 * @param  string $str     The modfication
650
		 * @return string          Returns the resulting URI on success, FALSE otherwise
651
		 */
652 1
		public static function username(&$object, $action, $str) {
653 1
			return self::user($object, $action, $str);
654
		}
655
		
656
		/**
657
		 * Modfies the Password
658
		 * 
659
		 * @param  object $object  The object to modify
660
		 * @param  string $action  The action to take
661
		 * @param  string $str     The modfication
662
		 * @return string          Returns the resulting URI on success, FALSE otherwise
663
		 */
664 2
		public static function pass(&$object, $action, $str) {
665 2
			$str = rawurlencode($str);
666
			
667 2
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
668 2
			return \uri\generate::string($object);
669
		}
670
		
671
		/**
672
		 * Alias of pass()
673
		 * 
674
		 * @param  object $object  The object to modify
675
		 * @param  string $action  The action to take
676
		 * @param  string $str     The modfication
677
		 * @return string          Returns the resulting URI on success, FALSE otherwise
678
		 */
679 1
		public static function password(&$object, $action, $str) {
680 1
			return self::pass($object, $action, $str);
681
		}
682
		
683
		/**
684
		 * Modfies the Host
685
		 * 
686
		 * @param  object $object  The object to modify
687
		 * @param  string $action  The action to take
688
		 * @param  string $str     The modfication
689
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
690
		 */
691 5 View Code Duplication
		public static function host(&$object, $action, $str) {
692 5
			$org = $object->host;
693 5
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
694
			if ((
695 5
				!preg_match('/\A(([a-z0-9_]([a-z0-9\-_]+)?)\.)+[a-z0-9]([a-z0-9\-]+)?\Z/i', $object->host) // fqdn
696 5
				&&
697 1
				!preg_match('/\A([0-9]\.){3}[0-9]\Z/i', $object->host) // ip
698 5
			)) {
699 1
				$object->host = $org;
700 1
				return FALSE;
701
			}
702
			
703 4
			return \uri\generate::string($object);
704
		}
705
		
706
		/**
707
		 * Alias of host()
708
		 * 
709
		 * @param  object $object  The object to modify
710
		 * @param  string $action  The action to take
711
		 * @param  string $str     The modfication
712
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
713
		 */
714 1
		public static function domain(&$object, $action, $str) {
715 1
			return self::host($object, $action, $str);
716
		}
717
		
718
		/**
719
		 * Alias of host()
720
		 * 
721
		 * @param  object $object  The object to modify
722
		 * @param  string $action  The action to take
723
		 * @param  string $str     The modfication
724
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
725
		 */
726 1
		public static function fqdn(&$object, $action, $str) {
727 1
			return self::host($object, $action, $str);
728
		}
729
		
730
		/**
731
		 * Modfies the Port
732
		 * 
733
		 * @param  object $object  The object to modify
734
		 * @param  string $action  The action to take
735
		 * @param  string $str     The modfication
736
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
737
		 */
738 3
		public static function port(&$object, $action, $str) {
739 3
			$org = $object->port;
740 3
			if (isset($str[0]) && $str[0] == ':') {
741 1
				$str = substr($str, 1);
742 1
			}
743 3
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
744 3
			if (!(preg_match('/\A[0-9]{0,5}\Z/', $object->port) || empty($str))) {
745 1
				$object->port = $org;
746 1
				return FALSE;
747
			}
748
			
749 2
			return \uri\generate::string($object);
750
		}
751
		
752
		/**
753
		 * Modfies the Path
754
		 * 
755
		 * @param  object $object  The object to modify
756
		 * @param  string $action  The action to take
757
		 * @param  string $str     The modfication
758
		 * @return string          Returns the resulting URI on success, FALSE otherwise
759
		 */
760 4
		public static function path(&$object, $action, $str) {
761 4
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
762 4
			return \uri\generate::string($object);
763
		}
764
		
765
		/**
766
		 * Modfies the Query
767
		 * 
768
		 * @param  object $object  The object to modify
769
		 * @param  string $action  The action to take
770
		 * @param  string $str     The modfication
771
		 * @return string          Returns the resulting URI on success, FALSE otherwise
772
		 */
773 7 View Code Duplication
		public static function query(&$object, $action, $str) {
774 7
			if (isset($str[0]) && $str[0] == '?') {
775 1
				$str = substr($str, 1);
776 1
			}
777
			
778 7
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
779 7
			return \uri\generate::string($object);
780
		}
781
		
782
		/**
783
		 * Modfies the Fragment
784
		 * 
785
		 * @param  object $object  The object to modify
786
		 * @param  string $action  The action to take
787
		 * @param  string $str     The modfication
788
		 * @return string          Returns the resulting URI on success, FALSE otherwise
789
		 */
790 2 View Code Duplication
		public static function fragment(&$object, $action, $str) {
791 2
			if (isset($str[0]) && $str[0] == '#') {
792 1
				$str = substr($str, 1);
793 1
			}
794 2
			$str = urlencode($str);
795
			
796 2
			\uri\actions::callback($object, $action, __FUNCTION__, $str);
797 2
			return \uri\generate::string($object);
798
		}
799
	}
800
	
801
	
802
	
803
	/**
804
	 * The Actions Class
805
	 * 
806
	 * This class handlles the available actions
807
	 */
808
	class actions {
809
		
810
		/**
811
		 * Acts as universal alias to the modify class, ensuring the call is viable
812
		 * 
813
		 * @param  object $object  The object to modify
814
		 * @param  string $action  The action to take
815
		 * @param  string $section The section of the object to modify
816
		 * @param  string $str     The modfication
817
		 * @return string|false    Returns the resulting URI on success, FALSE otherwise
818
		 */
819 26
		public static function modify(&$object, $action, $section, $str) {
820 26
			settype($section, 'string');
821 26
			settype($str, 'string');
822 26
			$section = strtolower($section);
823
			
824 26
			if (is_callable(array('\\uri\\modify', $section))) {
825 25
				return call_user_func_array(array('\\uri\\modify', $section), array(&$object, $action, $str));
826
			} else {
827 1
				return FALSE;
828
			}
829
		}
830
		
831
		/**
832
		 * Handles which action is taken; since there are only 3 very simple
833
		 * actions, it makes sense to put them all in 1 method.
834
		 * 
835
		 * @param  object $object  The object to modify
836
		 * @param  string $action  The action to take
837
		 * @param  string $section The section of the object to modify
838
		 * @param  string $str     The modfication
839
		 * @return void
840
		 */
841 25
		public static function callback(&$object, $action, $section, $str) {
842
			switch ($action) {
843 25
				case 'replace':
844 25
					$object->$section = $str;
845 25
					break;
846 12
				case 'prepend':
847 12
					$object->$section = $str.$object->$section;
848 12
					break;
849 12
				case 'append':
850 12
					$object->$section = $object->$section.$str;
851 12
			}
852 25
		}
853
		
854
	}
855
	
856
	
857
	
858
	/**
859
	 * The Generator Class
860
	 * 
861
	 * This class makes sure everything stays in sync and is produced correctly.
862
	 * Unlike the the modify class, this class only changes $object to keep
863
	 * things syncronized. It's primary purpose is to use the information in
864
	 * $object to create some type of returnable value.
865
	 */
866
	class generate {
867
		
868
		/*** Methods ***/
869
		
870
		/**
871
		 * Generates all the aliases for $object
872
		 * 
873
		 * @param  object $object The object to modify
874
		 * @return void
875
		 */
876 37
		public static function aliases(&$object) {
877 37
			$object->protocol = &$object->scheme;
878 37
			$object->username = &$object->user;
879 37
			$object->password = &$object->pass;
880 37
			$object->domain   = &$object->host;
881 37
			$object->fqdn     = &$object->host;
882 37
		}
883
		
884
		/**
885
		 * Generate the scheme. This method exists to make changing how the scheme
886
		 * is generated easier; and will likely help prevent redundant code in the
887
		 * future
888
		 * 
889
		 * @param  object $object The object to modify
890
		 * @return void
891
		 */
892 28
		public static function scheme(&$object) {
893 28
			$object->scheme = $object->scheme_name.$object->scheme_symbols;
894 28
		}
895
		
896
		/**
897
		 * Regenerates the Authority string
898
		 * 
899
		 * @param  object $object The object to modify
900
		 * @return void
901
		 */
902 37
		public static function authority(&$object) {
903 37
			$str_arr = array($object->user);
904 37
			if (empty($object->user) == FALSE && empty($object->pass)) {
905 3
				$str_arr[] = '@';
906 37
			} elseif (!empty($object->user)) {
907 27
				$str_arr[] = ':'.$object->pass.'@';
908 27
			}
909 37
			$str_arr[] = $object->host;
910 37
			if (!empty($object->port)) {
911 27
				$str_arr[] = ':'.$object->port;
912 27
			}
913 37
			$object->authority = implode('', $str_arr);
914 37
		}
915
		
916
		/**
917
		 * Generate a the full URI as a string, from the current object
918
		 * 
919
		 * @param  object $object The object to use
920
		 * @return string         The current URI string
921
		 */
922 25
		public static function string(&$object) {
923 25
			self::scheme($object);
924 25
			self::authority($object);
925 25
			$str_arr = array($object->scheme, $object->authority, $object->path);
926 25
			if (!empty($object->query)) {
927 18
				$str_arr[] = '?'.$object->query;
928 18
			}
929 25
			if (!empty($object->fragment)) {
930 17
				$str_arr[] = '#'.$object->fragment;
931 17
			}
932 25
			return implode('', $str_arr);
933
		}
934
		
935
		/**
936
		 * Generate a the full URI as a string, from the current object
937
		 * 
938
		 * @param  object $object The object to use
939
		 * @return array          The current URI as an array
940
		 */
941 2
		public static function to_array(&$object) {
942
			$arr = array(
943 2
				'authority'      => $object->authority,
944 2
				'fragment'       => $object->fragment,
945 2
				'host'           => $object->host,
946 2
				'pass'           => $object->pass,
947 2
				'path'           => $object->path,
948 2
				'port'           => $object->port,
949 2
				'query'          => $object->query,
950 2
				'scheme'         => $object->scheme,
951 2
				'scheme_name'    => $object->scheme_name,
952 2
				'scheme_symbols' => $object->scheme_symbols,
953 2
				'user'           => $object->user,
954 2
			);
955
			
956 2
			$arr['domain']   = &$arr['host'];
957 2
			$arr['fqdn']     = &$arr['host'];
958 2
			$arr['password'] = &$arr['pass'];
959 2
			$arr['protocol'] = &$arr['scheme'];
960 2
			$arr['username'] = &$arr['user'];
961
			
962 2
			ksort($arr);
963 2
			return $arr;
964
		}
965
		
966
		/**
967
		 * Returns various information about the current $path
968
		 * 
969
		 * @param  object $object The object to use
970
		 * @return array          Associative array of information about the current $path
971
		 */
972 1
		public static function path_info(&$object) {
973
			$defaults = array(
974 1
				'dirname' => '',
975 1
				'basename' => '',
976 1
				'extension' => '',
977 1
				'filename' => '',
978 1
				'array' => array()
979 1
			);
980
			
981 1
			$info          = pathinfo($object->path) + $defaults;
982 1
			$info['array'] = array_values(array_filter(explode('/', $object->path)));
983 1
			ksort($info);
984
			
985 1
			return $info;
986
		}
987
		
988
		/**
989
		 * The current $query string parsed into an array
990
		 * 
991
		 * @param  object $object The object to use
992
		 * @return array          The query string as an array
993
		 */
994 9
		public static function query_array(&$object) {
995 9
			parse_str($object->query, $return);
996 9
			return (array) $return;
997
		}
998
		
999
	}
1000
	
1001
	
1002
	/**
1003
	 * The Query Class
1004
	 * 
1005
	 * This class is resposible for checking and taking actions on the query
1006
	 * string. It should be noted that this class relies heavily on
1007
	 * generate::query_arr() and that excessive modification to the query
1008
	 * string should be done manually through generate::query_arr() and then
1009
	 * \uri\main::$query should be set (use http_build_query()).
1010
	 */
1011
	class query {
1012
		
1013
		/*** Methods ***/
1014
		
1015
		/**
1016
		 * Builds the query under RFC3986. RFC3986 is used as a replacement for
1017
		 * RFC1738 because it is more portable.
1018
		 * 
1019
		 * @param  array $query_array The array to build the query string from
1020
		 * @return string             The resulting query string
1021
		 */
1022 5
		private static function build_query($query_array) {
1023 5
			return http_build_query($query_array, '', '&', PHP_QUERY_RFC3986);
1024
		}
1025
		
1026
		/**
1027
		 * Adds query var to the query string if it is not already set and returns
1028
		 * TRUE. Otherwise it returns FALSE
1029
		 * 
1030
		 * @param  object $object The object to modify
1031
		 * @param  string $key    The key to add
1032
		 * @param  string $value  The value of $key
1033
		 * @return boolean        TRUE on success, FALSE otherwise
1034
		 */
1035 2
		public static function add(&$object, $key, $value) {
1036 2
			$qarray = \uri\generate::query_array($object);
1037 2 View Code Duplication
			if (!isset($qarray[$key])) {
1038 2
				$qarray[$key] = $value;
1039 2
				\uri\actions::modify($object, 'replace', 'QUERY', self::build_query($qarray));
1040 2
				return TRUE;
1041
			}
1042 1
			return FALSE;
1043
		}
1044
		
1045
		/**
1046
		 * Adds query var to the query string regardless if it already set or not
1047
		 * 
1048
		 * @param  object $object The object to modify
1049
		 * @param  string $key    The key to replace
1050
		 * @param  string $value  The value of $key
1051
		 * @return void
1052
		 */
1053 2
		public static function replace(&$object, $key, $value) {
1054 2
			$qarray       = \uri\generate::query_array($object);
1055 2
			$qarray[$key] = $value;
1056 2
			\uri\actions::modify($object, 'replace', 'QUERY', self::build_query($qarray));
1057 2
		}
1058
		
1059
		/**
1060
		 * Removes $key from the query if it exists
1061
		 * 
1062
		 * @param  object $object The object to modify
1063
		 * @param  string $key    The key to remove
1064
		 * @return void
1065
		 */
1066 2
		public static function remove(&$object, $key) {
1067 2
			$qarray = \uri\generate::query_array($object);
1068 2 View Code Duplication
			if (isset($qarray[$key])) {
1069 2
				unset($qarray[$key]);
1070 2
				\uri\actions::modify($object, 'replace', 'QUERY', self::build_query($qarray));
1071 2
			}
1072 2
		}
1073
		
1074
		/**
1075
		 * Checks if $key exists in the query
1076
		 * 
1077
		 * @param  object $object The object to use
1078
		 * @param  string $key    The key to search for
1079
		 * @return boolean        TRUE if the $key exists, FALSE otherwise
1080
		 */
1081 1
		public static function exists(&$object, $key) {
1082 1
			$qarray = \uri\generate::query_array($object);
1083 1
			return isset($qarray[$key]);
1084
		}
1085
		
1086
		/**
1087
		 * Gets a specific var's value from the query. It is HIGHLY recommended
1088
		 * that you use query_arr() instead, when fetching multiple values from
1089
		 * the same query string. Returns NULL if $key does not exist.
1090
		 * 
1091
		 * @param  object $object The object to use
1092
		 * @param  string $key    The key to get
1093
		 * @return mixed|null     The value of $key, or NULL if it does not exist.
1094
		 */
1095 1
		public static function get(&$object, $key) {
1096 1
			$qarray = \uri\generate::query_array($object);
1097 1
			if (isset($qarray[$key])) {
1098 1
				return $qarray[$key];
1099
			}
1100 1
			return NULL;
1101
		}
1102
		
1103
		/**
1104
		 * Renames a specific $key within the query. If the key exists within query
1105
		 * string and is successfully renamed, the TRUE is returned. Otherwise
1106
		 * FALSE is returned.
1107
		 * 
1108
		 * @param  object $object  The object to modify
1109
		 * @param  string $key     The key to rename
1110
		 * @param  string $new_key The new name of $key
1111
		 * @return boolean         TRUE on success, FALSE otherwise
1112
		 */
1113 3
		public static function rename(&$object, $key, $new_key) {
1114 3
			$qarray = \uri\generate::query_array($object);
1115 3
			if (isset($qarray[$key])) {
1116 2
				$qarray[$new_key] = $qarray[$key];
1117 2
				unset($qarray[$key]);
1118 2
				\uri\actions::modify($object, 'replace', 'QUERY', self::build_query($qarray));
1119 2
				return TRUE;
1120
			}
1121 2
			return FALSE;
1122
		}
1123
	}
1124
	
1125
	/**
1126
	 * The Chaining Class
1127
	 * 
1128
	 * This class is like \uri\main except that only actionable methods can be
1129
	 * called. This mean they must either modify $object or print something. It
1130
	 * also means that the normal return of all methods is replaced with this
1131
	 * class in its' current instance.
1132
	 */
1133
	class chain {
1134
		
1135
		/*** Variables ***/
1136
		private $class;
1137
		private $object;
1138
		public  $error_count;
1139
		
1140
		/*** Magic Methods ***/
1141
		
1142
		/**
1143
		 * Simple method to init a chainable object
1144
		 * 
1145
		 * @param object $class The current instance of \uri\main
1146
		 */
1147 37
		public function __construct(&$class) {
1148 37
			$this->class       = &$class;
1149 37
			$this->object      = &$class->object;
1150 37
			$this->error_count = 0;
1151 37
			return $this;
1152
		}
1153
		
1154
		/*** Methods ***/
1155
		
1156
		/**
1157
		 * Chainable alias to \uri\main::replace() within the current instance
1158
		 * 
1159
		 * @return object This instance
1160
		 */
1161 1
		public function p_str($prepend = '', $append = '') {
1162 1
			echo $prepend.\uri\generate::string($this->object).$append;
1163 1
			return $this;
1164
		}
1165
		
1166
		/**
1167
		 * Chainable alias to \uri\main::replace() within the current instance
1168
		 * 
1169
		 * @param  string $section The section to replace
1170
		 * @param  string $str     The string to replace the section with
1171
		 * @return object          This instance
1172
		 */
1173 3
		public function replace($section, $str) {
1174 3
			if (\uri\actions::modify($this->object, 'replace', $section, $str) === FALSE) {
1175 1
				$this->error_count++;
1176 1
			}
1177 3
			return $this;
1178
		}
1179
		
1180
		/**
1181
		 * Chainable alias to \uri\main::prepend() within the current instance
1182
		 * 
1183
		 * @param  string $section The section to prepend
1184
		 * @param  string $str     The string to prepend the section with
1185
		 * @return object          This instance
1186
		 */
1187 2
		public function prepend($section, $str) {
1188 2
			if (\uri\actions::modify($this->object, 'prepend', $section, $str) === FALSE) {
1189 1
				$this->error_count++;
1190 1
			}
1191 2
			return $this;
1192
		}
1193
		
1194
		/**
1195
		 * Chainable alias to \uri\main::append() within the current instance
1196
		 * 
1197
		 * @param  string $section The section to append
1198
		 * @param  string $str     The string to append the section with
1199
		 * @return object          This instance
1200
		 */
1201 2
		public function append($section, $str) {
1202 2
			if (\uri\actions::modify($this->object, 'append', $section, $str) === FALSE) {
1203 1
				$this->error_count++;
1204 1
			}
1205 2
			return $this;
1206
		}
1207
		
1208
		/**
1209
		 * Chainable alias to \uri\main::query_add() within the current instance
1210
		 * 
1211
		 * @param  string $key   The key to add
1212
		 * @param  mixed  $value The value of $key
1213
		 * @return object        This instance
1214
		 */
1215 1
		public function query_add($key, $value) {
1216 1
			\uri\query::add($this->object, $key, $value);
1217 1
			return $this;
1218
		}
1219
		
1220
		/**
1221
		 * Chainable alias to \uri\main::query_replace() within the current instance
1222
		 * 
1223
		 * @param  string $key   The key to replace
1224
		 * @param  mixed  $value The value of $key
1225
		 * @return object        This instance
1226
		 */
1227 1
		public function query_replace($key, $value) {
1228 1
			\uri\query::replace($this->object, $key, $value);
1229 1
			return $this;
1230
		}
1231
		
1232
		/**
1233
		 * Chainable alias to \uri\main::query_remove() within the current instance
1234
		 * 
1235
		 * @param  string $key The key to remove
1236
		 * @return object      This instance
1237
		 */
1238 1
		public function query_remove($key) {
1239 1
			\uri\query::remove($this->object, $key);
1240 1
			return $this;
1241
		}
1242
		
1243
		/**
1244
		 * Chainable alias to \uri\main::query_rename() within the current instance
1245
		 * 
1246
		 * @param  string $key     The key to rename
1247
		 * @param  string $new_key The new name of $key
1248
		 * @return object          This instance
1249
		 */
1250 2
		public function query_rename($key, $new_key) {
1251 2
			if (\uri\query::rename($this->object, $key, $new_key) === FALSE) {
1252 1
				$this->error_count++;
1253 1
			}
1254 2
			return $this;
1255
		}
1256
		
1257
		/**
1258
		 * Chainable alias to \uri\main::reset() within the current instance
1259
		 * 
1260
		 * @return object This instance
1261
		 */
1262 1
		public function reset() {
1263 1
			$this->class->__construct($this->class->input);
1264 1
			return $this;
1265
		}
1266
		
1267
		/**
1268
		 * Provides a simple error handle for invalid chainable methods
1269
		 * 
1270
		 * @param  array $trace Debug Backtrace
1271
		 * @return void
1272
		 */
1273 1
		private function _err($trace) {
1274 1
			$this->error_count++;
1275 1
			trigger_error(
1276 1
				sprintf(
1277 1
					'The method <code>%1$s()</code> cannot be chained in <b>%2$s</b> on line <b>%3$s</b>. Error triggered',
1278 1
					$trace[0]['function'],
1279 1
					$trace[0]['file'],
1280 1
					$trace[0]['line']
1281 1
				),
1282
				E_USER_NOTICE
1283 1
			);
1284 1
		}
1285
		
1286
		/*** Invalid Chaining Methods ***/
1287
		
1288
		/**
1289
		 * Invalid Chaining Method
1290
		 */
1291 1
		public function str() {
1292 1
			$this->_err(debug_backtrace());
1293 1
			return $this;
1294
		}
1295
		
1296
		/**
1297
		 * Invalid Chaining Method
1298
		 */
1299 1
		public function to_string() {
1300 1
			$this->_err(debug_backtrace());
1301 1
			return $this;
1302
		}
1303
		
1304
		/**
1305
		 * Invalid Chaining Method
1306
		 */
1307 1
		public function arr() {
1308 1
			$this->_err(debug_backtrace());
1309 1
			return $this;
1310
		}
1311
		
1312
		/**
1313
		 * Invalid Chaining Method
1314
		 */
1315 1
		public function to_array() {
1316 1
			$this->_err(debug_backtrace());
1317 1
			return $this;
1318
		}
1319
		
1320
		/**
1321
		 * Invalid Chaining Method
1322
		 */
1323 1
		public function path_info() {
1324 1
			$this->_err(debug_backtrace());
1325 1
			return $this;
1326
		}
1327
		
1328
		/**
1329
		 * Invalid Chaining Method
1330
		 */
1331 1
		public function query_array() {
1332 1
			$this->_err(debug_backtrace());
1333 1
			return $this;
1334
		}
1335
		
1336
		/**
1337
		 * Invalid Chaining Method
1338
		 */
1339 1
		public function query_exists() {
1340 1
			$this->_err(debug_backtrace());
1341 1
			return $this;
1342
		}
1343
		
1344
		/**
1345
		 * Invalid Chaining Method
1346
		 */
1347 1
		public function query_get() {
1348 1
			$this->_err(debug_backtrace());
1349 1
			return $this;
1350
		}
1351
	}
1352
}