PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/server/vpn.51sync.com/library/q.php

https://github.com/qibinghua/vpn.51sync
PHP | 1141 lines | 866 code | 28 blank | 247 comment | 25 complexity | 314746e948b11d6ab92959f849ff4dc0 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. // $Id: q.php 2684 2010-07-18 17:30:05Z dualface $
  3. /**
  4. * 定义 QeePHP 核心类,并初始化框架基本设置
  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: q.php 2684 2010-07-18 17:30:05Z dualface $
  10. * @package core
  11. */
  12. header('Content-Type: text/html; charset= utf-8');
  13. /**
  14. * 定义是否运行在 PHP 5.3 中
  15. */
  16. define('PHP53', phpversion() >= '5.3');
  17. /**
  18. * QeePHP 框架基本库所在路径
  19. */
  20. define('Q_DIR', dirname(__FILE__));
  21. /**
  22. * DIRECTORY_SEPARATOR 的简写
  23. */
  24. define('DS', DIRECTORY_SEPARATOR);
  25. /**
  26. * CURRENT_TIMESTAMP 定义为当前时间,减少框架调用 time() 的次数
  27. */
  28. define('CURRENT_TIMESTAMP', time());
  29. global $G_CLASS_FILES;
  30. if (empty($G_CLASS_FILES))
  31. {
  32. require Q_DIR . '/_config/qeephp_class_files.php';
  33. }
  34. /**
  35. * 类 Q 是 QeePHP 框架的核心类,提供了框架运行所需的基本服务
  36. *
  37. * 类 Q 提供 QeePHP 框架的基本服务,包括:
  38. *
  39. * - 设置的读取和修改;
  40. * - 类定义文件的搜索和载入;
  41. * - 对象的单子模式实现,以及对象注册和检索;
  42. * - 统一缓存接口;
  43. * - 基本工具方法。
  44. *
  45. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  46. * @version $Id: q.php 2684 2010-07-18 17:30:05Z dualface $
  47. * @package core
  48. */
  49. class Q
  50. {
  51. /**
  52. * 指示应用程序运行模式
  53. */
  54. // 开发运行模式
  55. const RUN_MODE_DEVEL = 'devel';
  56. // 生产部署模式
  57. const RUN_MODE_DEPLOY = 'deploy';
  58. // 测试模式
  59. const RUN_MODE_TEST = 'test';
  60. /**
  61. * 对象注册表
  62. *
  63. * @var array
  64. */
  65. private static $_objects = array();
  66. /**
  67. * 类搜索路径
  68. *
  69. * @var array
  70. */
  71. private static $_class_path = array();
  72. /**
  73. * 类搜索路径的选项
  74. *
  75. * @var array
  76. */
  77. private static $_class_path_options = array();
  78. /**
  79. * 应用程序设置
  80. *
  81. * @var array
  82. */
  83. private static $_ini = array();
  84. /**
  85. * 返回 QeePHP 版本号
  86. *
  87. * @return string QeePHP 版本号
  88. */
  89. static function version()
  90. {
  91. return '2.1';
  92. }
  93. /**
  94. * 获取指定的设置内容
  95. *
  96. * $option 参数指定要获取的设置名。
  97. * 如果设置中找不到指定的选项,则返回由 $default 参数指定的值。
  98. *
  99. * @code php
  100. * $option_value = Q::ini('my_option');
  101. * @endcode
  102. *
  103. * 对于层次化的设置信息,可以通过在 $option 中使用“/”符号来指定。
  104. *
  105. * 例如有一个名为 option_group 的设置项,其中包含三个子项目。
  106. * 现在要查询其中的 my_option 设置项的内容。
  107. *
  108. * @code php
  109. * // +--- option_group
  110. * // +-- my_option = this is my_option
  111. * // +-- my_option2 = this is my_option2
  112. * // \-- my_option3 = this is my_option3
  113. *
  114. * // 查询 option_group 设置组里面的 my_option 项
  115. * // 将会显示 this is my_option
  116. * echo Q::ini('option_group/my_option');
  117. * @endcode
  118. *
  119. * 要读取更深层次的设置项,可以使用更多的“/”符号,但太多层次会导致读取速度变慢。
  120. *
  121. * 如果要获得所有设置项的内容,将 $option 参数指定为 '/' 即可:
  122. *
  123. * @code php
  124. * // 获取所有设置项的内容
  125. * $all = Q::ini('/');
  126. * @endcode
  127. *
  128. * @param string $option 要获取设置项的名称
  129. * @param mixed $default 当设置不存在时要返回的设置默认值
  130. *
  131. * @return mixed 返回设置项的值
  132. */
  133. static function ini($option, $default = null)
  134. {
  135. if ($option == '/') return self::$_ini;
  136. if (strpos($option, '/') === false)
  137. {
  138. return array_key_exists($option, self::$_ini)
  139. ? self::$_ini[$option]
  140. : $default;
  141. }
  142. $parts = explode('/', $option);
  143. $pos =& self::$_ini;
  144. foreach ($parts as $part)
  145. {
  146. if (!isset($pos[$part])) return $default;
  147. $pos =& $pos[$part];
  148. }
  149. return $pos;
  150. }
  151. /**
  152. * 修改指定设置的内容
  153. *
  154. * 当 $option 参数是字符串时,$option 指定了要修改的设置项。
  155. * $data 则是要为该设置项指定的新数据。
  156. *
  157. * @code php
  158. * // 修改一个设置项
  159. * Q::changeIni('option_group/my_option2', 'new value');
  160. * @endcode
  161. *
  162. * 如果 $option 是一个数组,则假定要修改多个设置项。
  163. * 那么 $option 则是一个由设置项名称和设置值组成的名值对,或者是一个嵌套数组。
  164. *
  165. * @code php
  166. * // 假设已有的设置为
  167. * // +--- option_1 = old value
  168. * // +--- option_group
  169. * // +-- option1 = old value
  170. * // +-- option2 = old value
  171. * // \-- option3 = old value
  172. *
  173. * // 修改多个设置项
  174. * $arr = array(
  175. * 'option_1' => 'value 1',
  176. * 'option_2' => 'value 2',
  177. * 'option_group/option2' => 'new value',
  178. * );
  179. * Q::changeIni($arr);
  180. *
  181. * // 修改后
  182. * // +--- option_1 = value 1
  183. * // +--- option_2 = value 2
  184. * // +--- option_group
  185. * // +-- option1 = old value
  186. * // +-- option2 = new value
  187. * // \-- option3 = old value
  188. * @endcode
  189. *
  190. * 上述代码展示了 Q::changeIni() 的一个重要特性:保持已有设置的层次结构。
  191. *
  192. * 因此如果要完全替换某个设置项和其子项目,应该使用 Q::replaceIni() 方法。
  193. *
  194. * @param string|array $option 要修改的设置项名称,或包含多个设置项目的数组
  195. * @param mixed $data 指定设置项的新值
  196. */
  197. static function changeIni($option, $data = null)
  198. {
  199. if (is_array($option))
  200. {
  201. foreach ($option as $key => $value)
  202. {
  203. self::changeIni($key, $value);
  204. }
  205. return;
  206. }
  207. if (!is_array($data))
  208. {
  209. if (strpos($option, '/') === false)
  210. {
  211. self::$_ini[$option] = $data;
  212. return;
  213. }
  214. $parts = explode('/', $option);
  215. $max = count($parts) - 1;
  216. $pos =& self::$_ini;
  217. for ($i = 0; $i <= $max; $i ++)
  218. {
  219. $part = $parts[$i];
  220. if ($i < $max)
  221. {
  222. if (!isset($pos[$part]))
  223. {
  224. $pos[$part] = array();
  225. }
  226. $pos =& $pos[$part];
  227. }
  228. else
  229. {
  230. $pos[$part] = $data;
  231. }
  232. }
  233. }
  234. else
  235. {
  236. foreach ($data as $key => $value)
  237. {
  238. self::changeIni($option . '/' . $key, $value);
  239. }
  240. }
  241. }
  242. /**
  243. * 替换已有的设置值
  244. *
  245. * Q::replaceIni() 表面上看和 Q::changeIni() 类似。
  246. * 但是 Q::replaceIni() 不会保持已有设置的层次结构,
  247. * 而是直接替换到指定的设置项及其子项目。
  248. *
  249. * @code php
  250. * // 假设已有的设置为
  251. * // +--- option_1 = old value
  252. * // +--- option_group
  253. * // +-- option1 = old value
  254. * // +-- option2 = old value
  255. * // \-- option3 = old value
  256. *
  257. * // 替换多个设置项
  258. * $arr = array(
  259. * 'option_1' => 'value 1',
  260. * 'option_2' => 'value 2',
  261. * 'option_group/option2' => 'new value',
  262. * );
  263. * Q::replaceIni($arr);
  264. *
  265. * // 修改后
  266. * // +--- option_1 = value 1
  267. * // +--- option_2 = value 2
  268. * // +--- option_group
  269. * // +-- option2 = new value
  270. * @endcode
  271. *
  272. * 从上述代码的执行结果可以看出 Q::replaceIni() 和 Q::changeIni() 的重要区别。
  273. *
  274. * 不过由于 Q::replaceIni() 速度比 Q::changeIni() 快很多,
  275. * 因此应该尽量使用 Q::replaceIni() 来代替 Q::changeIni()。
  276. *
  277. * @param string|array $option 要修改的设置项名称,或包含多个设置项目的数组
  278. * @param mixed $data 指定设置项的新值
  279. */
  280. static function replaceIni($option, $data = null)
  281. {
  282. if (is_array($option))
  283. {
  284. self::$_ini = array_merge(self::$_ini, $option);
  285. }
  286. else
  287. {
  288. self::$_ini[$option] = $data;
  289. }
  290. }
  291. /**
  292. * 删除指定的设置
  293. *
  294. * Q::cleanIni() 可以删除指定的设置项目及其子项目。
  295. *
  296. * @param mixed $option 要删除的设置项名称
  297. */
  298. static function cleanIni($option)
  299. {
  300. if (strpos($option, '/') === false)
  301. {
  302. unset(self::$_ini[$option]);
  303. }
  304. else
  305. {
  306. $parts = explode('/', $option);
  307. $max = count($parts) - 1;
  308. $pos =& self::$_ini;
  309. for ($i = 0; $i <= $max; $i ++)
  310. {
  311. $part = $parts[$i];
  312. if ($i < $max)
  313. {
  314. if (!isset($pos[$part]))
  315. {
  316. $pos[$part] = array();
  317. }
  318. $pos =& $pos[$part];
  319. }
  320. else
  321. {
  322. unset($pos[$part]);
  323. }
  324. }
  325. }
  326. }
  327. /**
  328. * 载入指定类的定义文件,如果载入失败抛出异常
  329. *
  330. * @code php
  331. * Q::loadClass('Table_Posts');
  332. * @endcode
  333. *
  334. * $dirs 参数可以是一个以 PATH_SEPARATOR 常量分隔的字符串,
  335. * 也可以是一个包含多个目录名的数组。
  336. *
  337. * @code php
  338. * Q::loadClass('Table_Posts', array('/www/mysite/app', '/www/mysite/lib'));
  339. * @endcode
  340. *
  341. * @param string $class_name 要载入的类
  342. * @param string|array $dirs 指定载入类的搜索路径
  343. *
  344. * @return string|boolean 成功返回类名,失败返回 false
  345. */
  346. static function loadClass($class_name, $dirs = null, $throw = true)
  347. {
  348. if (class_exists($class_name, false) || interface_exists($class_name, false))
  349. {
  350. return $class_name;
  351. }
  352. global $G_CLASS_FILES;
  353. $class_name_l = strtolower($class_name);
  354. if (isset($G_CLASS_FILES[$class_name_l]))
  355. {
  356. require Q_DIR . DS . $G_CLASS_FILES[$class_name_l];
  357. return $class_name_l;
  358. }
  359. $filename = str_replace('_', DS, $class_name);
  360. if ($filename != $class_name)
  361. {
  362. $dirname = dirname($filename);
  363. if (!empty($dirs))
  364. {
  365. if (!is_array($dirs))
  366. {
  367. $dirs = explode(PATH_SEPARATOR, $dirs);
  368. }
  369. }
  370. else
  371. {
  372. $dirs = self::$_class_path;
  373. }
  374. $filename = basename($filename) . '.php';
  375. return self::loadClassFile($filename, $dirs, $class_name, $dirname, $throw);
  376. }
  377. else
  378. {
  379. return self::loadClassFile("{$filename}.php", self::$_class_path, $class_name, '', $throw);
  380. }
  381. }
  382. /**
  383. * 添加一个类搜索路径
  384. *
  385. * 如果要使用 Q::loadClass() 载入非 QeePHP 的类,需要通过 Q::import() 添加类类搜索路径。
  386. *
  387. * 要注意,Q::import() 添加的路径和类名称有关系。
  388. *
  389. * 例如类的名称为 Vendor_Smarty_Adapter,那么该类的定义文件存储结构就是 vendor/smarty/adapter.php。
  390. * 因此在用 Q::import() 添加 Vendor_Smarty_Adapter 类的搜索路径时,
  391. * 只能添加 vendor/smarty/adapter.php 的父目录。
  392. *
  393. * @code php
  394. * Q::import('/www/app');
  395. * Q::loadClass('Vendor_Smarty_Adapter');
  396. * // 实际载入的文件是 /www/app/vendor/smarty/adapter.php
  397. * @endcode
  398. *
  399. * 由于 QeePHP 的规范是文件名全小写,因此要载入文件名存在大小写区分的第三方库时,
  400. * 应该指定 import() 方法的第二个参数。
  401. *
  402. * @code php
  403. * Q::import('/www/app/vendor', true);
  404. * Q::loadClass('Zend_Mail');
  405. * // 实际载入的文件是 /www/app/vendor/Zend/Mail.php
  406. * @endcode
  407. *
  408. * @param string $dir 要添加的搜索路径
  409. * @param boolean $case_sensitive 在该路径中查找类文件时是否区分文件名大小写
  410. */
  411. static function import($dir, $case_sensitive = false)
  412. {
  413. $real_dir = realpath($dir);
  414. if ($real_dir)
  415. {
  416. $dir = rtrim($real_dir, '/\\');
  417. if (!isset(self::$_class_path[$dir]))
  418. {
  419. self::$_class_path[$dir] = $dir;
  420. self::$_class_path_options[$dir] = $case_sensitive;
  421. }
  422. }
  423. }
  424. /**
  425. * 载入特定文件,并检查是否包含指定类的定义
  426. *
  427. * 该方法从 $dirs 参数提供的目录中查找并载入 $filename 参数指定的文件。
  428. * 然后检查该文件是否定义了 $class_name 参数指定的类。
  429. *
  430. * 如果没有找到指定类,则抛出异常。
  431. *
  432. * @code php
  433. * Q::loadClassFile('Smarty.class.php', $dirs, 'Smarty');
  434. * @endcode
  435. *
  436. * @param string $filename 要载入文件的文件名(含扩展名)
  437. * @param string|array $dirs 文件的搜索路径
  438. * @param string $class_name 要检查的类
  439. * @param string $dirname 是否在查找文件时添加目录前缀
  440. * @param string $throw 是否在找不到类时抛出异常
  441. */
  442. static function loadClassFile($filename, $dirs, $class_name, $dirname = '', $throw = true)
  443. {
  444. if (!is_array($dirs))
  445. {
  446. $dirs = explode(PATH_SEPARATOR, $dirs);
  447. }
  448. if ($dirname)
  449. {
  450. $filename = rtrim($dirname, '/\\') . DS . $filename;
  451. }
  452. $filename_l = strtolower($filename);
  453. foreach ($dirs as $dir)
  454. {
  455. if (isset(self::$_class_path[$dir]))
  456. {
  457. $path = $dir . DS . (self::$_class_path_options[$dir] ? $filename : $filename_l);
  458. }
  459. else
  460. {
  461. $path = rtrim($dir, '/\\') . DS . $filename;
  462. }
  463. if (is_file($path))
  464. {
  465. require $path;
  466. break;
  467. }
  468. }
  469. // 载入文件后判断指定的类或接口是否已经定义
  470. if (!class_exists($class_name, false) && ! interface_exists($class_name, false))
  471. {
  472. if ($throw)
  473. {
  474. throw new Q_ClassNotDefinedException($class_name, $path);
  475. }
  476. return false;
  477. }
  478. return $class_name;
  479. }
  480. /**
  481. * 载入指定的文件
  482. *
  483. * 该方法从 $dirs 参数提供的目录中查找并载入 $filename 参数指定的文件。
  484. * 如果文件不存在,则根据 $throw 参数决定是否抛出异常。
  485. *
  486. * 与 PHP 内置的 require 和 include 相比,Q::loadFile() 会多处下列特征:
  487. *
  488. * <ul>
  489. * <li>检查文件名是否包含不安全字符;</li>
  490. * <li>检查文件是否可读;</li>
  491. * <li>文件无法读取时将抛出异常。</li>
  492. * </ul>
  493. *
  494. * @code php
  495. * Q::loadFile('my_file.php', $dirs);
  496. * @endcode
  497. *
  498. * @param string $filename 要载入文件的文件名(含扩展名)
  499. * @param array $dirs 文件的搜索路径
  500. * @param boolean $throw 在找不到文件时是否抛出异常
  501. *
  502. * @return mixed
  503. */
  504. static function loadFile($filename, $dirs = null, $throw = true)
  505. {
  506. if (preg_match('/[^a-z0-9\-_.]/i', $filename))
  507. {
  508. throw new Q_IllegalFilenameException($filename);
  509. }
  510. if (is_null($dirs))
  511. {
  512. $dirs = array();
  513. }
  514. elseif (is_string($dirs))
  515. {
  516. $dirs = explode(PATH_SEPARATOR, $dirs);
  517. }
  518. foreach ($dirs as $dir)
  519. {
  520. $path = rtrim($dir, '\\/') . DS . $filename;
  521. if (is_file($path)) return include $path;
  522. }
  523. if ($throw) throw new Q_FileNotFoundException($filename);
  524. return false;
  525. }
  526. /**
  527. * 返回指定对象的唯一实例
  528. *
  529. * Q::singleton() 完成下列工作:
  530. *
  531. * <ul>
  532. * <li>在对象注册表中查找指定类名称的对象实例是否存在;</li>
  533. * <li>如果存在,则返回该对象实例;</li>
  534. * <li>如果不存在,则载入类定义文件,并构造一个对象实例;</li>
  535. * <li>将新构造的对象以类名称作为对象名登记到对象注册表;</li>
  536. * <li>返回新构造的对象实例。</li>
  537. * </ul>
  538. *
  539. * 使用 Q::singleton() 的好处在于多次使用同一个对象时不需要反复构造对象。
  540. *
  541. * @code php
  542. * // 在位置 A 处使用对象 My_Object
  543. * $obj = Q::singleton('My_Object');
  544. * ...
  545. * ...
  546. * // 在位置 B 处使用对象 My_Object
  547. * $obj2 = Q::singleton('My_Object');
  548. * // $obj 和 $obj2 都是指向同一个对象实例,避免了多次构造,提高了性能
  549. * @endcode
  550. *
  551. * @param string $class_name 要获取的对象的类名字
  552. *
  553. * @return object 返回对象实例
  554. */
  555. static function singleton($class_name)
  556. {
  557. $key = strtolower($class_name);
  558. if (isset(self::$_objects[$key]))
  559. {
  560. return self::$_objects[$key];
  561. }
  562. self::loadClass($class_name);
  563. return self::register(new $class_name(), $class_name);
  564. }
  565. /**
  566. * 以特定名字在对象注册表中登记一个对象
  567. *
  568. * 开发者可以将一个对象登记到对象注册表中,以便在应用程序其他位置使用 Q::registry() 来查询该对象。
  569. * 登记时,如果没有为对象指定一个名字,则以对象的类名称作为登记名。
  570. *
  571. * @code php
  572. * // 注册一个对象
  573. * Q::register(new MyObject());
  574. * .....
  575. * // 稍后取出对象
  576. * $obj = Q::regitry('MyObject');
  577. * @endcode
  578. *
  579. * 当 $persistent 参数为 true 时,对象将被放入持久存储区。
  580. * 在下一次执行脚本时,可以通过 Q::registry() 取出放入持久存储区的对象,并且无需重新构造对象。
  581. *
  582. * 利用这个特性,开发者可以将一些需要大量构造时间的对象放入持久存储区,
  583. * 从而避免每一次执行脚本都去做对象构造操作。
  584. *
  585. * 使用哪一种持久化存储区来保存对象,由设置 object_persistent_provier 决定。
  586. * 该设置指定一个提供持久化服务的对象名。
  587. *
  588. * @code php
  589. * if (!Q::isRegistered('MyObject'))
  590. * {
  591. * Q::registry(new MyObject(), 'MyObject', true);
  592. * }
  593. * $app = Q::registry('MyObject');
  594. * @endcode
  595. *
  596. * @param object $obj 要登记的对象
  597. * @param string $name 用什么名字登记
  598. * @param boolean $persistent 是否将对象放入持久化存储区
  599. *
  600. * @return object
  601. */
  602. static function register($obj, $name = null, $persistent = false)
  603. {
  604. if (!is_object($obj))
  605. {
  606. // LC_MSG: Type mismatch. $obj expected is object, actual is "%s".
  607. throw new QException(__('Type mismatch. $obj expected is object, actual is "%s".',
  608. gettype($obj)));
  609. }
  610. // TODO: 实现对 $persistent 参数的支持
  611. if (is_null($name))
  612. {
  613. $name = get_class($obj);
  614. }
  615. $name = strtolower($name);
  616. self::$_objects[$name] = $obj;
  617. return $obj;
  618. }
  619. /**
  620. * 查找指定名字的对象实例,如果指定名字的对象不存在则抛出异常
  621. *
  622. * @code php
  623. * // 注册一个对象
  624. * Q::register(new MyObject(), 'obj1');
  625. * .....
  626. * // 稍后取出对象
  627. * $obj = Q::registry('obj1');
  628. * @endcode
  629. *
  630. * @param string $name 要查找对象的名字
  631. *
  632. * @return object 查找到的对象
  633. */
  634. static function registry($name)
  635. {
  636. $name = strtolower($name);
  637. if (isset(self::$_objects[$name]))
  638. {
  639. return self::$_objects[$name];
  640. }
  641. return false;
  642. // LC_MSG: No object is registered of name "%s".
  643. throw new QException(__('No object is registered of name "%s".', $name));
  644. }
  645. /**
  646. * 检查指定名字的对象是否已经注册
  647. *
  648. * @param string $name 要检查的对象名字
  649. *
  650. * @return boolean 对象是否已经登记
  651. */
  652. static function isRegistered($name)
  653. {
  654. $name = strtolower($name);
  655. return isset(self::$_objects[$name]);
  656. }
  657. /**
  658. * 读取指定的缓存内容,如果内容不存在或已经失效,则返回 false
  659. *
  660. * 在操作缓存数据时,必须指定缓存的 ID。每一个缓存内容都有一个唯一的 ID。
  661. * 例如数据 A 的缓存 ID 是 data-a,而数据 B 的缓存 ID 是 data-b。
  662. *
  663. * 在大量使用缓存时,应该采用一定的规则来确定缓存 ID。下面是一个推荐的方案:
  664. *
  665. * <ul>
  666. * <li>首先按照缓存数据的性质确定前缀,例如 page、db 等;</li>
  667. * <li>然后按照数据的唯一索引来确定后缀,并和前缀一起组合成完整的缓存 ID。</li>
  668. * </ul>
  669. *
  670. * 按照这个规则,缓存 ID 看上去类似 page.news.1、db.members.userid。
  671. *
  672. * Q::cache() 可以指定 $policy 参数来覆盖缓存数据本身带有的策略。
  673. * 具体哪些策略可以使用,请参考不同缓存服务的文档。
  674. *
  675. * $backend_class 用于指定要使用的缓存服务对象类名称。例如 QCache_File、QCache_APC 等。
  676. *
  677. * @code php
  678. * $data = Q::cache($cache_id);
  679. * if ($data === false)
  680. * {
  681. * $data = ....
  682. * Q::writeCache($cache_id, $data);
  683. * }
  684. * @endcode
  685. *
  686. * @param string $id 缓存的 ID
  687. * @param array $policy 缓存策略
  688. * @param string $backend_class 要使用的缓存服务
  689. *
  690. * @return mixed 成功返回缓存内容,失败返回 false
  691. */
  692. static function cache($id, array $policy = null, $backend_class = null)
  693. {
  694. static $obj = null;
  695. if (is_null($backend_class))
  696. {
  697. if (is_null($obj))
  698. {
  699. $obj = self::singleton(self::ini('runtime_cache_backend'));
  700. }
  701. return $obj->get($id, $policy);
  702. }
  703. else
  704. {
  705. $cache = self::singleton($backend_class);
  706. return $cache->get($id, $policy);
  707. }
  708. }
  709. /**
  710. * 将变量内容写入缓存,失败抛出异常
  711. *
  712. * $data 参数指定要缓存的内容。如果 $data 参数不是一个字符串,则必须将缓存策略 serialize 设置为 true。
  713. * $policy 参数指定了内容的缓存策略,如果没有提供该参数,则使用缓存服务的默认策略。
  714. *
  715. * 其他参数同 Q::cache()。
  716. *
  717. * @param string $id 缓存的 ID
  718. * @param mixed $data 要缓存的数据
  719. * @param array $policy 缓存策略
  720. * @param string $backend_class 要使用的缓存服务
  721. */
  722. static function writeCache($id, $data, array $policy = null, $backend_class = null)
  723. {
  724. static $obj = null;
  725. if (is_null($backend_class))
  726. {
  727. if (is_null($obj))
  728. {
  729. $obj = self::singleton(self::ini('runtime_cache_backend'));
  730. }
  731. $obj->set($id, $data, $policy);
  732. }
  733. else
  734. {
  735. $cache = self::singleton($backend_class);
  736. $cache->set($id, $data, $policy);
  737. }
  738. }
  739. /**
  740. * 删除指定的缓存内容
  741. *
  742. * 通常,失效的缓存数据无需清理。但有时候,希望在某些操作完成后立即清除缓存。
  743. * 例如更新数据库记录后,希望删除该记录的缓存文件,以便在下一次读取缓存时重新生成缓存文件。
  744. *
  745. * @code php
  746. * Q::cleanCache($cache_id);
  747. * @endcode
  748. *
  749. * @param string $id 缓存的 ID
  750. * @param array $policy 缓存策略
  751. * @param string $backend_class 要使用的缓存服务
  752. */
  753. static function cleanCache($id, array $policy = null, $backend_class = null)
  754. {
  755. static $obj = null;
  756. if (is_null($backend_class))
  757. {
  758. if (is_null($obj))
  759. {
  760. $obj = self::singleton(self::ini('runtime_cache_backend'));
  761. }
  762. $obj->remove($id, $policy);
  763. }
  764. else
  765. {
  766. $cache = self::singleton($backend_class);
  767. $cache->remove($id, $policy);
  768. }
  769. }
  770. static function session($key,$val = NULL)
  771. {
  772. if($val === FALSE )
  773. {
  774. $_SESSION[$key] = NULL;
  775. unset($_SESSION[$key]);
  776. return true;
  777. }
  778. if($val === NULL )
  779. {
  780. if(isset($_SESSION[$key]))
  781. return $_SESSION[$key];
  782. }
  783. $val = is_array($val) ? json_encode($val):$val;
  784. $_SESSION[$key] = $val;
  785. }
  786. static function cookie($key,$val = NULL)
  787. {
  788. if($val === FALSE )
  789. {
  790. $_COOKIE[$key] = NULL;
  791. unset($_COOKIE[$key]);
  792. setcookie($key,'',time()-1,'/');
  793. return true;
  794. }
  795. if($val === NULL )
  796. {
  797. if(isset($_COOKIE[$key]))
  798. return $_COOKIE[$key];
  799. }
  800. $val = is_array($val) ? json_encode($val):$val;
  801. setcookie($key,$val,time()+3864000,'/');
  802. }
  803. /**
  804. * 对字符串或数组进行格式化,返回格式化后的数组
  805. *
  806. * $input 参数如果是字符串,则首先以“,”为分隔符,将字符串转换为一个数组。
  807. * 接下来对数组中每一个项目使用 trim() 方法去掉首尾的空白字符。最后过滤掉空字符串项目。
  808. *
  809. * 该方法的主要用途是将诸如:“item1, item2, item3” 这样的字符串转换为数组。
  810. *
  811. * @code php
  812. * $input = 'item1, item2, item3';
  813. * $output = Q::normalize($input);
  814. * // $output 现在是一个数组,结果如下:
  815. * // $output = array(
  816. * // 'item1',
  817. * // 'item2',
  818. * // 'item3',
  819. * // );
  820. *
  821. * $input = 'item1|item2|item3';
  822. * // 指定使用什么字符作为分割符
  823. * $output = Q::normalize($input, '|');
  824. * @endcode
  825. *
  826. * @param array|string $input 要格式化的字符串或数组
  827. * @param string $delimiter 按照什么字符进行分割
  828. *
  829. * @return array 格式化结果
  830. */
  831. static function normalize($input, $delimiter = ',')
  832. {
  833. if (!is_array($input))
  834. {
  835. $input = explode($delimiter, $input);
  836. }
  837. $input = array_map('trim', $input);
  838. return array_filter($input, 'strlen');
  839. }
  840. /**
  841. * 创建一个用户界面控件对象
  842. *
  843. * 使用 Q::control() 方法,可以很容易的创建指定类型的用户界面控件对象。
  844. *
  845. * @param string $type 用户界面控件对象的类型
  846. * @param string $id 控件ID
  847. * @param array $attrs 要传递给控件的附加属性
  848. *
  849. *
  850. * @return QUI_Control_Abstract 创建的用户界面控件对象
  851. */
  852. static function control($type, $id = null, $attrs = array())
  853. {
  854. $id = empty($id) ? strtolower($type) : strtolower($id);
  855. $class_name = "Control_{$type}";
  856. return new $class_name($id, $attrs);
  857. }
  858. /**
  859. * 用于 QeePHP 的类自动载入,不需要由开发者调用
  860. *
  861. * @param string $class_name
  862. */
  863. static function autoload($class_name)
  864. {
  865. self::loadClass($class_name, null, false);
  866. }
  867. /**
  868. * 注册或取消注册一个自动类载入方法
  869. *
  870. * 该方法参考 Zend Framework。
  871. *
  872. * @param string $class 提供自动载入服务的类
  873. * @param boolean $enabled 启用或禁用该服务
  874. */
  875. static function registerAutoload($class = 'Q', $enabled = true)
  876. {
  877. if (!function_exists('spl_autoload_register'))
  878. {
  879. require_once Q_DIR . '/qexception.php';
  880. throw new QException('spl_autoload does not exist in this PHP installation');
  881. }
  882. if ($enabled === true)
  883. {
  884. spl_autoload_register(array($class, 'autoload'));
  885. }
  886. else
  887. {
  888. spl_autoload_unregister(array($class, 'autoload'));
  889. }
  890. }
  891. }
  892. /**
  893. * QeePHP 内部使用的多语言翻译函数
  894. *
  895. * 应用程序应该使用 QTranslate 组件实现多语言界面。
  896. *
  897. * @return $msg
  898. */
  899. function __()
  900. {
  901. $args = func_get_args();
  902. $msg = array_shift($args);
  903. /*
  904. $language = strtolower(Q::ini('error_language'));
  905. $messages = Q::loadFile('lc_messages.php', Q_DIR . '/_lang/' . $language, false);
  906. if (isset($messages[$msg]))
  907. {
  908. $msg = $messages[$msg];
  909. }
  910. */
  911. array_unshift($args, $msg);
  912. return call_user_func_array('sprintf', $args);
  913. }
  914. /**
  915. * 转换 HTML 特殊字符,等同于 htmlspecialchars()
  916. *
  917. * @param string $text
  918. *
  919. * @return string
  920. */
  921. function h($text)
  922. {
  923. return htmlspecialchars($text);
  924. }
  925. /**
  926. * 输出转义后的字符串
  927. *
  928. * @param string $text
  929. */
  930. function p($text)
  931. {
  932. echo htmlspecialchars($text);
  933. }
  934. /**
  935. * QDebug::dump() 的简写,用于输出一个变量的内容
  936. *
  937. * @param mixed $vars 要输出的变量
  938. * @param string $label 输出变量时显示的标签
  939. * @param boolean $return 是否返回输出内容
  940. *
  941. * @return string
  942. */
  943. function dump($vars, $label = null, $return = false)
  944. {
  945. if ($return) ob_start();
  946. QDebug::dump($vars, $label);
  947. if ($return)
  948. {
  949. $return = ob_get_clean();
  950. return $return;
  951. }
  952. }
  953. /**
  954. * QContext::url() 方法的简写,用于构造一个 URL 地址
  955. *
  956. * url() 方法的参数比较复杂,请参考 QContext::url() 方法的详细说明。
  957. *
  958. * @param string $udi UDI 字符串
  959. * @param array|string $params 附加参数数组
  960. * @param string $route_name 路由名
  961. * @param array $opts 控制如何生成 URL 的选项
  962. *
  963. * @return string 生成的 URL 地址
  964. */
  965. function url($udi, $params = null, $route_name = null, array $opts = null)
  966. {
  967. if($udi == '-')
  968. {
  969. if(!is_array($params)) $params = Q::normalize($params, '/');
  970. $params = array_replace($_GET,$params);
  971. $udi = '.';
  972. }
  973. return QContext::instance()->url($udi, $params, $route_name, $opts);
  974. }
  975. define('SALT', 'AC*&8_sad127312');
  976. function encrypt($str,$key = SALT)
  977. {
  978. return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
  979. }
  980. function decrypt($str,$key = SALT)
  981. {
  982. return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($str), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
  983. }
  984. function _size($bytes,$precision = 2)
  985. {
  986. $units = array('B', 'KB', 'MB', 'GB', 'TB');
  987. $bytes = max($bytes, 0);
  988. $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
  989. $pow = min($pow, count($units) - 1);
  990. $bytes /= pow(1024, $pow);
  991. if($units[$pow] == 'B') { $pow += 1;$bytes /= 1024;}
  992. return round($bytes, $precision) . ' ' . $units[$pow];
  993. }
  994. function __uid()
  995. {
  996. return substr(md5(microtime()),8,16);
  997. }
  998. function __oid()
  999. {
  1000. list($uec, $sec) = explode(".", microtime(true));
  1001. $sec = str_pad($sec,4,0,STR_PAD_LEFT);
  1002. $str = date('YmdHis') . $sec;
  1003. return $str;
  1004. }
  1005. function __url($udi = null,$arg = array())
  1006. {
  1007. if(is_array($udi)) {$arg = $udi; $udi = null;}
  1008. $context = QContext::instance();
  1009. $get = $_GET;
  1010. unset($get['module']); unset($get['action']); unset($get['controller']);
  1011. if($udi === false)
  1012. $arg = $arg ? $arg : $get;
  1013. else
  1014. $arg = $arg ? array_merge($get,$arg) : $get;
  1015. $udi = $udi ? $udi : $context->requestUDI(false);
  1016. return url($udi) . '?'.http_build_query($arg, '', '&');
  1017. }
  1018. function __cut($title,$lenth = 10)
  1019. {
  1020. return chop(mb_substr($title,0,$lenth,"utf-8"));
  1021. }
  1022. function _echo($str)
  1023. {
  1024. echo $str;
  1025. }
  1026. function _trim($src,$str = ',')
  1027. {
  1028. return trim(str_replace($str,'',$src));
  1029. }
  1030. function ic($str_1,$str_2,$str_3)
  1031. {
  1032. if(strpos($str_2,','))
  1033. {
  1034. $str_4 = '';
  1035. foreach(explode(',',$str_2) as $s2)
  1036. {
  1037. if($str_1 == $s2)
  1038. {
  1039. $str_4 = $str_3;
  1040. }
  1041. }
  1042. echo $str_4;
  1043. }else
  1044. {
  1045. echo $str_1 == $str_2 ? $str_3 : '';
  1046. }
  1047. }
  1048. /**
  1049. * 设置对象的自动载入
  1050. */
  1051. Q::registerAutoload();