App.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. namespace think;
  13. use think\event\AppInit;
  14. use think\helper\Str;
  15. use think\initializer\BootService;
  16. use think\initializer\Error;
  17. use think\initializer\RegisterService;
  18. /**
  19. * App 基础类
  20. * @property Route $route
  21. * @property Config $config
  22. * @property Cache $cache
  23. * @property Request $request
  24. * @property Http $http
  25. * @property Console $console
  26. * @property Env $env
  27. * @property Event $event
  28. * @property Middleware $middleware
  29. * @property Log $log
  30. * @property Lang $lang
  31. * @property Db $db
  32. * @property Cookie $cookie
  33. * @property Session $session
  34. * @property Validate $validate
  35. * @property Filesystem $filesystem
  36. */
  37. class App extends Container
  38. {
  39. const VERSION = '6.0.12LTS';
  40. /**
  41. * 应用调试模式
  42. * @var bool
  43. */
  44. protected $appDebug = false;
  45. /**
  46. * 环境变量标识
  47. * @var string
  48. */
  49. protected $envName = '';
  50. /**
  51. * 应用开始时间
  52. * @var float
  53. */
  54. protected $beginTime;
  55. /**
  56. * 应用内存初始占用
  57. * @var integer
  58. */
  59. protected $beginMem;
  60. /**
  61. * 当前应用类库命名空间
  62. * @var string
  63. */
  64. protected $namespace = 'app';
  65. /**
  66. * 应用根目录
  67. * @var string
  68. */
  69. protected $rootPath = '';
  70. /**
  71. * 框架目录
  72. * @var string
  73. */
  74. protected $thinkPath = '';
  75. /**
  76. * 应用目录
  77. * @var string
  78. */
  79. protected $appPath = '';
  80. /**
  81. * Runtime目录
  82. * @var string
  83. */
  84. protected $runtimePath = '';
  85. /**
  86. * 路由定义目录
  87. * @var string
  88. */
  89. protected $routePath = '';
  90. /**
  91. * 配置后缀
  92. * @var string
  93. */
  94. protected $configExt = '.php';
  95. /**
  96. * 应用初始化器
  97. * @var array
  98. */
  99. protected $initializers = [
  100. Error::class,
  101. RegisterService::class,
  102. BootService::class,
  103. ];
  104. /**
  105. * 注册的系统服务
  106. * @var array
  107. */
  108. protected $services = [];
  109. /**
  110. * 初始化
  111. * @var bool
  112. */
  113. protected $initialized = false;
  114. /**
  115. * 容器绑定标识
  116. * @var array
  117. */
  118. protected $bind = [
  119. 'app' => App::class,
  120. 'cache' => Cache::class,
  121. 'config' => Config::class,
  122. 'console' => Console::class,
  123. 'cookie' => Cookie::class,
  124. 'db' => Db::class,
  125. 'env' => Env::class,
  126. 'event' => Event::class,
  127. 'http' => Http::class,
  128. 'lang' => Lang::class,
  129. 'log' => Log::class,
  130. 'middleware' => Middleware::class,
  131. 'request' => Request::class,
  132. 'response' => Response::class,
  133. 'route' => Route::class,
  134. 'session' => Session::class,
  135. 'validate' => Validate::class,
  136. 'view' => View::class,
  137. 'filesystem' => Filesystem::class,
  138. 'think\DbManager' => Db::class,
  139. 'think\LogManager' => Log::class,
  140. 'think\CacheManager' => Cache::class,
  141. // 接口依赖注入
  142. 'Psr\Log\LoggerInterface' => Log::class,
  143. ];
  144. /**
  145. * 架构方法
  146. * @access public
  147. * @param string $rootPath 应用根目录
  148. */
  149. public function __construct(string $rootPath = '')
  150. {
  151. $this->thinkPath = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  152. $this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
  153. $this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
  154. $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
  155. if (is_file($this->appPath . 'provider.php')) {
  156. $this->bind(include $this->appPath . 'provider.php');
  157. }
  158. static::setInstance($this);
  159. $this->instance('app', $this);
  160. $this->instance('think\Container', $this);
  161. }
  162. /**
  163. * 注册服务
  164. * @access public
  165. * @param Service|string $service 服务
  166. * @param bool $force 强制重新注册
  167. * @return Service|null
  168. */
  169. public function register($service, bool $force = false)
  170. {
  171. $registered = $this->getService($service);
  172. if ($registered && !$force) {
  173. return $registered;
  174. }
  175. if (is_string($service)) {
  176. $service = new $service($this);
  177. }
  178. if (method_exists($service, 'register')) {
  179. $service->register();
  180. }
  181. if (property_exists($service, 'bind')) {
  182. $this->bind($service->bind);
  183. }
  184. $this->services[] = $service;
  185. }
  186. /**
  187. * 执行服务
  188. * @access public
  189. * @param Service $service 服务
  190. * @return mixed
  191. */
  192. public function bootService($service)
  193. {
  194. if (method_exists($service, 'boot')) {
  195. return $this->invoke([$service, 'boot']);
  196. }
  197. }
  198. /**
  199. * 获取服务
  200. * @param string|Service $service
  201. * @return Service|null
  202. */
  203. public function getService($service)
  204. {
  205. $name = is_string($service) ? $service : get_class($service);
  206. return array_values(array_filter($this->services, function ($value) use ($name) {
  207. return $value instanceof $name;
  208. }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
  209. }
  210. /**
  211. * 开启应用调试模式
  212. * @access public
  213. * @param bool $debug 开启应用调试模式
  214. * @return $this
  215. */
  216. public function debug(bool $debug = true)
  217. {
  218. $this->appDebug = $debug;
  219. return $this;
  220. }
  221. /**
  222. * 是否为调试模式
  223. * @access public
  224. * @return bool
  225. */
  226. public function isDebug(): bool
  227. {
  228. return $this->appDebug;
  229. }
  230. /**
  231. * 设置应用命名空间
  232. * @access public
  233. * @param string $namespace 应用命名空间
  234. * @return $this
  235. */
  236. public function setNamespace(string $namespace)
  237. {
  238. $this->namespace = $namespace;
  239. return $this;
  240. }
  241. /**
  242. * 获取应用类库命名空间
  243. * @access public
  244. * @return string
  245. */
  246. public function getNamespace(): string
  247. {
  248. return $this->namespace;
  249. }
  250. /**
  251. * 设置环境变量标识
  252. * @access public
  253. * @param string $name 环境标识
  254. * @return $this
  255. */
  256. public function setEnvName(string $name)
  257. {
  258. $this->envName = $name;
  259. return $this;
  260. }
  261. /**
  262. * 获取框架版本
  263. * @access public
  264. * @return string
  265. */
  266. public function version(): string
  267. {
  268. return static::VERSION;
  269. }
  270. /**
  271. * 获取应用根目录
  272. * @access public
  273. * @return string
  274. */
  275. public function getRootPath(): string
  276. {
  277. return $this->rootPath;
  278. }
  279. /**
  280. * 获取应用基础目录
  281. * @access public
  282. * @return string
  283. */
  284. public function getBasePath(): string
  285. {
  286. return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
  287. }
  288. /**
  289. * 获取当前应用目录
  290. * @access public
  291. * @return string
  292. */
  293. public function getAppPath(): string
  294. {
  295. return $this->appPath;
  296. }
  297. /**
  298. * 设置应用目录
  299. * @param string $path 应用目录
  300. */
  301. public function setAppPath(string $path)
  302. {
  303. $this->appPath = $path;
  304. }
  305. /**
  306. * 获取应用运行时目录
  307. * @access public
  308. * @return string
  309. */
  310. public function getRuntimePath(): string
  311. {
  312. return $this->runtimePath;
  313. }
  314. /**
  315. * 设置runtime目录
  316. * @param string $path 定义目录
  317. */
  318. public function setRuntimePath(string $path): void
  319. {
  320. $this->runtimePath = $path;
  321. }
  322. /**
  323. * 获取核心框架目录
  324. * @access public
  325. * @return string
  326. */
  327. public function getThinkPath(): string
  328. {
  329. return $this->thinkPath;
  330. }
  331. /**
  332. * 获取应用配置目录
  333. * @access public
  334. * @return string
  335. */
  336. public function getConfigPath(): string
  337. {
  338. return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
  339. }
  340. /**
  341. * 获取配置后缀
  342. * @access public
  343. * @return string
  344. */
  345. public function getConfigExt(): string
  346. {
  347. return $this->configExt;
  348. }
  349. /**
  350. * 获取应用开启时间
  351. * @access public
  352. * @return float
  353. */
  354. public function getBeginTime(): float
  355. {
  356. return $this->beginTime;
  357. }
  358. /**
  359. * 获取应用初始内存占用
  360. * @access public
  361. * @return integer
  362. */
  363. public function getBeginMem(): int
  364. {
  365. return $this->beginMem;
  366. }
  367. /**
  368. * 加载环境变量定义
  369. * @access public
  370. * @param string $envName 环境标识
  371. * @return void
  372. */
  373. public function loadEnv(string $envName = ''): void
  374. {
  375. // 加载环境变量
  376. $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
  377. if (is_file($envFile)) {
  378. $this->env->load($envFile);
  379. }
  380. }
  381. /**
  382. * 初始化应用
  383. * @access public
  384. * @return $this
  385. */
  386. public function initialize()
  387. {
  388. $this->initialized = true;
  389. $this->beginTime = microtime(true);
  390. $this->beginMem = memory_get_usage();
  391. $this->loadEnv($this->envName);
  392. $this->configExt = $this->env->get('config_ext', '.php');
  393. $this->debugModeInit();
  394. // 加载全局初始化文件
  395. $this->load();
  396. // 加载应用默认语言包
  397. $this->loadLangPack();
  398. // 监听AppInit
  399. $this->event->trigger(AppInit::class);
  400. date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
  401. // 初始化
  402. foreach ($this->initializers as $initializer) {
  403. $this->make($initializer)->init($this);
  404. }
  405. return $this;
  406. }
  407. /**
  408. * 是否初始化过
  409. * @return bool
  410. */
  411. public function initialized()
  412. {
  413. return $this->initialized;
  414. }
  415. /**
  416. * 加载语言包
  417. * @return void
  418. */
  419. public function loadLangPack()
  420. {
  421. // 加载默认语言包
  422. $langSet = $this->lang->defaultLangSet();
  423. $this->lang->switchLangSet($langSet);
  424. }
  425. /**
  426. * 引导应用
  427. * @access public
  428. * @return void
  429. */
  430. public function boot(): void
  431. {
  432. array_walk($this->services, function ($service) {
  433. $this->bootService($service);
  434. });
  435. }
  436. /**
  437. * 加载应用文件和配置
  438. * @access protected
  439. * @return void
  440. */
  441. protected function load(): void
  442. {
  443. $appPath = $this->getAppPath();
  444. if (is_file($appPath . 'common.php')) {
  445. include_once $appPath . 'common.php';
  446. }
  447. include_once $this->thinkPath . 'helper.php';
  448. $configPath = $this->getConfigPath();
  449. $files = [];
  450. if (is_dir($configPath)) {
  451. $files = glob($configPath . '*' . $this->configExt);
  452. }
  453. foreach ($files as $file) {
  454. $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
  455. }
  456. if (is_file($appPath . 'event.php')) {
  457. $this->loadEvent(include $appPath . 'event.php');
  458. }
  459. if (is_file($appPath . 'service.php')) {
  460. $services = include $appPath . 'service.php';
  461. foreach ($services as $service) {
  462. $this->register($service);
  463. }
  464. }
  465. }
  466. /**
  467. * 调试模式设置
  468. * @access protected
  469. * @return void
  470. */
  471. protected function debugModeInit(): void
  472. {
  473. // 应用调试模式
  474. if (!$this->appDebug) {
  475. $this->appDebug = $this->env->get('app_debug') ? true : false;
  476. ini_set('display_errors', 'Off');
  477. }
  478. if (!$this->runningInConsole()) {
  479. //重新申请一块比较大的buffer
  480. if (ob_get_level() > 0) {
  481. $output = ob_get_clean();
  482. }
  483. ob_start();
  484. if (!empty($output)) {
  485. echo $output;
  486. }
  487. }
  488. }
  489. /**
  490. * 注册应用事件
  491. * @access protected
  492. * @param array $event 事件数据
  493. * @return void
  494. */
  495. public function loadEvent(array $event): void
  496. {
  497. if (isset($event['bind'])) {
  498. $this->event->bind($event['bind']);
  499. }
  500. if (isset($event['listen'])) {
  501. $this->event->listenEvents($event['listen']);
  502. }
  503. if (isset($event['subscribe'])) {
  504. $this->event->subscribe($event['subscribe']);
  505. }
  506. }
  507. /**
  508. * 解析应用类的类名
  509. * @access public
  510. * @param string $layer 层名 controller model ...
  511. * @param string $name 类名
  512. * @return string
  513. */
  514. public function parseClass(string $layer, string $name): string
  515. {
  516. $name = str_replace(['/', '.'], '\\', $name);
  517. $array = explode('\\', $name);
  518. $class = Str::studly(array_pop($array));
  519. $path = $array ? implode('\\', $array) . '\\' : '';
  520. return $this->namespace . '\\' . $layer . '\\' . $path . $class;
  521. }
  522. /**
  523. * 是否运行在命令行下
  524. * @return bool
  525. */
  526. public function runningInConsole(): bool
  527. {
  528. return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
  529. }
  530. /**
  531. * 获取应用根目录
  532. * @access protected
  533. * @return string
  534. */
  535. protected function getDefaultRootPath(): string
  536. {
  537. return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
  538. }
  539. }