Completed
Push — master ( 163c98...b90c12 )
by Jean-Christophe
07:17
created

URI::_set_uri_string()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
1
<?php
2
namespace Ajax\php\yii;
3
4
class URI {
5
6
	/**
7
	 * List of cached uri segments
8
	 *
9
	 * @var array
10
	 * @access public
11
	 *
12
	 */
13
	var $keyval=array ();
14
	/**
15
	 * Current uri string
16
	 *
17
	 * @var string
18
	 * @access public
19
	 *
20
	 */
21
	var $uri_string;
22
	/**
23
	 * List of uri segments
24
	 *
25
	 * @var array
26
	 * @access public
27
	 *
28
	 */
29
	var $segments=array ();
30
	/**
31
	 * Re-indexed list of uri segments
32
	 * Starts at 1 instead of 0
33
	 *
34
	 * @var array
35
	 * @access public
36
	 *
37
	 */
38
	var $rsegments=array ();
39
	var $permitted_uri_chars="a-z 0-9~%.:_\-";
40
	var $enable_query_strings=false;
41
42
	/**
43
	 * | 'AUTO' Default - auto detects
44
	 * | 'PATH_INFO' Uses the PATH_INFO
45
	 * | 'QUERY_STRING' Uses the QUERY_STRING
46
	 * | 'REQUEST_URI' Uses the REQUEST_URI
47
	 * | 'ORIG_PATH_INFO' Uses the ORIG_PATH_INFO
48
	 */
49
	var $uri_protocol="AUTO";
50
 //
51
52
	/**
53
	 * Constructor
54
	 *
55
	 * Simply globalizes the $RTR object. The front
56
	 * loads the Router class early on so it's not available
57
	 * normally as other classes are.
58
	 *
59
	 * @access public
60
	 *
61
	 */
62
	function __construct() {
63
		$this->uri_string=$this->_detect_uri();
64
		$this->_explode_segments();
65
	}
66
67
	// --------------------------------------------------------------------
68
69
	/**
70
	 * Get the URI String
71
	 *
72
	 * @access private
73
	 * @return string
74
	 *
75
	 */
76
	function _fetch_uri_string() {
77
		$uri="";
78
		if ($this->uri_protocol == 'AUTO') {
79
			// Is the request coming from the command line?
80
			if (php_sapi_name() == 'cli' or defined('STDIN')) {
81
				$this->_set_uri_string($this->_parse_cli_args());
82
				return;
83
			}
84
85
			// Let's try the REQUEST_URI first, this will work in most situations
86
			if ($uri=$this->_detect_uri()) {
87
				$this->_set_uri_string($uri);
88
				return;
89
			}
90
91
			// Is there a PATH_INFO variable?
92
			// Note: some servers seem to have trouble with getenv() so we'll test it two ways
93
			$path=(isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
94
			if (trim($path, '/') != '' && $path != "/" . SELF) {
95
				$this->_set_uri_string($path);
96
				return;
97
			}
98
99
			// No PATH_INFO?... What about QUERY_STRING?
100
			$path=(isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
101
			if (trim($path, '/') != '') {
102
				$this->_set_uri_string($path);
103
				return;
104
			}
105
106
			// As a last ditch effort lets try using the $_GET array
107
			if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') {
108
				$this->_set_uri_string(key($_GET));
109
				return;
110
			}
111
112
			// We've exhausted all our options...
113
			$this->uri_string='';
114
			return;
115
		}
116
117
		if ($uri == 'REQUEST_URI') {
118
			$this->_set_uri_string($this->_detect_uri());
119
			return;
120
		}
121
122
		$path=(isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
123
		$this->_set_uri_string($path);
124
	}
125
126
	// --------------------------------------------------------------------
127
128
	/**
129
	 * Set the URI String
130
	 *
131
	 * @access public
132
	 * @param string
133
	 * @return string
134
	 *
135
	 */
136
	function _set_uri_string($str) {
137
		// Filter out control characters
138
		// $str = remove_invisible_characters($str, FALSE);
139
140
		// If the URI contains only a slash we'll kill it
141
		$this->uri_string=($str == '/') ? '' : $str;
142
	}
143
144
	// --------------------------------------------------------------------
145
146
	/**
147
	 * Detects the URI
148
	 *
149
	 * This function will detect the URI automatically and fix the query string
150
	 * if necessary.
151
	 *
152
	 * @access private
153
	 * @return string
154
	 *
155
	 */
156
	private function _detect_uri() {
157
		if (!isset($_SERVER['REQUEST_URI']) or !isset($_SERVER['SCRIPT_NAME'])) {
158
			return '';
159
		}
160
161
		$uri=$_SERVER['REQUEST_URI'];
162
		if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
163
			$uri=substr($uri, strlen($_SERVER['SCRIPT_NAME']));
164
		} elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) {
165
			$uri=substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
166
		}
167
168
		// This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
169
		// URI is found, and also fixes the QUERY_STRING server var and $_GET array.
170
		if (strncmp($uri, '?/', 2) === 0) {
171
			$uri=substr($uri, 2);
172
		}
173
		$parts=preg_split('#\?#i', $uri, 2);
174
		$uri=$parts[0];
175
		if (isset($parts[1])) {
176
			$_SERVER['QUERY_STRING']=$parts[1];
177
			parse_str($_SERVER['QUERY_STRING'], $_GET);
178
		} else {
179
			$_SERVER['QUERY_STRING']='';
180
			$_GET=array ();
181
		}
182
183
		if ($uri == '/' || empty($uri)) {
184
			return '/';
185
		}
186
187
		$uri=parse_url($uri, PHP_URL_PATH);
188
189
		// Do some final cleaning of the URI and return it
190
		return str_replace(array ('//','../' ), '/', trim($uri, '/'));
191
	}
192
193
	// --------------------------------------------------------------------
194
195
	/**
196
	 * Parse cli arguments
197
	 *
198
	 * Take each command line argument and assume it is a URI segment.
199
	 *
200
	 * @access private
201
	 * @return string
202
	 *
203
	 */
204
	private function _parse_cli_args() {
205
		$args=array_slice($_SERVER['argv'], 1);
206
207
		return $args ? '/' . implode('/', $args) : '';
208
	}
209
210
	// --------------------------------------------------------------------
211
212
	/**
213
	 * Filter segments for malicious characters
214
	 *
215
	 * @access private
216
	 * @param string
217
	 * @return string
218
	 *
219
	 */
220
	function _filter_uri($str) {
221
		if ($str != '' && $this->permitted_uri_chars != '' && $this->enable_query_strings == FALSE) {
222
			// preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
223
			// compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
224
			if (!preg_match("|^[" . str_replace(array ('\\-','\-' ), '-', preg_quote($this->permitted_uri_chars, '-')) . "]+$|i", $str)) {
225
				show_error('The URI you submitted has disallowed characters.', 400);
226
			}
227
		}
228
229
		// Convert programatic characters to entities
230
		$bad=array ('$','(',')','%28','%29' );
231
		$good=array ('&#36;','&#40;','&#41;','&#40;','&#41;' );
232
233
		return str_replace($bad, $good, $str);
234
	}
235
236
	/**
237
	 * Explode the URI Segments.
238
	 * The individual segments will
239
	 * be stored in the $this->segments array.
240
	 *
241
	 * @access private
242
	 * @return void
243
	 *
244
	 */
245
	function _explode_segments() {
246
		foreach ( explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val ) {
247
			// Filter segments for security
248
			$val=trim($this->_filter_uri($val));
249
250
			if ($val != '') {
251
				$this->segments[]=$val;
252
			}
253
		}
254
	}
255
256
	// --------------------------------------------------------------------
257
	/**
258
	 * Re-index Segments
259
	 *
260
	 * This function re-indexes the $this->segment array so that it
261
	 * starts at 1 rather than 0. Doing so makes it simpler to
262
	 * use functions like $this->uri->segment(n) since there is
263
	 * a 1:1 relationship between the segment array and the actual segments.
264
	 *
265
	 * @access private
266
	 * @return void
267
	 *
268
	 */
269
	function _reindex_segments() {
270
		array_unshift($this->segments, NULL);
271
		array_unshift($this->rsegments, NULL);
272
		unset($this->segments[0]);
273
		unset($this->rsegments[0]);
274
	}
275
276
	// --------------------------------------------------------------------
277
278
	/**
279
	 * Fetch a URI Segment
280
	 *
281
	 * This function returns the URI segment based on the number provided.
282
	 *
283
	 * @access public
284
	 * @param integer
285
	 * @param bool
286
	 * @return string
287
	 *
288
	 */
289
	function segment($n, $no_result=FALSE) {
290
		return (!isset($this->segments[$n])) ? $no_result : $this->segments[$n];
291
	}
292
293
	// --------------------------------------------------------------------
294
295
	/**
296
	 * Fetch a URI "routed" Segment
297
	 *
298
	 * This function returns the re-routed URI segment (assuming routing rules are used)
299
	 * based on the number provided. If there is no routing this function returns the
300
	 * same result as $this->segment()
301
	 *
302
	 * @access public
303
	 * @param integer
304
	 * @param bool
305
	 * @return string
306
	 *
307
	 */
308
	function rsegment($n, $no_result=FALSE) {
309
		return (!isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
310
	}
311
312
	// --------------------------------------------------------------------
313
314
	/**
315
	 * Generate a key value pair from the URI string
316
	 *
317
	 * This function generates and associative array of URI data starting
318
	 * at the supplied segment. For example, if this is your URI:
319
	 *
320
	 * example.com/user/search/name/joe/location/UK/gender/male
321
	 *
322
	 * You can use this function to generate an array with this prototype:
323
	 *
324
	 * array (
325
	 * name => joe
326
	 * location => UK
327
	 * gender => male
328
	 * )
329
	 *
330
	 * @access public
331
	 * @param integer the starting segment number
332
	 * @param array an array of default values
333
	 * @return array
334
	 *
335
	 */
336
	function uri_to_assoc($n=3, $default=array()) {
337
		return $this->_uri_to_assoc($n, $default, 'segment');
338
	}
339
340
	/**
341
	 * Identical to above only it uses the re-routed segment array
342
	 *
343
	 * @access public
344
	 * @param integer the starting segment number
345
	 * @param array an array of default values
346
	 * @return array
347
	 *
348
	 *
349
	 */
350
	function ruri_to_assoc($n=3, $default=array()) {
351
		return $this->_uri_to_assoc($n, $default, 'rsegment');
352
	}
353
354
	// --------------------------------------------------------------------
355
356
	/**
357
	 * Generate a key value pair from the URI string or Re-routed URI string
358
	 *
359
	 * @access private
360
	 * @param integer the starting segment number
361
	 * @param array an array of default values
362
	 * @param string which array we should use
363
	 * @return array
364
	 *
365
	 */
366
	function _uri_to_assoc($n=3, $default=array(), $which='segment') {
367
		if ($which == 'segment') {
368
			$total_segments='total_segments';
369
			$segment_array='segment_array';
370
		} else {
371
			$total_segments='total_rsegments';
372
			$segment_array='rsegment_array';
373
		}
374
375
		if (!is_numeric($n)) {
376
			return $default;
377
		}
378
379
		if (isset($this->keyval[$n])) {
380
			return $this->keyval[$n];
381
		}
382
383
		if ($this->$total_segments() < $n) {
384
			if (count($default) == 0) {
385
				return array ();
386
			}
387
388
			$retval=array ();
389
			foreach ( $default as $val ) {
390
				$retval[$val]=FALSE;
391
			}
392
			return $retval;
393
		}
394
395
		$segments=array_slice($this->$segment_array(), ($n - 1));
396
397
		$i=0;
398
		$lastval='';
399
		$retval=array ();
400
		foreach ( $segments as $seg ) {
401
			if ($i % 2) {
402
				$retval[$lastval]=$seg;
403
			} else {
404
				$retval[$seg]=FALSE;
405
				$lastval=$seg;
406
			}
407
408
			$i++;
409
		}
410
411
		if (count($default) > 0) {
412
			foreach ( $default as $val ) {
413
				if (!array_key_exists($val, $retval)) {
414
					$retval[$val]=FALSE;
415
				}
416
			}
417
		}
418
419
		// Cache the array for reuse
420
		$this->keyval[$n]=$retval;
421
		return $retval;
422
	}
423
424
	// --------------------------------------------------------------------
425
426
	/**
427
	 * Generate a URI string from an associative array
428
	 *
429
	 *
430
	 * @access public
431
	 * @param array an associative array of key/values
432
	 * @return array
433
	 *
434
	 */
435
	function assoc_to_uri($array) {
436
		$temp=array ();
437
		foreach ( ( array ) $array as $key => $val ) {
438
			$temp[]=$key;
439
			$temp[]=$val;
440
		}
441
442
		return implode('/', $temp);
443
	}
444
445
	// --------------------------------------------------------------------
446
447
	/**
448
	 * Fetch a URI Segment and add a trailing slash
449
	 *
450
	 * @access public
451
	 * @param integer
452
	 * @param string
453
	 * @return string
454
	 *
455
	 */
456
	function slash_segment($n, $where='trailing') {
457
		return $this->_slash_segment($n, $where, 'segment');
458
	}
459
460
	// --------------------------------------------------------------------
461
462
	/**
463
	 * Fetch a URI Segment and add a trailing slash
464
	 *
465
	 * @access public
466
	 * @param integer
467
	 * @param string
468
	 * @return string
469
	 *
470
	 */
471
	function slash_rsegment($n, $where='trailing') {
472
		return $this->_slash_segment($n, $where, 'rsegment');
473
	}
474
475
	// --------------------------------------------------------------------
476
477
	/**
478
	 * Fetch a URI Segment and add a trailing slash - helper function
479
	 *
480
	 * @access private
481
	 * @param integer
482
	 * @param string
483
	 * @param string
484
	 * @return string
485
	 *
486
	 */
487
	function _slash_segment($n, $where='trailing', $which='segment') {
488
		$leading='/';
489
		$trailing='/';
490
491
		if ($where == 'trailing') {
492
			$leading='';
493
		} elseif ($where == 'leading') {
494
			$trailing='';
495
		}
496
497
		return $leading . $this->$which($n) . $trailing;
498
	}
499
500
	// --------------------------------------------------------------------
501
502
	/**
503
	 * Segment Array
504
	 *
505
	 * @access public
506
	 * @return array
507
	 *
508
	 */
509
	function segment_array() {
510
		return $this->segments;
511
	}
512
513
	// --------------------------------------------------------------------
514
515
	/**
516
	 * Routed Segment Array
517
	 *
518
	 * @access public
519
	 * @return array
520
	 *
521
	 */
522
	function rsegment_array() {
523
		return $this->rsegments;
524
	}
525
526
	// --------------------------------------------------------------------
527
528
	/**
529
	 * Total number of segments
530
	 *
531
	 * @access public
532
	 * @return integer
533
	 *
534
	 */
535
	function total_segments() {
536
		return count($this->segments);
537
	}
538
539
	// --------------------------------------------------------------------
540
541
	/**
542
	 * Total number of routed segments
543
	 *
544
	 * @access public
545
	 * @return integer
546
	 *
547
	 */
548
	function total_rsegments() {
549
		return count($this->rsegments);
550
	}
551
552
	// --------------------------------------------------------------------
553
554
	/**
555
	 * Fetch the entire URI string
556
	 *
557
	 * @access public
558
	 * @return string
559
	 *
560
	 */
561
	function uri_string() {
562
		return $this->uri_string;
563
	}
564
565
	// --------------------------------------------------------------------
566
567
	/**
568
	 * Fetch the entire Re-routed URI string
569
	 *
570
	 * @access public
571
	 * @return string
572
	 *
573
	 */
574
	function ruri_string() {
575
		return '/' . implode('/', $this->rsegment_array());
576
	}
577
}
578
// END URI Class