YHttpRequest.class.php
36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
<?php
/**
* 上下文处理类
*
* @name Framework_YHttpRequest
* @version 0.1 2012-6-25
* @package framework
* @license 引用qee实现方式
* @author xiaoma
*/
class Framework_YHttpRequest
{
/**
* 指示 UDI 中的部分
*/
// UDI 中的所有部分
const UDI_ALL = 'all';
// UDI 中的控制器
const UDI_CONTROLLER = 'controller';
// UDI 中的动作
const UDI_ACTION = 'action';
// UDI 中的名字空间
const UDI_NAMESPACE = 'namespace';
// UDI 中的模块
const UDI_MODULE = 'module';
/**
* 指示 UDI 的默认值
*/
// 默认控制器
const UDI_DEFAULT_CONTROLLER = 'default';
// 默认动作
const UDI_DEFAULT_ACTION = 'index';
// 默认的模块
const UDI_DEFAULT_MODULE = 'default';
// 默认的名字空间
const UDI_DEFAULT_NAMESPACE = 'default';
/**
* 指示 URL 模式
*/
// 标准 URL 模式
const URL_MODE_STANDARD = 'standard';
// 使用 PATHINFO
const URL_MODE_PATHINFO = 'pathinfo';
// 使用 URL 重写
const URL_MODE_REWRITE = 'rewrite';
/**
* 请求包含的模块名
*
* 为了性能原因,$module_name 设置为了 public 成员变量。
* 但开发者应该使用 changeRequestUDI() 方法来修改 $module_name 等变量。
*
* @var string
*/
public $module_name;
/**
* 请求包含的命名空间
*
* @var string
*/
public $namespace;
/**
* 请求包含的控制器名称
*
* @var string
*/
public $controller_name;
/**
* 请求包含的动作名
*
* @var string
*/
public $action_name;
/**
* 附加的参数
*
* @var array
*/
private $_params = array ();
/**
* 路由对象
*
* @var Framework_YRouter
*/
private $_router;
/**
* 域名配置
*
* @var array
*/
private $_domain_config;
/**
* 当前请求 URL
*
* @var string
*/
private static $_request_uri;
/**
* 当前请求 URL 不包含查询参数的部分
*
* @var string
*/
private static $_base_uri;
/**
* 当前请求 URL 的目录部分
*
* @var string
*/
private static $_base_dir;
/**
* 路由模式
*
* @var string
*/
private static $_url_mode;
/**
* UDI 的默认值
*/
private static $_udi_defaults = array(
self::UDI_MODULE => self::UDI_DEFAULT_MODULE,
self::UDI_NAMESPACE => self::UDI_DEFAULT_NAMESPACE,
self::UDI_CONTROLLER => self::UDI_DEFAULT_CONTROLLER,
self::UDI_ACTION => self::UDI_DEFAULT_ACTION
);
/**
* 构造函数
*/
private function __construct()
{
$this->reinit();
}
/**
* 根据服务器运行环境,重新初始化 Framework_YHttpRequest 对象
*
* @param boolean $full_init 是否进行完全的初始化
*/
public function reinit($full_init = false)
{
if ($full_init)
{
$this->_params = array ();
$this->_router = null;
}
self::$_request_uri = null;
self::$_base_uri = null;
self::$_base_dir = null;
self::$_url_mode = null;
// @todo 如果有必要,初始化路由服务
$url_mode = C(Y_CONFIG . '.Dispatcher_Url_Mode');
if (is_null ( $this->_router ) && ($url_mode == self::URL_MODE_PATHINFO || $url_mode == self::URL_MODE_REWRITE))
{
$this->_router = new Framework_YRouter ();
$router_file = C(Y_CONFIG . '.Config_Dir') . DS . 'Router.inc.php';
if (file_exists ( $router_file ))
{
$routers = include $router_file;
}
else
{
$routers = $this->_router->getDefaultRouter ();
}
//@TODO 导入配置
$this->_router->import ( $routers );
$result = $this->_router->match ( '/' . ltrim ( $this->pathinfo (), '/' ) );
if ($result)
{
foreach ( $result as $var => $value )
{
if (strlen ( $value ) === 0)
continue;
if (! isset ( $_GET [$var] ) || strlen ( $_GET [$var] ) === 0)
{
$_GET [$var] = $_REQUEST [$var] = $value;
}
}
}
}
self::$_url_mode = $url_mode;
//初始化二级域名配置
$domain_config = $this->FormatDomainConfig ();
if ($domain_config && isset ( $domain_config ['config'] ))
{
//获取当前域名
$current_domain = $this->currentDomain ();
//获取当前域名所使用的配置,允许的模块及分组
$exist_domain = array_keys ( $domain_config ['config'], $current_domain );
//默认域名配置
$domain_default = isset ( $domain_config ['default_config'] [$current_domain] ) ? $domain_config ['default_config'] [$current_domain] : array ();
$domain_default = $this->normalizeUDI ( $domain_default );
if (! empty ( $exist_domain ))
{
//如果访问的modules或namespace不在配置里面,则使用默认操作地址
$http_module = ($this->get ( self::UDI_MODULE )) ? $this->get ( self::UDI_MODULE ) : self::UDI_DEFAULT_MODULE;
$http_namespace = ($this->get ( self::UDI_NAMESPACE )) ? $this->get ( self::UDI_NAMESPACE ) : self::UDI_DEFAULT_NAMESPACE;
//匹配两种模式 即 1. module/namespace 2. module/*
$match1 = $http_module . '/' . $http_namespace;
$match2 = $http_module . '/*';
//验证是否存在于配置中,不存在则使用默认配置
if (! in_array ( $match1, $exist_domain ) && ! in_array ( $match2, $exist_domain ))
{
foreach ( $domain_default as $var => $value )
{
$_GET [$var] = $_REQUEST [$var] = $value;
}
}
}
}
// 从 $_GET 中提取请求参数
$keys = array_keys ( $_GET );
if (! empty ( $keys ))
{
$keys = array_combine($keys, $keys);
$keys = array_change_key_case ( $keys );
}
$udi = array ();
$udi [self::UDI_CONTROLLER] = (isset ( $keys [self::UDI_CONTROLLER] )) ? $_GET [$keys [self::UDI_CONTROLLER]] : null;
$udi [self::UDI_ACTION] = (isset ( $keys [self::UDI_ACTION] )) ? $_GET [$keys [self::UDI_ACTION]] : null;
$udi [self::UDI_MODULE] = (isset ( $keys [self::UDI_MODULE] )) ? $_GET [$keys [self::UDI_MODULE]] : null;
$udi [self::UDI_NAMESPACE] = (isset ( $keys [self::UDI_NAMESPACE] )) ? $_GET [$keys [self::UDI_NAMESPACE]] : null;
$this->changeRequestUDI ( $udi );
}
/**
* 返回 Framework_YHttpRequest 对象的唯一实例
*
* @code php
* $context = Framework_YHttpRequest::instance();
* @endcode
*
* @return Framework_YHttpRequest Framework_YHttpRequest 对象的唯一实例
*/
public static function instance()
{
static $instance = null;
if (null === $instance)
{
$instance = new Framework_YHttpRequest();
}
return $instance;
}
/**
* 魔法方法,访问请求参数
*
* __get() 魔法方法让开发者可以用 $context->parameter 的形式访问请求参数。
* 如果指定的参数不存在,则返回 null。
*
* @code php
* $title = $context->title;
* @endcode
*
* 查找请求参数的顺行是 $_GET、$_POST 和 Framework_YHttpRequest 对象附加参数。
*
* @param string $parameter 要访问的请求参数
*
* @return mixed 参数值
*/
public function __get($parameter)
{
return $this->query ( $parameter );
}
/**
* 魔法方法,设置附加参数
*
* 与 __get() 魔法方法不同,__set() 仅仅设置 Framework_YHttpRequest 对象附加参数。
* 因此当 $_GET 或 $_POST 中包含同名参数时,用 __set() 设置的参数值
* 只能使用 Framework_YHttpRequest::param() 方法来获得。
*
* @code php
* $context->title = $title;
* echo $context->param('title');
* @endcode
*
* @param string $parameter 要设置值的参数名
* @param mixed $value 参数值
*/
public function __set($parameter, $value)
{
$this->changeParam ( $parameter, $value );
}
/**
* 魔法方法,确定是否包含指定的参数
*
* @param string $parameter 要检查的参数
*
* @return boolean 是否具有指定参数
*/
public function __isset($parameter)
{
return $this->offsetExists ( $parameter );
}
/**
* 删除指定的附加参数
*
* __unset() 魔法方法只影响 Framework_YHttpRequest 对象的附加参数。
*
* @code php
* unset($context['title']);
* // 此时读取 title 附加参数将获得 null
* echo $context->param('title');
* @endcode
*
* @param string $parameter 要删除的参数
*/
public function __unset($parameter)
{
unset ( $this->_params [$parameter] );
}
/**
* 确定是否包含指定的参数,实现 ArrayAccess 接口
*
* @code php
* echo isset($context['title']);
* @endcode
*
* @param string $parameter 要检查的参数
*
* @return boolean 参数是否存在
*/
public function offsetExists($parameter)
{
if (isset ( $_GET [$parameter] ))
return true;
elseif (isset ( $_POST [$parameter] ))
return true;
else
return isset ( $this->_params [$parameter] );
}
/**
* 设置附加参数,实现 ArrayAccess 接口
*
* 该方法功能同 __set() 魔法方法。
*
* @code php
* $context['title'] = $title;
* echo $context->param('title');
* @endcode
*
* @param string $parameter 要设置的参数
* @param mixed $value 参数值
*/
public function offsetSet($parameter, $value)
{
$this->changeParam ( $parameter, $value );
}
/**
* 访问请求参数,实现 ArrayAccess 接口
*
* @code php
* $title = $context['title'];
* @endcode
*
* @param string $parameter 要访问的请求参数
*
* @return mixed 参数值
*/
public function offsetGet($parameter)
{
return $this->query ( $parameter );
}
/**
* 取消附加参数,实现 ArrayAccess 接口
*
* 同 __unset() 方法,Framework_YHttpRequest::offsetUnset() 只影响 Framework_YHttpRequest 对象的附加参数。
*
* @code php
* unset($context['title']);
* @endcode
*
* @param string $parameter 要取消的附加参数
*/
public function offsetUnset($parameter)
{
unset ( $this->_params [$parameter] );
}
/**
* 魔法方法,访问请求参数
*
* Framework_YHttpRequest::get() 方法让开发者可以用 $context->parameter 的形式访问请求参数。
* 如果指定的参数不存在,则返回 $default 参数指定的默认值。
*
* @code php
* $title = $context->query('title', 'default title');
* @endcode
*
* 查找请求参数的顺行是 $_GET、$_POST 和 Framework_YHttpRequest 对象附加参数。
*
* @param string $parameter 要访问的请求参数
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function query($parameter = null, $default = null)
{
if ($parameter === null)
{
return $_REQUEST ;
}
if (isset ( $_GET [$parameter] ))
return $_GET [$parameter];
elseif (isset ( $_POST [$parameter] ))
return $_POST [$parameter];
elseif (isset ( $this->_params [$parameter] ))
return $this->_params [$parameter];
else
return $default;
}
/**
* 获得 GET 数据
*
* 从 $_GET 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $title = $context->get('title', 'default title');
* @endcode
*
* 如果 $parameter 参数为 null,则返回整个 $_GET 的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function get($parameter = null, $default = null)
{
if (is_null ( $parameter ))
return $_GET;
return isset ( $_GET [$parameter] ) ? $_GET [$parameter] : $default;
}
/**
* 获得 POST 数据
*
* 从 $_POST 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $body = $context->post('body', 'default body');
* @endcode
*
* 如果 $parameter 参数为 null,则返回整个 $_POST 的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function post($parameter = null, $default = null)
{
if (is_null ( $parameter ))
return $_POST;
return isset ( $_POST [$parameter] ) ? $_POST [$parameter] : $default;
}
/**
* 获得 Cookie 数据
*
* 从 $_COOKIE 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $auto_login = $context->cookie('auto_login');
* @endcode
*
* 如果 $parameter 参数为 null,则返回整个 $_COOKIE 的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function cookie($parameter = null, $default = null)
{
if (is_null ( $parameter ))
return $_COOKIE;
return isset ( $_COOKIE [$parameter] ) ? $_COOKIE [$parameter] : $default;
}
/**
* 从 $_SERVER 查询服务器运行环境数据
*
* 如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $request_time = $context->server('REQUEST_TIME');
* @endcode
*
* 如果 $parameter 参数为 null,则返回整个 $_SERVER 的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function server($parameter = null, $default = null)
{
if (is_null ( $parameter ))
return $_SERVER;
return isset ( $_SERVER [$parameter] ) ? $_SERVER [$parameter] : $default;
}
/**
* 从 $_ENV 查询服务器运行环境数据
*
* 如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $os_type = $context->env('OS', 'non-win');
* @endcode
*
* 如果 $parameter 参数为 null,则返回整个 $_ENV 的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function env($parameter = null, $default = null)
{
if (is_null ( $parameter ))
return $_ENV;
return isset ( $_ENV [$parameter] ) ? $_ENV [$parameter] : $default;
}
/**
* 设置 Framework_YHttpRequest 对象的附加参数
*
* @code php
* $context->changeParam('arg', $value);
* @endcode
*
* @param string $parameter 要设置的参数名
* @param mixed $value 参数值
*
* @return Framework_YHttpRequest 返回 Framework_YHttpRequest 对象本身,实现连贯接口
*/
public function changeParam($parameter, $value)
{
$this->_params [$parameter] = $value;
}
/**
* 获得 Framework_YHttpRequest 对象的附加参数
*
* 如果参数不存在则返回 $default 指定的默认值。
*
* @code php
* $value = $context->param('arg', 'default value');
* @endcode
*
* 如果 $parameter 参数为 null,则返回所有附加参数的内容。
*
* @param string $parameter 要查询的参数名
* @param mixed $default 参数不存在时要返回的默认值
*
* @return mixed 参数值
*/
public function param($parameter, $default = null)
{
if (is_null ( $parameter ))
return $this->_params;
return isset ( $this->_params [$parameter] ) ? $this->_params [$parameter] : $default;
}
/**
* 返回所有上下文参数
*
*/
public function params()
{
return $this->_params;
}
/**
* 设置上下文件参数
*
*/
public function setParams(array $args)
{
$this->_params = array_merge($this->_params, $args);
}
/**
* 取得当前请求使用的协议
*
* 返回值不包含协议的版本。常见的返回值是 HTTP。
*
* @code php
* $protocol = $context->protocol();
* echo $protocol;
* @endcode
*
* @return string 当前请求使用的协议
*/
public function protocol()
{
static $protocol;
if (is_null ( $protocol ))
{
list ( $protocol ) = explode ( '/', $_SERVER ['SERVER_PROTOCOL'] );
}
return strtolower ( $protocol );
}
/**
* 设置 REQUEST_URI
*
* 这项修改不会影响 $_SERVER 中值,但是修改后 Framework_YHttpRequest::requestUri() 将返回新值。
* 同时还影响 Framework_YHttpRequest::baseUri() 和 Framework_YHttpRequest::baseDir() 的返回结果。
*
* @param string $request_uri 新的 REQUEST_URI 值
*
* @return Framework_YHttpRequest 返回 Framework_YHttpRequest 对象本身,实现连贯接口
*/
public function changeRequestUri($request_uri)
{
self::$_request_uri = $request_uri;
self::$_base_uri = self::$_base_dir = null;
return $this;
}
/**
* 确定请求的完整 URL
*
* 几个示例:
*
* <ul>
* <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
* <li>返回 /index.php?controller=posts&action=create</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
* <li>返回 /news/index.php?controller=posts&action=create</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/index.php/posts/create</li>
* <li>返回 /index.php/posts/create</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/show/id/1</li>
* <li>返回 /news/show/id/1</li>
* </ul>
*
* 此方法参考 Zend Framework 实现。
*
* @return string 请求的完整 URL
*/
public function requestUri() {
if (self::$_request_uri)
return self::$_request_uri;
if (isset ( $_SERVER ['HTTP_X_REWRITE_URL'] )) {
$uri = $_SERVER ['HTTP_X_REWRITE_URL'];
} elseif (isset ( $_SERVER ['REQUEST_URI'] )) {
$uri = $_SERVER ['REQUEST_URI'];
} elseif (isset ( $_SERVER ['ORIG_PATH_INFO'] )) {
$uri = $_SERVER ['ORIG_PATH_INFO'];
if (! empty ( $_SERVER ['QUERY_STRING'] )) {
$uri .= '?' . $_SERVER ['QUERY_STRING'];
}
} else {
$uri = '';
}
self::$_request_uri = $uri;
return $uri;
}
/**
* 返回不包含任何查询参数的 URI(但包含脚本名称)
*
* 几个示例:
*
* <ul>
* <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
* <li>返回 /index.php</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
* <li>返回 /news/index.php</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/index.php/posts/create</li>
* <li>返回 /index.php</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/show/id/1</li>
* <li>返回 /news/show/id/1</li>
* <li>假设使用了 URL 重写,并且 index.php 位于根目录</li>
* </ul>
*
* 此方法参考 Zend Framework 实现。
*
* @return string 请求 URL 中不包含查询参数的部分
*/
public function baseUri() {
if (self::$_base_uri)
return self::$_base_uri;
$filename = basename ( $_SERVER ['SCRIPT_FILENAME'] );
if (basename ( $_SERVER ['SCRIPT_NAME'] ) === $filename) {
$url = $_SERVER ['SCRIPT_NAME'];
} elseif (basename ( $_SERVER ['PHP_SELF'] ) === $filename) {
$url = $_SERVER ['PHP_SELF'];
} elseif (isset ( $_SERVER ['ORIG_SCRIPT_NAME'] ) && basename ( $_SERVER ['ORIG_SCRIPT_NAME'] ) === $filename) {
$url = $_SERVER ['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
} else {
// Backtrack up the script_filename to find the portion matching
// php_self
$path = $_SERVER ['PHP_SELF'];
$segs = explode ( '/', trim ( $_SERVER ['SCRIPT_FILENAME'], '/' ) );
$segs = array_reverse ( $segs );
$index = 0;
$last = count ( $segs );
$url = '';
do {
$seg = $segs [$index];
$url = '/' . $seg . $url;
++ $index;
} while ( ($last > $index) && (false !== ($pos = strpos ( $path, $url ))) && (0 != $pos) );
}
// Does the baseUrl have anything in common with the request_uri?
$request_uri = $this->requestUri ();
if (0 === strpos ( $request_uri, $url )) {
// full $url matches
self::$_base_uri = $url;
return self::$_base_uri;
}
if (0 === strpos ( $request_uri, dirname ( $url ) )) {
// directory portion of $url matches
self::$_base_uri = rtrim ( dirname ( $url ), '/' ) . '/';
return self::$_base_uri;
}
if (! strpos ( $request_uri, basename ( $url ) )) {
// no match whatsoever; set it blank
return '';
}
// If using mod_rewrite or ISAPI_Rewrite strip the script filename
// out of baseUrl. $pos !== 0 makes sure it is not matching a value
// from PATH_INFO or QUERY_STRING
if ((strlen ( $request_uri ) >= strlen ( $url )) && ((false !== ($pos = strpos ( $request_uri, $url ))) && ($pos !== 0))) {
$url = substr ( $request_uri, 0, $pos + strlen ( $url ) );
}
self::$_base_uri = rtrim ( $url, '/' ) . '/';
return self::$_base_uri;
}
/**
* 返回请求 URL 中的基础路径(不包含脚本名称)
*
* 几个示例:
*
* <ul>
* <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
* <li>返回 /</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
* <li>返回 /news/</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/index.php/posts/create</li>
* <li>返回 /</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/show/id/1</li>
* <li>返回 /</li>
* </ul>
*
* @return string 请求 URL 中的基础路径
*/
public function baseDir() {
if (self::$_base_dir)
return self::$_base_dir;
$base_uri = $this->baseUri ();
if (substr ( $base_uri, - 1, 1 ) == '/') {
$base_dir = $base_uri;
} else {
$base_dir = dirname ( $base_uri );
}
self::$_base_dir = rtrim ( $base_dir, '/\\' ) . '/';
return self::$_base_dir;
}
/**
* 返回服务器响应请求使用的端口
*
* 通常服务器使用 80 端口与客户端通信,该方法可以获得服务器所使用的端口号。
*
* @return string 服务器响应请求使用的端口
*/
public function serverPort() {
static $server_port = null;
if ($server_port)
return $server_port;
if (isset ( $_SERVER ['SERVER_PORT'] )) {
$server_port = intval ( $_SERVER ['SERVER_PORT'] );
} else {
$server_port = 80;
}
if (isset ( $_SERVER ['HTTP_HOST'] )) {
$arr = explode ( ':', $_SERVER ['HTTP_HOST'] );
$count = count ( $arr );
if ($count > 1) {
$port = intval ( $arr [$count - 1] );
if ($port != $server_port) {
$server_port = $port;
}
}
}
return $server_port;
}
/**
* 获得响应请求的脚本文件名
*
* @return string 响应请求的脚本文件名
*/
public function scriptName() {
return basename ( $_SERVER ['SCRIPT_FILENAME'] );
}
/**
* 返回 PATHINFO 信息
*
* <ul>
* <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
* <li>返回 /</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
* <li>返回 /</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/index.php/posts/create</li>
* <li>返回 /</li>
* </ul>
* <ul>
* <li>请求 http://www.example.com/news/show/id/1</li>
* <li>返回 /news/show/id/1</li>
* <li>假设使用了 URL 重写,并且 index.php 位于根目录</li>
* </ul>
*
* 此方法参考 Zend Framework 实现。
*
* @return string
*/
public function pathinfo() {
if (! empty ( $_SERVER ['PATH_INFO'] ))
return $_SERVER ['PATH_INFO'];
$base_url = $this->baseUri ();
if (null === ($request_uri = $this->requestUri ()))
return '';
// Remove the query string from REQUEST_URI
if (($pos = strpos ( $request_uri, '?' ))) {
$request_uri = substr ( $request_uri, 0, $pos );
}
if ((null !== $base_url) && (false === ($pathinfo = substr ( $request_uri, strlen ( $base_url ) )))) {
// If substr() returns false then PATH_INFO is set to an empty string
$pathinfo = '';
} elseif (null === $base_url) {
$pathinfo = $request_uri;
}
return $pathinfo;
}
/**
* 返回请求使用的方法
*
* @return string
*/
public function requestMethod() {
return $_SERVER ['REQUEST_METHOD'];
}
/**
* 是否是 GET 请求
*
* @return boolean
*/
public function isGet()
{
return $this->requestMethod () == 'GET';
}
/**
* 是否是 POST 请求
*
* @return boolean
*/
public function isPost()
{
return $this->requestMethod () == 'POST';
}
/**
* 是否是 PUT 请求
*
* @return boolean
*/
public function isPut()
{
return $this->requestMethod () == 'PUT';
}
/**
* 是否是 DELETE 请求
*
* @return boolean
*/
public function isDelete()
{
return $this->requestMethod () == 'DELETE';
}
/**
* 是否是 HEAD 请求
*
* @return boolean
*/
public function isHead()
{
return $this->requestMethod () == 'HEAD';
}
/**
* 是否是 OPTIONS 请求
*
* @return boolean
*/
public function isOptions()
{
return $this->requestMethod () == 'OPTIONS';
}
/**
* 判断 HTTP 请求是否是通过 XMLHttp 发起的
*
* @return boolean
*/
public function isAjax()
{
return strtolower ( $this->header ( 'X_REQUESTED_WITH' ) ) == 'xmlhttprequest';
}
/**
* 判断 HTTP 请求是否是通过 Flash 发起的
*
* @return boolean
*/
public function isFlash()
{
return strtolower ( $this->header ( 'USER_AGENT' ) ) == 'shockwave flash';
}
/**
* 返回请求的原始内容
*
* @return string
*/
function requestRawBody() {
$body = file_get_contents ( 'php://input' );
return (strlen ( trim ( $body ) ) > 0) ? $body : false;
}
/**
* 返回 HTTP 请求头中的指定信息,如果没有指定参数则返回 false
*
* @param string $header 要查询的请求头参数
*
* @return string 参数值
*/
public function header($header) {
$temp = 'HTTP_' . strtoupper ( str_replace ( '-', '_', $header ) );
if (! empty ( $_SERVER [$temp] ))
return $_SERVER [$temp];
if (function_exists ( 'apache_request_headers' )) {
$headers = apache_request_headers ();
if (! empty ( $headers [$header] ))
return $headers [$header];
}
return false;
}
/**
* 返回当前请求的参照 URL
*
* @return string 当前请求的参照 URL
*/
public function referer() {
return $this->header ( 'REFERER' );
}
/**
* 返回当前使用的路由服务对象
*
* 如果没有启动路由,则返回 null。
*
* @return Framework_YRouter 当前使用的路由服务对象
*/
public function router() {
return $this->_router;
}
/**
* 构造 url
*
* 用法:
*
* @code php
* url(UDI, [附加参数数组], [路由名])
* @endcode
*
* UDI 是统一目的地标识符(Uniform Destination Identifier)的缩写。
* UDI 由控制器、动作、名字空间以及模块名组成,采用如下的格式:
*
* @code php
* namespace::controller/action@module
* @endcode
*
* UDI 字符串中,每一个部分都是可选的。
* 如果没有提供控制器和动作名,则使用当前的控制器和默认动作名(index)代替。
* 同样,如果未提供模块名和名字空间,均使用当前值代替。
*
* UDI 字符串写法示例:
*
* @code php
* 'controller'
* 'controller/action'
* '/action'
* 'controller@module'
* 'controller/action@module'
* 'namespace::controller'
* 'namespace::controller/action'
* 'namespace::controller@module'
* 'namespace::controller/action@module'
* '@module'
* 'namespace::@module'
* @endcode
*
* 示例:
* @code php
* url('admin::posts/edit', array('id' => $post->id()));
* @endcode
*
* $params 参数除了采用数组,还可以是以“/”符号分割的字符串:
*
* @code php
* url('posts/index', 'page/3');
* url('users/show', 'id/5/profile/yes');
* @endcode
*
* 在使用 PATHINFO 和 URL 重写时,可以使用通过制定路由名来强制要求
* 采用指定的路由规则来生成 URL。强制指定路由规则可以加快 URL 的生成,
* 但在路由规则名称发生变化时,需要修改生成 URL 的代码。
*
* $opts 参数用于控制如何生成 URL。可用的选项有:
*
* - base_uri: 指定 URL 前部要添加的路径(可以包括协议、域名和端口,以及路径)
* - script: 指定 URL 前部要使用的脚本名
* - mode: 指定 URL 生成模式,可以是 standard、pathinfo 和 rewrite
*
* @param string $udi UDI 字符串
* @param array|string $params 附加参数数组
* @param string $route_name 路由名
* @param array $opts 控制如何生成 URL 的选项
*
* @return string 生成的 URL 地址
*/
public function url($udi, $params = null, $route_name = null, array $opts = null) {
static $base_uri;
if (is_null ( $base_uri )) {
$base_uri = '/' . trim ( $this->baseDir (), '/' );
if ($base_uri != '/') {
$base_uri .= '/';
}
}
$udi = $this->normalizeUDI ( $udi );
//组装前缀域名开始
$base_url = $base_uri;
//如果配置了domain_config,则进行二级域名匹配
if ($this->_domain_config) {
$domain_config = $this->_domain_config ['config'];
$match_module = $udi [self::UDI_MODULE] . '/*';
$match_namespace = $udi [self::UDI_MODULE] . '/' . $udi [self::UDI_NAMESPACE];
$domain = $this->_domain_config ['main_domain'];
do {
if (isset ( $domain_config [$match_namespace] )) {
$domain = $domain_config [$match_namespace];
break;
}
if (isset ( $domain_config [$match_module] )) {
$domain = $domain_config [$match_module];
break;
}
} while ( false );
$base_url = $domain . $base_url;
}
if (is_string ( $params )) {
$arr = explode ( '/', $params );
$params = array ();
while ( $key = array_shift ( $arr ) ) {
$val = array_shift ( $arr );
if (! is_object ( $val ) && ! is_array ( $val ) && @strlen ( $val ) > 0)
$params [$key] = $val;
}
} elseif (is_array ( $params )) {
foreach ( $params as $key => $val ) {
if (is_object ( $val ) || is_array ( $val ) || @strlen ( $val ) == 0)
unset ( $params [$key] );
}
}
if (! is_array ( $params ))
$params = array ();
// 处理 $opts
if (is_array ( $opts )) {
$mode = ! empty ( $opts ['mode'] ) ? $opts ['mode'] : self::$_url_mode;
$script = ! empty ( $opts ['script'] ) ? $opts ['script'] : $this->scriptName ();
$url = ! empty ( $opts ['base_uri'] ) ? rtrim ( $opts ['base_uri'], '/' ) . '/' : $base_url;
} else {
$mode = self::$_url_mode;
$url = $base_url;
$script = $this->scriptName ();
}
if (! is_null ( $this->_router ) && $mode != self::URL_MODE_STANDARD) {
// 使用路由生成 URL
$params = array_merge ( $params, $udi );
$path = $this->_router->url ( $params, $route_name );
if (self::$_url_mode == self::URL_MODE_PATHINFO && $path != '/') {
$url .= $this->scriptName ();
} else {
$url = rtrim ( $url, '/' );
}
$url .= $path;
} else {
foreach ( self::$_udi_defaults as $key => $value ) {
if ($udi [$key] == $value)
unset ( $udi [$key] );
unset ( $params [$key] );
}
$params = array_filter ( array_merge ( $udi, $params ) );
$url .= $script;
if (! empty ( $params )) {
$url .= '?' . http_build_query ( $params, '', '&' );
}
}
return $url;
}
/**
* 获取二级域名配置(如果开启二级域名的话)
*
*/
private function FormatDomainConfig() {
//增加二级域名配置
if ( C(Y_CONFIG . '.Open_Domain') && is_null ( $this->_domain_config )) {
$this->_domain_config = include C(Y_CONFIG.'.Config_Dir') . '/Domain.inc.php';
}
return $this->_domain_config;
}
/**
* 返回 UDI 的字符串表现形式
*
* @param array $udi 要处理的 UDI
*
* @return string
*/
public function UDIString(array $udi) {
return "{$udi[self::UDI_NAMESPACE]}::{$udi[self::UDI_CONTROLLER]}/{$udi[self::UDI_ACTION]}@{$udi[self::UDI_MODULE]}";
}
/**
* 返回规范化以后的 UDI 数组
*
* @code php
* $udi = array(
* Framework_YHttpRequest::UDI_CONTROLLER => '',
* Framework_YHttpRequest::UDI_ACTION => '',
* Framework_YHttpRequest::UDI_NAMESPACE => '',
* Framework_YHttpRequest::UDI_MODULE => '',
* );
*
* // 输出
* // array(
* // controller: default
* // action: index
* // namespace: default
* // module: default
* // )
* dump($context->normalizeUDI($udi));
*
* $udi = 'admin::posts/edit';
* // 输出
* // array(
* // controller: posts
* // action: edit
* // namespace: admin
* // module: default
* // )
* dump($context->normalizeUDI($udi));
* @endcode
*
* 如果要返回字符串形式的 UDI,设置 $return_array 参数为 false。
*
* @param string|array $udi 要处理的 UDI
* @param boolean $return_array 是否返回数组形式的 UDI
*
* @return array 处理后的 UDI
*/
public function normalizeUDI($udi, $return_array = true) {
if (! is_array ( $udi )) {
// 特殊处理 ".", "/" UDI解析
// url('.') 返回当前动作
// url('/') 返回当前控制器+index
if ($udi === '.') {
$namespace = $this->namespace;
$module_name = $this->module_name;
$controller = $this->controller;
$action = $this->action;
} elseif ($udi === '/') {
$namespace = $this->namespace;
$module_name = $this->module_name;
$controller = $this->controller;
$action = self::$_udi_defaults [self::UDI_ACTION];
} else {
if (strpos ( $udi, '::' ) !== false) {
$arr = explode ( '::', $udi );
$namespace = array_shift ( $arr );
$udi = array_shift ( $arr );
} else {
$namespace = $this->namespace;
}
if (strpos ( $udi, '@' ) !== false) {
$arr = explode ( '@', $udi );
$module_name = array_pop ( $arr );
$udi = array_pop ( $arr );
} else {
$module_name = $this->module_name;
}
$arr = explode ( '/', $udi );
$controller = array_shift ( $arr );
$action = array_shift ( $arr );
}
$udi = array (self::UDI_MODULE => $module_name, self::UDI_NAMESPACE => $namespace, self::UDI_CONTROLLER => $controller, self::UDI_ACTION => $action );
}
if (empty ( $udi [self::UDI_MODULE] )) {
$udi [self::UDI_MODULE] = $this->module_name;
}
if (empty ( $udi [self::UDI_NAMESPACE] )) {
$udi [self::UDI_NAMESPACE] = $this->namespace;
}
if (empty ( $udi [self::UDI_CONTROLLER] )) {
$udi [self::UDI_CONTROLLER] = $this->controller_name;
}
if (empty ( $udi [self::UDI_ACTION] )) {
$udi [self::UDI_ACTION] = self::UDI_DEFAULT_ACTION;
}
foreach ( self::$_udi_defaults as $key => $value ) {
if (empty ( $udi [$key] )) {
$udi [$key] = $value;
} else {
$udi [$key] = preg_replace ( '/[^a-z0-9]+/', '', strtolower ( $udi [$key] ) );
}
}
if (! $return_array) {
return "{$udi[self::UDI_NAMESPACE]}::{$udi[self::UDI_CONTROLLER]}/{$udi[self::UDI_ACTION]}@{$udi[self::UDI_MODULE]}";
} else {
return $udi;
}
}
/**
* 返回当前请求对应的 UDI
*
* 将当前请求中包含的控制器、动作、名字空间和模块名提取出来,构造为一个 UDI。
*
* @code php
* dump($context->requestUDI());
* @endcode
*
* @param boolean $return_array 是否返回数组形式的 UDI
*
* @return string|array 对应当前请求的 UDI
*/
public function requestUDI($return_array = true) {
return $this->normalizeUDI ( "/{$this->action_name}", $return_array );
}
/**
* 将 Framework_YHttpRequest 对象保存的请求参数设置为 UDI 指定的值
*
* @code php
* $context->changeRequestUDI('posts/edit');
* // 将输出 posts
* echo $context->controller_name;
* @endcode
*
* @param array|string $udi 要设置的 UDI
*
* @return Framework_YHttpRequest 返回 Framework_YHttpRequest 对象本身,实现连贯接口
*/
public function changeRequestUDI($udi) {
$udi = $this->normalizeUDI ( $udi );
$this->controller_name = $udi [self::UDI_CONTROLLER];
$this->action_name = $udi [self::UDI_ACTION];
$this->module_name = $udi [self::UDI_MODULE];
$this->namespace = $udi [self::UDI_NAMESPACE];
if ($this->module_name == self::UDI_DEFAULT_MODULE) {
$this->module_name = null;
}
if ($this->namespace == self::UDI_DEFAULT_NAMESPACE) {
$this->namespace = null;
}
return $this;
}
/**
* 获取当前域名地址
*
* @return URL (example: http://test.dev.yoho.cn)
* @since 0.2
*/
public function currentDomain()
{
$protocol = 'http';
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
{
$protocol .= 's';
}
$protocol .= '://';
if (! isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 80)
{
$port = '';
}
else
{
$port = ':' . $_SERVER['SERVER_PORT'];
}
if (isset($_SERVER['HTTP_HOST']))
{
return $protocol . strtolower($_SERVER['HTTP_HOST']) . $port;
}
elseif (isset($_SERVER['SERVER_NAME']))
{
return $protocol . strtolower($_SERVER['SERVER_NAME']) . $port;
}
else
{
return $protocol . getenv('SERVER_NAME') . $port;
}
}
}