Application.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. <?php
  2. namespace Qii;
  3. use Qii\Base\Bootstrap;
  4. use \Qii\Base\Dispatcher;
  5. use \Qii\Autoloader\Factory;
  6. use \Qii\Autoloader\Import;
  7. use \Qii\Autoloader\Psr4;
  8. use \Qii\Autoloader\Instance;
  9. use \Qii\Config\Register;
  10. use \Qii\Config\Consts;
  11. use \Qii\Config\Setting;
  12. use Qii\Exceptions\ClassInstanceof;
  13. use Qii\Exceptions\ClassNotFound;
  14. use Qii\Exceptions\FileNotFound;
  15. use Qii\Exceptions\FolderDoesNotExist;
  16. use Qii\Request\Url;
  17. class Application
  18. {
  19. /**
  20. * 存储网站配置文件内容
  21. *
  22. * @var array $config 配置内容
  23. */
  24. protected static $config = [];
  25. /**
  26. * @var object $loggerWriter 写日志工具
  27. */
  28. public $loggerWriter = null;
  29. /**
  30. * @var string $workspace 工作目录
  31. */
  32. private static $workspace = './';
  33. /**
  34. * @var string $env 环境变量
  35. */
  36. public static $env = 'product';
  37. /**
  38. * @var array $paths 网站使用的路径
  39. */
  40. public static $paths = array('configure', 'controller', 'model', 'middleware', 'helper', 'view', 'plugins', 'tmp');
  41. /**
  42. * 系统注册的 router
  43. * @var array
  44. */
  45. private $router = [];
  46. /**
  47. * Qii\Request\Url
  48. */
  49. public $request;
  50. /**
  51. * 分发器
  52. */
  53. public $dispatcher = null;
  54. /**
  55. * @var mixed $helper
  56. */
  57. public $helper;
  58. /**
  59. * 全局middleware
  60. * @var array
  61. */
  62. private $middleware = [];
  63. /**
  64. * 路由middleware
  65. * @var array middleware
  66. */
  67. public function __construct()
  68. {
  69. $this->helper = Psr4::getInstance()->loadClass('\Qii\Autoloader\Helper');
  70. }
  71. /**
  72. * 初始化本实例对象
  73. *
  74. * @return object
  75. */
  76. public static function getInstance()
  77. {
  78. $args = func_get_args();
  79. if (count($args) > 0) {
  80. $className = array_shift($args);
  81. return Psr4::getInstance($className);
  82. }
  83. return Factory::getInstance('\Qii\Application');
  84. }
  85. /**
  86. * 设置网站运行环境
  87. *
  88. * @param string $env 网站环境
  89. * @return $this
  90. */
  91. public function setEnv($env)
  92. {
  93. self::$env = $env;
  94. return $this;
  95. }
  96. /**
  97. * 设置缓存文件路径
  98. * @param string $path 缓存路径
  99. */
  100. public function setCachePath($path)
  101. {
  102. Register::set(Consts::APP_CACHE_PATH, $this->getCachePath($path));
  103. return $this;
  104. }
  105. /**
  106. * 保存网站的配置文件
  107. *
  108. * @param $iniFile
  109. */
  110. public function setAppIniFile($iniFile)
  111. {
  112. Register::set(Consts::APP_INI_FILE, $iniFile);
  113. return $this;
  114. }
  115. /**
  116. * 设置网站的工作目录,可以通过此方法将网站的重要文件指向到其他目录
  117. *
  118. * @param string $workspace 工作目录
  119. * @return $this
  120. */
  121. public function setWorkspace($workspace = './')
  122. {
  123. //此处转换成真实路径,防止workspace中引入的文件出错
  124. if (!is_dir($workspace)) {
  125. throw new FolderDoesNotExist(\Qii::i(1045, $workspace), __LINE__);
  126. }
  127. $workspace = Psr4::getInstance()->realpath($workspace);
  128. Psr4::getInstance()->removeNamespace('workspace', self::$workspace);
  129. //如果配置了使用namespace就走namespace
  130. self::$workspace = $workspace;
  131. Psr4::getInstance()->addNamespace('workspace', $workspace, true);
  132. foreach (self::$paths AS $path) {
  133. Psr4::getInstance()->addNamespace($path, $workspace . '\\' . $path);
  134. }
  135. return $this;
  136. }
  137. /**
  138. * 获取指定路径的缓存绝对路径
  139. * @param string $path 路径
  140. * @return string 绝对路径
  141. */
  142. public function getCachePath($path)
  143. {
  144. if (self::$workspace != '') return self::$workspace . DS . $path;
  145. $dir = '';
  146. $workspace = Psr4::getInstance()->getNamespace('workspace');
  147. foreach ($workspace AS $dir) {
  148. if (is_dir($dir)) $dir = $dir;
  149. }
  150. return $dir . DS . $path;
  151. }
  152. /**
  153. * 获取网站运行环境
  154. *
  155. * @return string
  156. */
  157. public function getEnv()
  158. {
  159. return self::$env;
  160. }
  161. /**
  162. * 获取当前工作目录
  163. */
  164. public function getWorkspace()
  165. {
  166. return self::$workspace;
  167. }
  168. /**
  169. * 获取网站的配置文件
  170. * @return Config\Mix
  171. */
  172. public function getAppIniFile()
  173. {
  174. return Register::get(Consts::APP_INI_FILE);
  175. }
  176. /**
  177. * 设置网站配置文件
  178. * @param string $ini 配置文件路径
  179. * @param string $env 环境
  180. * @return $this
  181. * @throws ClassNotFound
  182. */
  183. public function setAppConfigure($ini, $env = '')
  184. {
  185. if ($env == '') $env = $this->getEnv();
  186. $ini = Psr4::getInstance()->getFileByPrefix($ini);
  187. $this->setAppIniFile($ini);
  188. if (!Register::setAppConfigure($ini, $env)) throw new FileNotFound($ini, 404);
  189. //载入request方法
  190. $this->request = $this->getRequest();
  191. $this->dispatcher = $this->getDispatcher();
  192. if (!$this->dispatcher instanceof Dispatcher) {
  193. throw new \Exception('Dispatcher must instance of Qii\Base\Dispatcher', __LINE__);
  194. }
  195. Setting::getInstance()->setDefaultTimeZone();
  196. Setting::getInstance()->setDefaultControllerAction();
  197. Setting::getInstance()->setDefaultNamespace();
  198. Setting::getInstance()->setDefaultLanguage();
  199. $this->helper->load(self::$workspace);
  200. return $this;
  201. }
  202. /**
  203. * 合并ini文件生成的数组
  204. * @param String $iniFile ini文件名
  205. * @param Array $array
  206. * @return $this
  207. */
  208. public function mergeAppConfigure($iniFile, $array)
  209. {
  210. if (!is_array($array)) return;
  211. Register::mergeAppConfigure($iniFile, $array);
  212. return $this;
  213. }
  214. /**
  215. * 覆盖/添加ini文件的key对应的值
  216. * @param string $iniFile ini文件名
  217. * @param string $key
  218. * @param string $val
  219. * @return $this
  220. */
  221. public function rewriteAppConfigure($iniFile, $key, $val)
  222. {
  223. Register::rewriteConfig($iniFile, $key, $val);
  224. return $this;
  225. }
  226. /**
  227. * 设置指定的前缀是否使用命名空间
  228. * @param string $prefix 前缀
  229. * @param bool $useNamespace 是否使用
  230. * @return $this
  231. */
  232. public function setUseNamespace($prefix, $useNamespace = true)
  233. {
  234. Psr4::getInstance()->setUseNamespace($prefix, $useNamespace);
  235. return $this;
  236. }
  237. /**
  238. * 添加命名空间对应的网站目录
  239. * @param string $prefix 前缀
  240. * @param string $baseDir 对应的路径
  241. * @param bool $prepend 是否追加
  242. * @return $this
  243. */
  244. public function addNamespace($prefix, $baseDir, $prepend = false)
  245. {
  246. if (!is_dir($baseDir)) {
  247. throw new FolderDoesNotExist(\Qii::i(1009, $baseDir), __LINE__);
  248. }
  249. $baseDir = Psr4::getInstance()->realpath($baseDir);
  250. Psr4::getInstance()->addNamespace($prefix, $baseDir, $prepend);
  251. return $this;
  252. }
  253. /**
  254. * 设置启动前执行的方法
  255. *
  256. * @return $this
  257. * @throws Exception
  258. */
  259. public function setBootstrap()
  260. {
  261. Psr4::getInstance()->loadFileByClass('Bootstrap');
  262. if (!class_exists('Bootstrap', false)) throw new ClassNotFound(\Qii::i(1405, 'Bootstrap'), __LINE__);;
  263. $bootstrap = Psr4::getInstance()->instance('Bootstrap');
  264. if (!$bootstrap instanceof Bootstrap) {
  265. throw new ClassInstanceof(Qii::i(1107, 'Bootstrap', 'Qii\Bootstrap'), __LINE__);;
  266. }
  267. $refectionClass = new \ReflectionClass('Bootstrap');
  268. $methods = $refectionClass->getMethods();
  269. //自动执行以init开头的公共方法
  270. foreach ($methods as $method) {
  271. $name = $method->getName();
  272. if (substr($name, 0, 4) == 'init' && $method->isPublic()) $bootstrap->$name();
  273. }
  274. return $this;
  275. }
  276. /**
  277. * 全局 Middleware
  278. * @param array $middleware
  279. * @return void
  280. */
  281. public function setGlobalMiddleware($middleware) {
  282. if (!is_array($middleware)) {
  283. return $this;
  284. }
  285. $this->middleware = array_merge($this->middleware, $middleware);
  286. return $this;
  287. }
  288. /**
  289. * 实例化request并返回
  290. *
  291. * @return mixed
  292. * @throws ClassNotFound
  293. */
  294. public function getRequest() {
  295. if ($this->request !== null) {
  296. return $this->request;
  297. }
  298. return Psr4::getInstance()->loadClass('\Qii\Request\Http');
  299. }
  300. /**
  301. * Set Dispatcher
  302. * @return void
  303. * @throws ClassNotFound
  304. */
  305. public function getDispatcher() {
  306. if ($this->dispatcher !== null) {
  307. return $this->dispatcher;
  308. }
  309. return Psr4::getInstance()->loadClass('\Qii\Base\Dispatcher');
  310. }
  311. /**
  312. * 设置写loger的类
  313. *
  314. * @param LoggerWriter $loggerCls 日志记录类
  315. * @return $this
  316. */
  317. public function setLogger($loggerCls)
  318. {
  319. $this->loggerWriter = Instance::instance(
  320. '\Qii\Logger\Instance',
  321. Instance::instance($loggerCls)
  322. );
  323. return $this;
  324. }
  325. /**
  326. * 设置缓存
  327. *
  328. * @param string $engine 缓存方法
  329. * @param array $policy 缓存策略
  330. */
  331. public function setCache($engine = '', $policy = array())
  332. {
  333. $engine = $engine == '' ? \Qii::appConfigure('cache') : $engine;
  334. $basicPolicy = array(
  335. 'servers' => $this->getCachePolicy($engine),
  336. );
  337. if ($basicPolicy['servers']) {
  338. $policy = array_merge($basicPolicy, $policy);
  339. }
  340. $loader = new \Qii\Cache\Loader($engine);
  341. return $loader->initialization($policy);
  342. }
  343. /**
  344. * 获取缓存的策略
  345. * @param String $cache 缓存的内容
  346. * @return multitype:multitype:Ambigous <>
  347. */
  348. public function getCachePolicy($cache)
  349. {
  350. $data = array();
  351. if (!$cache) return $data;
  352. $cacheInfo = Register::getAppConfigure(Register::get(Consts::APP_INI_FILE), $cache);
  353. if (!$cacheInfo) return $data;
  354. $servers = explode(";", $cacheInfo['servers']);
  355. $ports = explode(";", $cacheInfo['ports']);
  356. $password = explode(';', (isset($cacheInfo['password']) ? $cacheInfo['password'] : ''));
  357. for ($i = 0; $i < count($servers); $i++) {
  358. $data[] = array('host' => $servers[$i], 'port' => $ports[$i], 'password' => $password[$i]);
  359. }
  360. return $data;
  361. }
  362. /**
  363. * 设置view
  364. *
  365. * @param string $engine
  366. * @param array $policy
  367. * @return mixed
  368. */
  369. public function setView($engine = 'smarty', $policy = array())
  370. {
  371. $viewConfigure = \Qii::appConfigure('view');
  372. //如果之前实例化过相同的就不再实例化
  373. if (!$engine) $engine = $viewConfigure['engine'];
  374. $policy = (array)$policy;
  375. if (!$policy) {
  376. $policy = array_merge($policy, $viewConfigure[$engine]);
  377. }
  378. $viewEngine = Psr4::getInstance()->loadClass('\Qii\View\Loader');
  379. $viewEngine->setView($engine, $policy);
  380. return $viewEngine;
  381. }
  382. /**
  383. * 设置数据库使用的文件
  384. *
  385. * @param $iniFile
  386. * @throws \Qii\Exceptions\Overwrite
  387. */
  388. public function setDBIniFile($iniFile)
  389. {
  390. Register::set(Consts::APP_DB, $iniFile);
  391. }
  392. /**
  393. * 获取当前数据库文件
  394. *
  395. * @return \Qii\Mix
  396. * @throws \Qii\Exceptions\Variable
  397. */
  398. public function getDBIniFile()
  399. {
  400. return Register::get(Consts::APP_DB);
  401. }
  402. /**
  403. * 设置数据库配置文件
  404. * @param string $ini 配置文件路径
  405. * @param string $env 环境
  406. */
  407. public function setDB($ini, $env = '')
  408. {
  409. if ($env == '') $env = $this->getEnv();
  410. $this->setDBIniFile($ini);
  411. if (!Register::setAppConfigure(
  412. Psr4::getInstance()->getFileByPrefix($ini),
  413. $env)
  414. ) {
  415. throw new FileNotFound($ini, 404);
  416. }
  417. return $this;
  418. }
  419. /**
  420. * @param string $path 从指定路径加载路由
  421. *
  422. * @return $this
  423. */
  424. public function loadRouteFromPath($path) {
  425. Import::requireByDir($path);
  426. return $this;
  427. }
  428. /**
  429. * 设置路由规则
  430. *
  431. * @param string $router 路由配置文件位置
  432. * @return mixed
  433. */
  434. public function setRouter($router)
  435. {
  436. $router = Import::includes(Psr4::realpath(Psr4::getInstance()->getFileByPrefix($router)));
  437. if(is_array($router)) {
  438. $this->router = array_merge($this->router, $router);
  439. }
  440. Register::set(
  441. Consts::APP_SITE_ROUTER,
  442. $this->router
  443. );
  444. return $this;
  445. }
  446. /**
  447. * sprintf 格式化语言错误信息内容
  448. *
  449. * Qii::e($message, $argv1, $argv2, ..., $line);
  450. * $message = sprintf($message, $argv1, $argv2, ...);
  451. * throw new \Qii\Exceptions\Error($message, $line);
  452. */
  453. public function showError()
  454. {
  455. return call_user_func_array(array('\Qii\Exceptions\Errors', 'e'), func_get_args());
  456. }
  457. /**
  458. * 执行
  459. * @return $this
  460. * @throws \Exception
  461. */
  462. public function run()
  463. {
  464. //全局middleware
  465. $reserve = array_unique(array_reverse($this->middleware));
  466. $next = array_reduce($reserve,function ($carry, $item){
  467. return function () use ($carry, $item){
  468. return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
  469. };
  470. }, function(){
  471. return true;
  472. })();
  473. if ($next === false) {
  474. return $this;
  475. }
  476. //route middleware
  477. $next = call_user_func(
  478. array_reduce(['Route'], function($carry, $item){
  479. return function () use ($carry, $item){
  480. return \Qii::getInstance("Qii\Base\\". $item)->match($this->request, $carry);
  481. };
  482. }, function(){
  483. //载入rewrite规则后重写request
  484. $rewrite = Psr4::loadStatic(
  485. '\Qii\Router\Parse',
  486. 'get',
  487. Url::getPathInfo(),
  488. $this->request->controller,
  489. $this->request->action,
  490. $this->request->url->get(2)
  491. );
  492. $rewrite['controller'] = isset($rewrite['controller']) && $rewrite['controller'] ? $rewrite['controller'] : $this->request->defaultController();
  493. $rewrite['action'] = isset($rewrite['action']) && $rewrite['action'] ? $rewrite['action'] : $this->request->defaultAction();
  494. //是否已经rewrite,如果和url中的不一样就是已经rewrite
  495. if ($this->request->controller != $rewrite['controller'] || $this->request->action != $rewrite['action']) {
  496. $this->request->setRouted(true);
  497. }
  498. $this->request->setControllerName($rewrite['controller']);
  499. $this->request->setActionName($rewrite['action']);
  500. return true;
  501. })
  502. );
  503. if ($next === false) {
  504. return $this;
  505. }
  506. //如果app.ini中设置了host的话,看host对应的controller路径
  507. $hosts = $this->appConfigure('hosts');
  508. if (!empty($hosts) && is_array($hosts) && count($hosts) > 0) {
  509. foreach ($hosts AS $host) {
  510. if ($host['domain'] == $this->request->host) {
  511. Register::set(
  512. Consts::APP_DEFAULT_CONTROLLER_PREFIX,
  513. (isset($host['path']) && $host['path'] ? $host['path'] : $host['domain'])
  514. );
  515. break;
  516. }
  517. }
  518. }
  519. $this->request->setDispatcher($this->dispatcher);
  520. //rewrite规则
  521. $this->dispatcher->setRequest($this->request);
  522. $this->dispatcher->dispatch();
  523. $this->request->setDispatched(true);
  524. return $this;
  525. }
  526. }