<?php<liu21st@gmail.com>declare (strict_types = 1);
namespace think;
use think\event\AppInit;
use think\helper\Str;
use think\initializer\BootService;
use think\initializer\Error;
use think\initializer\RegisterService;
class App extends Container
{
const VERSION = '6.0.3';
protected $appDebug = false;
protected $beginTime;
protected $beginMem;
protected $namespace = 'app';
protected $rootPath = '';
protected $thinkPath = '';
protected $appPath = '';
protected $runtimePath = '';
protected $routePath = '';
protected $configExt = '.php';
protected $initializers = [
Error::class,
RegisterService::class,
BootService::class,
];
protected $services = [];
protected $initialized = false;
protected $bind = [
'app' => App::class,
'cache' => Cache::class,
'config' => Config::class,
'console' => Console::class,
'cookie' => Cookie::class,
'db' => Db::class,
'env' => Env::class,
'event' => Event::class,
'http' => Http::class,
'lang' => Lang::class,
'log' => Log::class,
'middleware' => Middleware::class,
'request' => Request::class,
'response' => Response::class,
'route' => Route::class,
'session' => Session::class,
'validate' => Validate::class,
'view' => View::class,
'filesystem' => Filesystem::class,
'think\DbManager' => Db::class,
'think\LogManager' => Log::class,
'think\CacheManager' => Cache::class,
'Psr\Log\LoggerInterface' => Log::class,
];
public function __construct(string $rootPath = '')
{
$this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
$this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
$this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
if (is_file($this->appPath . 'provider.php')) {
$this->bind(include $this->appPath . 'provider.php');
}
static::setInstance($this);
$this->instance('app', $this);
$this->instance('think\Container', $this);
}
public function register($service, bool $force = false)
{
$registered = $this->getService($service);
if ($registered && !$force) {
return $registered;
}
if (is_string($service)) {
$service = new $service($this);
}
if (method_exists($service, 'register')) {
$service->register();
}
if (property_exists($service, 'bind')) {
$this->bind($service->bind);
}
$this->services[] = $service;
}
public function bootService($service)
{
if (method_exists($service, 'boot')) {
return $this->invoke([$service, 'boot']);
}
}
public function getService($service)
{
$name = is_string($service) ? $service : get_class($service);
return array_values(array_filter($this->services, function ($value) use ($name) {
return $value instanceof $name;
}, ARRAY_FILTER_USE_BOTH))[0] ?? null;
}
public function debug(bool $debug = true)
{
$this->appDebug = $debug;
return $this;
}
public function isDebug(): bool
{
return $this->appDebug;
}
public function setNamespace(string $namespace)
{
$this->namespace = $namespace;
return $this;
}
public function getNamespace(): string
{
return $this->namespace;
}
public function version(): string
{
return static::VERSION;
}
public function getRootPath(): string
{
return $this->rootPath;
}
public function getBasePath(): string
{
return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
}
public function getAppPath(): string
{
return $this->appPath;
}
public function setAppPath(string $path)
{
$this->appPath = $path;
}
public function getRuntimePath(): string
{
return $this->runtimePath;
}
public function setRuntimePath(string $path): void
{
$this->runtimePath = $path;
}
public function getThinkPath(): string
{
return $this->thinkPath;
}
public function getConfigPath(): string
{
return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
}
public function getConfigExt(): string
{
return $this->configExt;
}
public function getBeginTime(): float
{
return $this->beginTime;
}
public function getBeginMem(): int
{
return $this->beginMem;
}
public function initialize()
{
$this->initialized = true;
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->configExt = $this->env->get('config_ext', '.php');
$this->debugModeInit();
$this->load();
$langSet = $this->lang->defaultLangSet();
$this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');
$this->loadLangPack($langSet);
$this->event->trigger(AppInit::class);
date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
}
return $this;
}
public function initialized()
{
return $this->initialized;
}
public function loadLangPack($langset)
{
if (empty($langset)) {
return;
}
$files = glob($this->appPath . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
$this->lang->load($files);
$list = $this->config->get('lang.extend_list', []);
if (isset($list[$langset])) {
$this->lang->load($list[$langset]);
}
}
public function boot(): void
{
array_walk($this->services, function ($service) {
$this->bootService($service);
});
}
protected function load(): void
{
$appPath = $this->getAppPath();
if (is_file($appPath . 'common.php')) {
include_once $appPath . 'common.php';
}
include_once $this->thinkPath . 'helper.php';
$configPath = $this->getConfigPath();
$files = [];
if (is_dir($configPath)) {
$files = glob($configPath . '*' . $this->configExt);
}
foreach ($files as $file) {
$this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
}
if (is_file($appPath . 'event.php')) {
$this->loadEvent(include $appPath . 'event.php');
}
if (is_file($appPath . 'service.php')) {
$services = include $appPath . 'service.php';
foreach ($services as $service) {
$this->register($service);
}
}
}
protected function debugModeInit(): void
{
if (!$this->appDebug) {
$this->appDebug = $this->env->get('app_debug') ? true : false;
ini_set('display_errors', 'Off');
}
if (!$this->runningInConsole()) {
if (ob_get_level() > 0) {
$output = ob_get_clean();
}
ob_start();
if (!empty($output)) {
echo $output;
}
}
}
public function loadEvent(array $event): void
{
if (isset($event['bind'])) {
$this->event->bind($event['bind']);
}
if (isset($event['listen'])) {
$this->event->listenEvents($event['listen']);
}
if (isset($event['subscribe'])) {
$this->event->subscribe($event['subscribe']);
}
}
public function parseClass(string $layer, string $name): string
{
$name = str_replace(['/', '.'], '\\', $name);
$array = explode('\\', $name);
$class = Str::studly(array_pop($array));
$path = $array ? implode('\\', $array) . '\\' : '';
return $this->namespace . '\\' . $layer . '\\' . $path . $class;
}
public function runningInConsole()
{
return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
}
protected function getDefaultRootPath(): string
{
return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
}
}