PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/server/vpn.51sync.com/library/core/context.php

https://github.com/qibinghua/vpn.51sync
PHP | 1375 lines | 672 code | 86 blank | 617 comment | 73 complexity | 418186493492ed5a3f900c5418a6b5d8 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. // $Id: context.php 2684 2010-07-18 17:30:05Z dualface $
  3. /**
  4. * 定义 QContext 类
  5. *
  6. * @link http://qeephp.com/
  7. * @copyright Copyright (c) 2006-2009 Qeeyuan Inc. {@link http://www.qeeyuan.com}
  8. * @license New BSD License {@link http://qeephp.com/license/}
  9. * @version $Id: context.php 2684 2010-07-18 17:30:05Z dualface $
  10. * @package core
  11. */
  12. /**
  13. * QContext 封装了运行时上下文
  14. *
  15. * 所谓运行时上下文是指 QeePHP 应用程序的运行环境本身。
  16. * QContext 主要封装了请求参数和请求状态,以及 URL 解析等功能。
  17. *
  18. * QContext 使用了单子设计模式,因此只能使用 QContext::instance() 来获得
  19. * QContext 对象的唯一实例。
  20. *
  21. * QContext 实现了 ArrayAccess 接口,可以将 QContext 对象当作数组一样使用。
  22. *
  23. * @code php
  24. * if (isset($context['title']))
  25. * {
  26. * echo $context['title'];
  27. * }
  28. * @endcode
  29. *
  30. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  31. * @version $Id: context.php 2684 2010-07-18 17:30:05Z dualface $
  32. * @package core
  33. */
  34. class QContext implements ArrayAccess
  35. {
  36. /**
  37. * 指示 UDI 中的部分
  38. */
  39. // UDI 中的所有部分
  40. const UDI_ALL = 'all';
  41. // UDI 中的控制器
  42. const UDI_CONTROLLER = 'controller';
  43. // UDI 中的动作
  44. const UDI_ACTION = 'action';
  45. // UDI 中的名字空间
  46. // UDI 中的模块
  47. const UDI_MODULE = 'module';
  48. /**
  49. * 指示 UDI 的默认值
  50. */
  51. // 默认控制器
  52. const UDI_DEFAULT_CONTROLLER = 'default';
  53. // 默认动作
  54. const UDI_DEFAULT_ACTION = 'index';
  55. // 默认的模块
  56. const UDI_DEFAULT_MODULE = 'default';
  57. // 默认的名字空间
  58. const UDI_DEFAULT_NAMESPACE = 'default';
  59. /**
  60. * 指示 URL 模式
  61. */
  62. // 标准 URL 模式
  63. const URL_MODE_STANDARD = 'standard';
  64. // 使用 PATHINFO
  65. const URL_MODE_PATHINFO = 'pathinfo';
  66. // 使用 URL 重写
  67. const URL_MODE_REWRITE = 'rewrite';
  68. /**
  69. * 请求包含的模块名
  70. *
  71. * 为了性能原因,$module_name 设置为了 public 成员变量。
  72. * 但开发者应该使用 changeRequestUDI() 方法来修改 $module_name 等变量。
  73. *
  74. * @var string
  75. */
  76. public $module_name;
  77. /**
  78. * 请求包含的控制器名称
  79. *
  80. * @var string
  81. */
  82. public $controller_name;
  83. /**
  84. * 请求包含的动作名
  85. *
  86. * @var string
  87. */
  88. public $action_name;
  89. /**
  90. * 附加的参数
  91. *
  92. * @var array
  93. */
  94. private $_params = array();
  95. /**
  96. * 路由对象
  97. *
  98. * @var QRouter
  99. */
  100. private $_router;
  101. /**
  102. * 当前请求 URL
  103. *
  104. * @var string
  105. */
  106. static private $_request_uri;
  107. /**
  108. * 当前请求 URL 不包含查询参数的部分
  109. *
  110. * @var string
  111. */
  112. static private $_base_uri;
  113. /**
  114. * 当前请求 URL 的目录部分
  115. *
  116. * @var string
  117. */
  118. static private $_base_dir;
  119. /**
  120. * 路由模式
  121. *
  122. * @var string
  123. */
  124. static private $_url_mode;
  125. /**
  126. * UDI 的默认值
  127. */
  128. private static $_udi_defaults = array(
  129. self::UDI_MODULE => self::UDI_DEFAULT_MODULE,
  130. self::UDI_CONTROLLER => self::UDI_DEFAULT_CONTROLLER,
  131. self::UDI_ACTION => self::UDI_DEFAULT_ACTION,
  132. );
  133. /**
  134. * 构造函数
  135. */
  136. private function __construct()
  137. {
  138. $this->reinit();
  139. }
  140. /**
  141. * 根据服务器运行环境,重新初始化 QContext 对象
  142. *
  143. * @param boolean $full_init 是否进行完全的初始化
  144. */
  145. function reinit($full_init = false)
  146. {
  147. if ($full_init)
  148. {
  149. $this->_params = array();
  150. $this->_router = null;
  151. }
  152. self::$_request_uri = null;
  153. self::$_base_uri = null;
  154. self::$_base_dir = null;
  155. self::$_url_mode = null;
  156. // 如果有必要,初始化路由服务
  157. $url_mode = strtolower(Q::ini('dispatcher_url_mode'));
  158. if (is_null($this->_router)
  159. && ($url_mode == self::URL_MODE_PATHINFO || $url_mode == self::URL_MODE_REWRITE))
  160. {
  161. $this->_router = new QRouter();
  162. $this->_router->import(Q::ini('routes'), 'global_default_routes');
  163. $result = $this->_router->match('/' . ltrim($this->pathinfo(), '/'));
  164. if ($result)
  165. {
  166. foreach ($result as $var => $value)
  167. {
  168. if (strlen($value) === 0) continue;
  169. if (!isset($_GET[$var]) || strlen($_GET[$var]) === 0)
  170. {
  171. $_GET[$var] = $_REQUEST[$var] = $value;
  172. }
  173. }
  174. }
  175. }
  176. self::$_url_mode = $url_mode;
  177. // 从 $_GET 中提取请求参数
  178. $keys = array_keys($_GET);
  179. if (!empty($keys))
  180. {
  181. $keys = array_combine($keys, $keys);
  182. $keys = array_change_key_case($keys);
  183. }
  184. $udi = array();
  185. $udi[self::UDI_CONTROLLER] = (isset($keys[self::UDI_CONTROLLER]))
  186. ? $_GET[$keys[self::UDI_CONTROLLER]] : null;
  187. $udi[self::UDI_ACTION] = (isset($keys[self::UDI_ACTION]))
  188. ? $_GET[$keys[self::UDI_ACTION]] : null;
  189. $udi[self::UDI_MODULE] = (isset($keys[self::UDI_MODULE]))
  190. ? $_GET[$keys[self::UDI_MODULE]] : null;
  191. $this->changeRequestUDI($udi);
  192. }
  193. /**
  194. * 返回 QContext 对象的唯一实例
  195. *
  196. * @code php
  197. * $context = QContext::instance();
  198. * @endcode
  199. *
  200. * @return QContext QContext 对象的唯一实例
  201. */
  202. static function instance()
  203. {
  204. static $instance;
  205. if (is_null($instance)) $instance = new QContext();
  206. return $instance;
  207. }
  208. /**
  209. * 魔法方法,访问请求参数
  210. *
  211. * __get() 魔法方法让开发者可以用 $context->parameter 的形式访问请求参数。
  212. * 如果指定的参数不存在,则返回 null。
  213. *
  214. * @code php
  215. * $title = $context->title;
  216. * @endcode
  217. *
  218. * 查找请求参数的顺行是 $_GET、$_POST 和 QContext 对象附加参数。
  219. *
  220. * @param string $parameter 要访问的请求参数
  221. *
  222. * @return mixed 参数值
  223. */
  224. function __get($parameter)
  225. {
  226. return $this->query($parameter);
  227. }
  228. /**
  229. * 魔法方法,设置附加参数
  230. *
  231. * 与 __get() 魔法方法不同,__set() 仅仅设置 QContext 对象附加参数。
  232. * 因此当 $_GET 或 $_POST 中包含同名参数时,用 __set() 设置的参数值
  233. * 只能使用 QContext::param() 方法来获得。
  234. *
  235. * @code php
  236. * $context->title = $title;
  237. * echo $context->param('title');
  238. * @endcode
  239. *
  240. * @param string $parameter 要设置值的参数名
  241. * @param mixed $value 参数值
  242. */
  243. function __set($parameter, $value)
  244. {
  245. $this->changeParam($parameter, $value);
  246. }
  247. /**
  248. * 魔法方法,确定是否包含指定的参数
  249. *
  250. * @param string $parameter 要检查的参数
  251. *
  252. * @return boolean 是否具有指定参数
  253. */
  254. function __isset($parameter)
  255. {
  256. return $this->offsetExists($parameter);
  257. }
  258. /**
  259. * 删除指定的附加参数
  260. *
  261. * __unset() 魔法方法只影响 QContext 对象的附加参数。
  262. *
  263. * @code php
  264. * unset($context['title']);
  265. * // 此时读取 title 附加参数将获得 null
  266. * echo $context->param('title');
  267. * @endcode
  268. *
  269. * @param string $parameter 要删除的参数
  270. */
  271. function __unset($parameter)
  272. {
  273. unset($this->_params[$parameter]);
  274. }
  275. /**
  276. * 确定是否包含指定的参数,实现 ArrayAccess 接口
  277. *
  278. * @code php
  279. * echo isset($context['title']);
  280. * @endcode
  281. *
  282. * @param string $parameter 要检查的参数
  283. *
  284. * @return boolean 参数是否存在
  285. */
  286. function offsetExists($parameter)
  287. {
  288. if (isset($_GET[$parameter]))
  289. return true;
  290. elseif (isset($_POST[$parameter]))
  291. return true;
  292. else
  293. return isset($this->_params[$parameter]);
  294. }
  295. /**
  296. * 设置附加参数,实现 ArrayAccess 接口
  297. *
  298. * 该方法功能同 __set() 魔法方法。
  299. *
  300. * @code php
  301. * $context['title'] = $title;
  302. * echo $context->param('title');
  303. * @endcode
  304. *
  305. * @param string $parameter 要设置的参数
  306. * @param mixed $value 参数值
  307. */
  308. function offsetSet($parameter, $value)
  309. {
  310. $this->changeParam($parameter, $value);
  311. }
  312. /**
  313. * 访问请求参数,实现 ArrayAccess 接口
  314. *
  315. * @code php
  316. * $title = $context['title'];
  317. * @endcode
  318. *
  319. * @param string $parameter 要访问的请求参数
  320. *
  321. * @return mixed 参数值
  322. */
  323. function offsetGet($parameter)
  324. {
  325. return $this->query($parameter);
  326. }
  327. /**
  328. * 取消附加参数,实现 ArrayAccess 接口
  329. *
  330. * 同 __unset() 方法,QContext::offsetUnset() 只影响 QContext 对象的附加参数。
  331. *
  332. * @code php
  333. * unset($context['title']);
  334. * @endcode
  335. *
  336. * @param string $parameter 要取消的附加参数
  337. */
  338. function offsetUnset($parameter)
  339. {
  340. unset($this->_params[$parameter]);
  341. }
  342. /**
  343. * 魔法方法,访问请求参数
  344. *
  345. * QContext::query() 方法让开发者可以用 $context->parameter 的形式访问请求参数。
  346. * 如果指定的参数不存在,则返回 $default 参数指定的默认值。
  347. *
  348. * @code php
  349. * $title = $context->query('title', 'default title');
  350. * @endcode
  351. *
  352. * 查找请求参数的顺行是 $_GET、$_POST 和 QContext 对象附加参数。
  353. *
  354. * @param string $parameter 要访问的请求参数
  355. * @param mixed $default 参数不存在时要返回的默认值
  356. *
  357. * @return mixed 参数值
  358. */
  359. function query($parameter, $default = null)
  360. {
  361. if (isset($_GET[$parameter]))
  362. return $_GET[$parameter];
  363. elseif (isset($_POST[$parameter]))
  364. return $_POST[$parameter];
  365. elseif (isset($this->_params[$parameter]))
  366. return $this->_params[$parameter];
  367. else
  368. return $default;
  369. }
  370. /**
  371. * 获得 GET 数据
  372. *
  373. * 从 $_GET 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
  374. *
  375. * @code php
  376. * $title = $context->get('title', 'default title');
  377. * @endcode
  378. *
  379. * 如果 $parameter 参数为 null,则返回整个 $_GET 的内容。
  380. *
  381. * @param string $parameter 要查询的参数名
  382. * @param mixed $default 参数不存在时要返回的默认值
  383. *
  384. * @return mixed 参数值
  385. */
  386. function get($parameter = null, $default = null)
  387. {
  388. if (is_null($parameter))
  389. return $_GET;
  390. return isset($_GET[$parameter]) ? $_GET[$parameter] : $default;
  391. }
  392. /**
  393. * 获得 POST 数据
  394. *
  395. * 从 $_POST 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
  396. *
  397. * @code php
  398. * $body = $context->post('body', 'default body');
  399. * @endcode
  400. *
  401. * 如果 $parameter 参数为 null,则返回整个 $_POST 的内容。
  402. *
  403. * @param string $parameter 要查询的参数名
  404. * @param mixed $default 参数不存在时要返回的默认值
  405. *
  406. * @return mixed 参数值
  407. */
  408. function post($parameter = null, $default = null)
  409. {
  410. if (is_null($parameter))
  411. return $_POST;
  412. return isset($_POST[$parameter]) ? $_POST[$parameter] : $default;
  413. }
  414. /**
  415. * 获得 Cookie 数据
  416. *
  417. * 从 $_COOKIE 中获得指定参数,如果参数不存在则返回 $default 指定的默认值。
  418. *
  419. * @code php
  420. * $auto_login = $context->cookie('auto_login');
  421. * @endcode
  422. *
  423. * 如果 $parameter 参数为 null,则返回整个 $_COOKIE 的内容。
  424. *
  425. * @param string $parameter 要查询的参数名
  426. * @param mixed $default 参数不存在时要返回的默认值
  427. *
  428. * @return mixed 参数值
  429. */
  430. function cookie($parameter = null, $default = null)
  431. {
  432. if (is_null($parameter))
  433. return $_COOKIE;
  434. return isset($_COOKIE[$parameter]) ? $_COOKIE[$parameter] : $default;
  435. }
  436. /**
  437. * 从 $_SERVER 查询服务器运行环境数据
  438. *
  439. * 如果参数不存在则返回 $default 指定的默认值。
  440. *
  441. * @code php
  442. * $request_time = $context->server('REQUEST_TIME');
  443. * @endcode
  444. *
  445. * 如果 $parameter 参数为 null,则返回整个 $_SERVER 的内容。
  446. *
  447. * @param string $parameter 要查询的参数名
  448. * @param mixed $default 参数不存在时要返回的默认值
  449. *
  450. * @return mixed 参数值
  451. */
  452. function server($parameter = null, $default = null)
  453. {
  454. if (is_null($parameter))
  455. return $_SERVER;
  456. return isset($_SERVER[$parameter]) ? $_SERVER[$parameter] : $default;
  457. }
  458. /**
  459. * 从 $_ENV 查询服务器运行环境数据
  460. *
  461. * 如果参数不存在则返回 $default 指定的默认值。
  462. *
  463. * @code php
  464. * $os_type = $context->env('OS', 'non-win');
  465. * @endcode
  466. *
  467. * 如果 $parameter 参数为 null,则返回整个 $_ENV 的内容。
  468. *
  469. * @param string $parameter 要查询的参数名
  470. * @param mixed $default 参数不存在时要返回的默认值
  471. *
  472. * @return mixed 参数值
  473. */
  474. function env($parameter = null, $default = null)
  475. {
  476. if (is_null($parameter))
  477. return $_ENV;
  478. return isset($_ENV[$parameter]) ? $_ENV[$parameter] : $default;
  479. }
  480. /**
  481. * 设置 QContext 对象的附加参数
  482. *
  483. * @code php
  484. * $context->changeParam('arg', $value);
  485. * @endcode
  486. *
  487. * @param string $parameter 要设置的参数名
  488. * @param mixed $value 参数值
  489. *
  490. * @return QContext 返回 QContext 对象本身,实现连贯接口
  491. */
  492. function changeParam($parameter, $value)
  493. {
  494. $this->_params[$parameter] = $value;
  495. }
  496. /**
  497. * 获得 QContext 对象的附加参数
  498. *
  499. * 如果参数不存在则返回 $default 指定的默认值。
  500. *
  501. * @code php
  502. * $value = $context->param('arg', 'default value');
  503. * @endcode
  504. *
  505. * 如果 $parameter 参数为 null,则返回所有附加参数的内容。
  506. *
  507. * @param string $parameter 要查询的参数名
  508. * @param mixed $default 参数不存在时要返回的默认值
  509. *
  510. * @return mixed 参数值
  511. */
  512. function param($parameter, $default = null)
  513. {
  514. if (is_null($parameter))
  515. return $this->_params;
  516. return isset($this->_params[$parameter]) ? $this->_params[$parameter] : $default;
  517. }
  518. /**
  519. * 返回所有上下文参数
  520. *
  521. *
  522. * @return array
  523. */
  524. function params()
  525. {
  526. return $this->_params;
  527. }
  528. /**
  529. * 取得当前请求使用的协议
  530. *
  531. * 返回值不包含协议的版本。常见的返回值是 HTTP。
  532. *
  533. * @code php
  534. * $protocol = $context->protocol();
  535. * echo $protocol;
  536. * @endcode
  537. *
  538. * @return string 当前请求使用的协议
  539. */
  540. function protocol()
  541. {
  542. static $protocol;
  543. if (is_null($protocol))
  544. {
  545. list ($protocol) = explode('/', $_SERVER['SERVER_PROTOCOL']);
  546. }
  547. return strtolower($protocol);
  548. }
  549. /**
  550. * 设置 REQUEST_URI
  551. *
  552. * 这项修改不会影响 $_SERVER 中值,但是修改后 QContext::requestUri() 将返回新值。
  553. * 同时还影响 QContext::baseUri() 和 QContext::baseDir() 的返回结果。
  554. *
  555. * @param string $request_uri 新的 REQUEST_URI 值
  556. *
  557. * @return QContext 返回 QContext 对象本身,实现连贯接口
  558. */
  559. function changeRequestUri($request_uri)
  560. {
  561. self::$_request_uri = $request_uri;
  562. self::$_base_uri = self::$_base_dir = null;
  563. return $this;
  564. }
  565. /**
  566. * 确定请求的完整 URL
  567. *
  568. * 几个示例:
  569. *
  570. * <ul>
  571. * <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
  572. * <li>返回 /index.php?controller=posts&action=create</li>
  573. * </ul>
  574. * <ul>
  575. * <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
  576. * <li>返回 /news/index.php?controller=posts&action=create</li>
  577. * </ul>
  578. * <ul>
  579. * <li>请求 http://www.example.com/index.php/posts/create</li>
  580. * <li>返回 /index.php/posts/create</li>
  581. * </ul>
  582. * <ul>
  583. * <li>请求 http://www.example.com/news/show/id/1</li>
  584. * <li>返回 /news/show/id/1</li>
  585. * </ul>
  586. *
  587. * 此方法参考 Zend Framework 实现。
  588. *
  589. * @return string 请求的完整 URL
  590. */
  591. function requestUri()
  592. {
  593. if (self::$_request_uri) return self::$_request_uri;
  594. if (isset($_SERVER['HTTP_X_REWRITE_URL']))
  595. {
  596. $uri = $_SERVER['HTTP_X_REWRITE_URL'];
  597. }
  598. elseif (isset($_SERVER['REQUEST_URI']))
  599. {
  600. $uri = $_SERVER['REQUEST_URI'];
  601. }
  602. elseif (isset($_SERVER['ORIG_PATH_INFO']))
  603. {
  604. $uri = $_SERVER['ORIG_PATH_INFO'];
  605. if (! empty($_SERVER['QUERY_STRING']))
  606. {
  607. $uri .= '?' . $_SERVER['QUERY_STRING'];
  608. }
  609. }
  610. else
  611. {
  612. $uri = '';
  613. }
  614. self::$_request_uri = $uri;
  615. return $uri;
  616. }
  617. /**
  618. * 返回不包含任何查询参数的 URI(但包含脚本名称)
  619. *
  620. * 几个示例:
  621. *
  622. * <ul>
  623. * <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
  624. * <li>返回 /index.php</li>
  625. * </ul>
  626. * <ul>
  627. * <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
  628. * <li>返回 /news/index.php</li>
  629. * </ul>
  630. * <ul>
  631. * <li>请求 http://www.example.com/index.php/posts/create</li>
  632. * <li>返回 /index.php</li>
  633. * </ul>
  634. * <ul>
  635. * <li>请求 http://www.example.com/news/show/id/1</li>
  636. * <li>返回 /news/show/id/1</li>
  637. * <li>假设使用了 URL 重写,并且 index.php 位于根目录</li>
  638. * </ul>
  639. *
  640. * 此方法参考 Zend Framework 实现。
  641. *
  642. * @return string 请求 URL 中不包含查询参数的部分
  643. */
  644. function baseUri()
  645. {
  646. if (self::$_base_uri) return self::$_base_uri;
  647. $filename = basename($_SERVER['SCRIPT_FILENAME']);
  648. if (basename($_SERVER['SCRIPT_NAME']) === $filename)
  649. {
  650. $url = $_SERVER['SCRIPT_NAME'];
  651. }
  652. elseif (basename($_SERVER['PHP_SELF']) === $filename)
  653. {
  654. $url = $_SERVER['PHP_SELF'];
  655. }
  656. elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename)
  657. {
  658. $url = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
  659. }
  660. else
  661. {
  662. // Backtrack up the script_filename to find the portion matching
  663. // php_self
  664. $path = $_SERVER['PHP_SELF'];
  665. $segs = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
  666. $segs = array_reverse($segs);
  667. $index = 0;
  668. $last = count($segs);
  669. $url = '';
  670. do
  671. {
  672. $seg = $segs[$index];
  673. $url = '/' . $seg . $url;
  674. ++ $index;
  675. } while (($last > $index) && (false !== ($pos = strpos($path, $url))) && (0 != $pos));
  676. }
  677. // Does the baseUrl have anything in common with the request_uri?
  678. $request_uri = $this->requestUri();
  679. if (0 === strpos($request_uri, $url))
  680. {
  681. // full $url matches
  682. self::$_base_uri = $url;
  683. return self::$_base_uri;
  684. }
  685. if (0 === strpos($request_uri, dirname($url)))
  686. {
  687. // directory portion of $url matches
  688. self::$_base_uri = rtrim(dirname($url), '/') . '/';
  689. return self::$_base_uri;
  690. }
  691. if (! strpos($request_uri, basename($url)))
  692. {
  693. // no match whatsoever; set it blank
  694. return '';
  695. }
  696. // If using mod_rewrite or ISAPI_Rewrite strip the script filename
  697. // out of baseUrl. $pos !== 0 makes sure it is not matching a value
  698. // from PATH_INFO or QUERY_STRING
  699. if ((strlen($request_uri) >= strlen($url))
  700. && ((false !== ($pos = strpos($request_uri, $url)))
  701. && ($pos !== 0)))
  702. {
  703. $url = substr($request_uri, 0, $pos + strlen($url));
  704. }
  705. self::$_base_uri = rtrim($url, '/') . '/';
  706. return self::$_base_uri;
  707. }
  708. /**
  709. * 返回请求 URL 中的基础路径(不包含脚本名称)
  710. *
  711. * 几个示例:
  712. *
  713. * <ul>
  714. * <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
  715. * <li>返回 /</li>
  716. * </ul>
  717. * <ul>
  718. * <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
  719. * <li>返回 /news/</li>
  720. * </ul>
  721. * <ul>
  722. * <li>请求 http://www.example.com/index.php/posts/create</li>
  723. * <li>返回 /</li>
  724. * </ul>
  725. * <ul>
  726. * <li>请求 http://www.example.com/news/show/id/1</li>
  727. * <li>返回 /</li>
  728. * </ul>
  729. *
  730. * @return string 请求 URL 中的基础路径
  731. */
  732. function baseDir()
  733. {
  734. if (self::$_base_dir) return self::$_base_dir;
  735. $base_uri = $this->baseUri();
  736. if (substr($base_uri, - 1, 1) == '/')
  737. {
  738. $base_dir = $base_uri;
  739. }
  740. else
  741. {
  742. $base_dir = dirname($base_uri);
  743. }
  744. self::$_base_dir = rtrim($base_dir, '/\\') . '/';
  745. return self::$_base_dir;
  746. }
  747. /**
  748. * 返回服务器响应请求使用的端口
  749. *
  750. * 通常服务器使用 80 端口与客户端通信,该方法可以获得服务器所使用的端口号。
  751. *
  752. * @return string 服务器响应请求使用的端口
  753. */
  754. function serverPort()
  755. {
  756. static $server_port = null;
  757. if ($server_port) return $server_port;
  758. if (isset($_SERVER['SERVER_PORT']))
  759. {
  760. $server_port = intval($_SERVER['SERVER_PORT']);
  761. }
  762. else
  763. {
  764. $server_port = 80;
  765. }
  766. if (isset($_SERVER['HTTP_HOST']))
  767. {
  768. $arr = explode(':', $_SERVER['HTTP_HOST']);
  769. $count = count($arr);
  770. if ($count > 1)
  771. {
  772. $port = intval($arr[$count - 1]);
  773. if ($port != $server_port)
  774. {
  775. $server_port = $port;
  776. }
  777. }
  778. }
  779. return $server_port;
  780. }
  781. /**
  782. * 获得响应请求的脚本文件名
  783. *
  784. * @return string 响应请求的脚本文件名
  785. */
  786. function scriptName()
  787. {
  788. return basename($_SERVER['SCRIPT_FILENAME']);
  789. }
  790. /**
  791. * 返回 PATHINFO 信息
  792. *
  793. * <ul>
  794. * <li>请求 http://www.example.com/index.php?controller=posts&action=create</li>
  795. * <li>返回 /</li>
  796. * </ul>
  797. * <ul>
  798. * <li>请求 http://www.example.com/news/index.php?controller=posts&action=create</li>
  799. * <li>返回 /</li>
  800. * </ul>
  801. * <ul>
  802. * <li>请求 http://www.example.com/index.php/posts/create</li>
  803. * <li>返回 /</li>
  804. * </ul>
  805. * <ul>
  806. * <li>请求 http://www.example.com/news/show/id/1</li>
  807. * <li>返回 /news/show/id/1</li>
  808. * <li>假设使用了 URL 重写,并且 index.php 位于根目录</li>
  809. * </ul>
  810. *
  811. * 此方法参考 Zend Framework 实现。
  812. *
  813. * @return string
  814. */
  815. function pathinfo()
  816. {
  817. if (!empty($_SERVER['PATH_INFO'])) return $_SERVER['PATH_INFO'];
  818. $base_url = $this->baseUri();
  819. if (null === ($request_uri = $this->requestUri())) return '';
  820. // Remove the query string from REQUEST_URI
  821. if (($pos = strpos($request_uri, '?')))
  822. {
  823. $request_uri = substr($request_uri, 0, $pos);
  824. }
  825. if ((null !== $base_url) && (false === ($pathinfo = substr($request_uri, strlen($base_url)))))
  826. {
  827. // If substr() returns false then PATH_INFO is set to an empty string
  828. $pathinfo = '';
  829. }
  830. elseif (null === $base_url)
  831. {
  832. $pathinfo = $request_uri;
  833. }
  834. return $pathinfo;
  835. }
  836. /**
  837. * 返回请求使用的方法
  838. *
  839. * @return string
  840. */
  841. function requestMethod()
  842. {
  843. return $_SERVER['REQUEST_METHOD'];
  844. }
  845. /**
  846. * 是否是 GET 请求
  847. *
  848. * @return boolean
  849. */
  850. function isGET()
  851. {
  852. return $this->requestMethod() == 'GET';
  853. }
  854. /**
  855. * 是否是 POST 请求
  856. *
  857. * @return boolean
  858. */
  859. function isPOST()
  860. {
  861. return $this->requestMethod() == 'POST';
  862. }
  863. /**
  864. * 是否是 PUT 请求
  865. *
  866. * @return boolean
  867. */
  868. function isPUT()
  869. {
  870. return $this->requestMethod() == 'PUT';
  871. }
  872. /**
  873. * 是否是 DELETE 请求
  874. *
  875. * @return boolean
  876. */
  877. function isDELETE()
  878. {
  879. return $this->requestMethod() == 'DELETE';
  880. }
  881. /**
  882. * 是否是 HEAD 请求
  883. *
  884. * @return boolean
  885. */
  886. function isHEAD()
  887. {
  888. return $this->requestMethod() == 'HEAD';
  889. }
  890. /**
  891. * 是否是 OPTIONS 请求
  892. *
  893. * @return boolean
  894. */
  895. function isOPTIONS()
  896. {
  897. return $this->requestMethod() == 'OPTIONS';
  898. }
  899. /**
  900. * 判断 HTTP 请求是否是通过 XMLHttp 发起的
  901. *
  902. * @return boolean
  903. */
  904. function isAJAX()
  905. {
  906. return strtolower($this->header('X_REQUESTED_WITH')) == 'xmlhttprequest';
  907. }
  908. /**
  909. * 判断 HTTP 请求是否是通过 Flash 发起的
  910. *
  911. * @return boolean
  912. */
  913. function isFlash()
  914. {
  915. $agent = strtolower($this->header('USER_AGENT'));
  916. return strpos($agent, 'shockwave flash') !== false || strpos($agent, 'adobeair') !== false;
  917. }
  918. /**
  919. * 返回请求的原始内容
  920. *
  921. * @return string
  922. */
  923. function requestRawBody()
  924. {
  925. $body = file_get_contents('php://input');
  926. return (strlen(trim($body)) > 0) ? $body : false;
  927. }
  928. /**
  929. * 返回 HTTP 请求头中的指定信息,如果没有指定参数则返回 false
  930. *
  931. * @param string $header 要查询的请求头参数
  932. *
  933. * @return string 参数值
  934. */
  935. function header($header)
  936. {
  937. $temp = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
  938. if (!empty($_SERVER[$temp])) return $_SERVER[$temp];
  939. if (function_exists('apache_request_headers'))
  940. {
  941. $headers = apache_request_headers();
  942. if (!empty($headers[$header])) return $headers[$header];
  943. }
  944. return false;
  945. }
  946. /**
  947. * 返回当前请求的参照 URL
  948. *
  949. * @return string 当前请求的参照 URL
  950. */
  951. function referer()
  952. {
  953. return $this->header('REFERER');
  954. }
  955. /**
  956. * 返回当前使用的路由服务对象
  957. *
  958. * 如果没有启动路由,则返回 null。
  959. *
  960. * @return QRouter 当前使用的路由服务对象
  961. */
  962. function router()
  963. {
  964. return $this->_router;
  965. }
  966. /**
  967. * 构造 url
  968. *
  969. * 用法:
  970. *
  971. * @code php
  972. * url(UDI, [附加参数数组], [路由名])
  973. * @endcode
  974. *
  975. * UDI 是统一目的地标识符(Uniform Destination Identifier)的缩写。
  976. * UDI 由控制器、动作、以及模块名组成,采用如下的格式:
  977. *
  978. * @code php
  979. * module::controller/action
  980. * @endcode
  981. *
  982. * UDI 字符串中,每一个部分都是可选的。
  983. * 如果没有提供控制器和动作名,则使用当前的控制器和默认动作名(index)代替。
  984. * 同样,如果未提供模块名,均使用当前值代替。
  985. *
  986. * UDI 字符串写法示例:
  987. *
  988. * @code php
  989. * 'controller'
  990. * 'controller/action'
  991. * '/action'
  992. * 'controller'
  993. * 'controller/action'
  994. * 'module::controller'
  995. * 'module::controller/action'
  996. * 'module::controller'
  997. * 'module::controller/action'
  998. * @endcode
  999. *
  1000. * 示例:
  1001. * @code php
  1002. * url('admin::posts/edit', array('id' => $post->id()));
  1003. * @endcode
  1004. *
  1005. * $params 参数除了采用数组,还可以是以“/”符号分割的字符串:
  1006. *
  1007. * @code php
  1008. * url('posts/index', 'page/3');
  1009. * url('users/show', 'id/5/profile/yes');
  1010. * @endcode
  1011. *
  1012. * 在使用 PATHINFO 和 URL 重写时,可以使用通过制定路由名来强制要求 QeePHP
  1013. * 采用指定的路由规则来生成 URL。强制指定路由规则可以加快 URL 的生成,
  1014. * 但在路由规则名称发生变化时,需要修改生成 URL 的代码。
  1015. *
  1016. * $opts 参数用于控制如何生成 URL。可用的选项有:
  1017. *
  1018. * - base_uri: 指定 URL 前部要添加的路径(可以包括协议、域名和端口,以及路径)
  1019. * - script: 指定 URL 前部要使用的脚本名
  1020. * - mode: 指定 URL 生成模式,可以是 standard、pathinfo 和 rewrite
  1021. *
  1022. * @param string $udi UDI 字符串
  1023. * @param array|string $params 附加参数数组
  1024. * @param string $route_name 路由名
  1025. * @param array $opts 控制如何生成 URL 的选项
  1026. *
  1027. * @return string 生成的 URL 地址
  1028. */
  1029. function url($udi, $params = null, $route_name = null, array $opts = null)
  1030. {
  1031. static $base_uri;
  1032. if (is_null($base_uri))
  1033. {
  1034. if(strlen(Q::ini('base_uri')) > 0)
  1035. {
  1036. $base_uri = Q::ini('base_uri');
  1037. }
  1038. else
  1039. {
  1040. $base_uri = '/' . trim($this->baseDir(), '/');
  1041. if ($base_uri != '/')
  1042. {
  1043. $base_uri .= '/';
  1044. }
  1045. }
  1046. }
  1047. $udi = $this->normalizeUDI($udi);
  1048. if (! is_array($params))
  1049. {
  1050. $arr = Q::normalize($params, '/');
  1051. $params = array();
  1052. while ($key = array_shift($arr))
  1053. {
  1054. $value = array_shift($arr);
  1055. $params[$key] = $value;
  1056. }
  1057. }
  1058. $params = array_filter($params, 'strlen');
  1059. // 处理 $opts
  1060. if (is_array($opts))
  1061. {
  1062. $mode = !empty($opts['mode']) ? $opts['mode'] : self::$_url_mode;
  1063. $script = !empty($opts['script'])
  1064. ? $opts['script']
  1065. : $this->scriptName();
  1066. $url = strlen($opts['base_uri']) > 0
  1067. ? rtrim($opts['base_uri'], '/') . '/'
  1068. : $base_uri;
  1069. }
  1070. else
  1071. {
  1072. $mode = self::$_url_mode;
  1073. $url = $base_uri;
  1074. $script = $this->scriptName();
  1075. }
  1076. if (!is_null($this->_router) && $mode != self::URL_MODE_STANDARD)
  1077. {
  1078. // 使用路由生成 URL
  1079. $params = array_merge($params, $udi);
  1080. $path = $this->_router->url($params, $route_name);
  1081. if (self::$_url_mode == self::URL_MODE_PATHINFO && $path != '/')
  1082. {
  1083. $url .= $this->scriptName();
  1084. }
  1085. else
  1086. {
  1087. $url = rtrim($url, '/');
  1088. }
  1089. $url .= $path;
  1090. }
  1091. else
  1092. {
  1093. foreach (self::$_udi_defaults as $key => $value)
  1094. {
  1095. if ($udi[$key] == $value) unset($udi[$key]);
  1096. unset($params[$key]);
  1097. }
  1098. $params = array_filter(array_merge($udi, $params), 'strlen');
  1099. $url .= $script;
  1100. if (!empty($params))
  1101. {
  1102. $url .= '?' . http_build_query($params, '', '&');
  1103. }
  1104. }
  1105. return $url;
  1106. }
  1107. /**
  1108. * 返回 UDI 的字符串表现形式
  1109. *
  1110. * @param array $udi 要处理的 UDI
  1111. *
  1112. * @return string
  1113. */
  1114. function UDIString(array $udi)
  1115. {
  1116. return "{$udi[self::UDI_MODULE]}::{$udi[self::UDI_CONTROLLER]}/{$udi[self::UDI_ACTION]}";
  1117. }
  1118. /**
  1119. * 返回规范化以后的 UDI 数组
  1120. *
  1121. * @code php
  1122. * $udi = array(
  1123. * QContext::UDI_CONTROLLER => '',
  1124. * QContext::UDI_ACTION => '',
  1125. * QContext::UDI_MODULE => '',
  1126. * );
  1127. *
  1128. * // 输出
  1129. * // array(
  1130. * // controller: default
  1131. * // action: index
  1132. * // ns: default
  1133. * // module: default
  1134. * // )
  1135. * dump($context->normalizeUDI($udi));
  1136. *
  1137. * $udi = 'admin::posts/edit';
  1138. * // 输出
  1139. * // array(
  1140. * // controller: posts
  1141. * // action: edit
  1142. * // ns: admin
  1143. * // module: default
  1144. * // )
  1145. * dump($context->normalizeUDI($udi));
  1146. * @endcode
  1147. *
  1148. * 如果要返回字符串形式的 UDI,设置 $return_array 参数为 false。
  1149. *
  1150. * @param string|array $udi 要处理的 UDI
  1151. * @param boolean $return_array 是否返回数组形式的 UDI
  1152. *
  1153. * @return array 处理后的 UDI
  1154. */
  1155. function normalizeUDI($udi, $return_array = true)
  1156. {
  1157. if (! is_array($udi))
  1158. {
  1159. // 特殊处理 "", ".", "/" UDI解析
  1160. // "", "." 返回当前动作
  1161. // "/" 返回当前控制器默认动作
  1162. if(! is_string($udi) || $udi == '' || $udi == '.')
  1163. {
  1164. $module_name = $this->module_name;
  1165. $controller = $this->controller;
  1166. $action = $this->action;
  1167. }
  1168. elseif($udi == '/')
  1169. {
  1170. $module_name = $this->module_name;
  1171. $controller = $this->controller;
  1172. $action = self::$_udi_defaults[self::UDI_ACTION];
  1173. }
  1174. else
  1175. {
  1176. if (strpos($udi, '::') !== false)
  1177. {
  1178. $arr = explode('::', $udi);
  1179. $module_name = array_shift($arr);
  1180. $udi = array_shift($arr);
  1181. }
  1182. else
  1183. {
  1184. $module_name = $this->module_name;
  1185. }
  1186. $arr = explode('/', $udi);
  1187. $controller = array_shift($arr);
  1188. $action = array_shift($arr);
  1189. }
  1190. $udi = array(
  1191. self::UDI_MODULE => $module_name,
  1192. self::UDI_CONTROLLER => $controller,
  1193. self::UDI_ACTION => $action,
  1194. );
  1195. }
  1196. if (empty($udi[self::UDI_MODULE]))
  1197. {
  1198. $udi[self::UDI_MODULE] = $this->module_name;
  1199. }
  1200. if (empty($udi[self::UDI_CONTROLLER]))
  1201. {
  1202. $udi[self::UDI_CONTROLLER] = $this->controller_name;
  1203. }
  1204. if (empty($udi[self::UDI_ACTION]))
  1205. {
  1206. $udi[self::UDI_ACTION] = self::UDI_DEFAULT_ACTION;
  1207. }
  1208. foreach (self::$_udi_defaults as $key => $value)
  1209. {
  1210. if (empty($udi[$key]))
  1211. {
  1212. $udi[$key] = $value;
  1213. }
  1214. else
  1215. {
  1216. $udi[$key] = preg_replace('/[^a-z0-9]+/', '', strtolower($udi[$key]));
  1217. }
  1218. }
  1219. if (!$return_array)
  1220. {
  1221. return "{$udi[self::UDI_MODULE]}::{$udi[self::UDI_CONTROLLER]}/{$udi[self::UDI_ACTION]}";
  1222. }
  1223. else
  1224. {
  1225. return $udi;
  1226. }
  1227. }
  1228. /**
  1229. * 返回当前请求对应的 UDI
  1230. *
  1231. * 将当前请求中包含的控制器、动作、名字空间和模块名提取出来,构造为一个 UDI。
  1232. *
  1233. * @code php
  1234. * dump($context->requestUDI());
  1235. * @endcode
  1236. *
  1237. * @param boolean $return_array 是否返回数组形式的 UDI
  1238. *
  1239. * @return string|array 对应当前请求的 UDI
  1240. */
  1241. function requestUDI($return_array = true)
  1242. {
  1243. return $this->normalizeUDI("/{$this->action_name}", $return_array);
  1244. }
  1245. /**
  1246. * 将 QContext 对象保存的请求参数设置为 UDI 指定的值
  1247. *
  1248. * @code php
  1249. * $context->changeRequestUDI('posts/edit');
  1250. * // 将输出 posts
  1251. * echo $context->controller_name;
  1252. * @endcode
  1253. *
  1254. * @param array|string $udi 要设置的 UDI
  1255. *
  1256. * @return QContext 返回 QContext 对象本身,实现连贯接口
  1257. */
  1258. function changeRequestUDI($udi)
  1259. {
  1260. $udi = $this->normalizeUDI($udi);
  1261. $this->controller_name = $udi[self::UDI_CONTROLLER];
  1262. $this->action_name = $udi[self::UDI_ACTION];
  1263. $this->module_name = $udi[self::UDI_MODULE];
  1264. #dump($this);
  1265. return $this;
  1266. }
  1267. }