* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Flarum\Foundation; use Illuminate\Container\Container; use Illuminate\Contracts\Foundation\Application as ApplicationContract; use Illuminate\Events\EventServiceProvider; use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; class Application extends Container implements ApplicationContract { /** * The Flarum version. * * @var string */ const VERSION = '0.1.0-beta.5'; /** * The base path for the Flarum installation. * * @var string */ protected $basePath; /** * The public path for the Flarum installation. * * @var string */ protected $publicPath; /** * Indicates if the application has "booted". * * @var bool */ protected $booted = false; /** * The array of booting callbacks. * * @var array */ protected $bootingCallbacks = []; /** * The array of booted callbacks. * * @var array */ protected $bootedCallbacks = []; /** * All of the registered service providers. * * @var array */ protected $serviceProviders = []; /** * The names of the loaded service providers. * * @var array */ protected $loadedProviders = []; /** * The deferred services and their providers. * * @var array */ protected $deferredServices = []; /** * The custom storage path defined by the developer. * * @var string */ protected $storagePath; /** * Create a new Flarum application instance. * * @param string|null $basePath * @param string|null $publicPath */ public function __construct($basePath = null, $publicPath = null) { $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); if ($basePath) { $this->setBasePath($basePath); } if ($publicPath) { $this->setPublicPath($publicPath); } } /** * Determine if Flarum has been installed. * * @return bool */ public function isInstalled() { return $this->bound('flarum.config'); } public function isUpToDate() { $settings = $this->make('Flarum\Settings\SettingsRepositoryInterface'); try { $version = $settings->get('version'); } finally { $isUpToDate = isset($version) && $version === $this->version(); } return $isUpToDate; } /** * @param string $key * @param mixed $default * @return mixed */ public function config($key, $default = null) { return array_get($this->make('flarum.config'), $key, $default); } /** * Check if Flarum is in debug mode. * * @return bool */ public function inDebugMode() { return ! $this->isInstalled() || $this->config('debug'); } /** * Get the URL to the Flarum installation. * * @param string $path * @return string */ public function url($path = null) { $config = $this->isInstalled() ? $this->make('flarum.config') : []; $url = array_get($config, 'url', $_SERVER['REQUEST_URI']); if (is_array($url)) { if (isset($url[$path])) { return $url[$path]; } $url = $url['base']; } if ($path) { $url .= '/'.array_get($config, "paths.$path", $path); } return $url; } /** * Get the version number of the application. * * @return string */ public function version() { return static::VERSION; } /** * Register the basic bindings into the container. */ protected function registerBaseBindings() { static::setInstance($this); $this->instance('app', $this); $this->instance('Illuminate\Container\Container', $this); } /** * Register all of the base service providers. */ protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); } /** * Set the base path for the application. * * @param string $basePath * @return $this */ public function setBasePath($basePath) { $this->basePath = rtrim($basePath, '\/'); $this->bindPathsInContainer(); return $this; } /** * Set the public path for the application. * * @param string $publicPath * @return $this */ public function setPublicPath($publicPath) { $this->publicPath = rtrim($publicPath, '\/'); $this->bindPathsInContainer(); return $this; } /** * Bind all of the application paths in the container. * * @return void */ protected function bindPathsInContainer() { foreach (['base', 'public', 'storage'] as $path) { $this->instance('path.'.$path, $this->{$path.'Path'}()); } } /** * Get the base path of the Laravel installation. * * @return string */ public function basePath() { return $this->basePath; } /** * Get the path to the public / web directory. * * @return string */ public function publicPath() { return $this->publicPath; } /** * Get the path to the storage directory. * * @return string */ public function storagePath() { return $this->storagePath ?: $this->basePath.DIRECTORY_SEPARATOR.'storage'; } /** * Set the storage directory. * * @param string $path * @return $this */ public function useStoragePath($path) { $this->storagePath = $path; $this->instance('path.storage', $path); return $this; } /** * Get or check the current application environment. * * @param mixed * @return string */ public function environment() { if (func_num_args() > 0) { $patterns = is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args(); foreach ($patterns as $pattern) { if (Str::is($pattern, $this['env'])) { return true; } } return false; } return $this['env']; } /** * Determine if we are running in the console. * * @return bool */ public function runningInConsole() { return php_sapi_name() == 'cli'; } /** * Determine if we are running unit tests. * * @return bool */ public function runningUnitTests() { return $this['env'] == 'testing'; } /** * Register all of the configured providers. * * @return void */ public function registerConfiguredProviders() { } /** * Register a service provider with the application. * * @param ServiceProvider|string $provider * @param array $options * @param bool $force * @return ServiceProvider */ public function register($provider, $options = [], $force = false) { if ($registered = $this->getProvider($provider) && ! $force) { return $registered; } // If the given "provider" is a string, we will resolve it, passing in the // application instance automatically for the developer. This is simply // a more convenient way of specifying your service provider classes. if (is_string($provider)) { $provider = $this->resolveProviderClass($provider); } $provider->register(); // Once we have registered the service we will iterate through the options // and set each of them on the application so they will be available on // the actual loading of the service objects and for developer usage. foreach ($options as $key => $value) { $this[$key] = $value; } $this->markAsRegistered($provider); // If the application has already booted, we will call this boot method on // the provider class so it has an opportunity to do its boot logic and // will be ready for any usage by the developer's application logics. if ($this->booted) { $this->bootProvider($provider); } return $provider; } /** * Get the registered service provider instance if it exists. * * @param ServiceProvider|string $provider * @return ServiceProvider|null */ public function getProvider($provider) { $name = is_string($provider) ? $provider : get_class($provider); return Arr::first($this->serviceProviders, function ($key, $value) use ($name) { return $value instanceof $name; }); } /** * Resolve a service provider instance from the class name. * * @param string $provider * @return ServiceProvider */ public function resolveProviderClass($provider) { return new $provider($this); } /** * Mark the given provider as registered. * * @param ServiceProvider $provider * @return void */ protected function markAsRegistered($provider) { $this['events']->fire($class = get_class($provider), [$provider]); $this->serviceProviders[] = $provider; $this->loadedProviders[$class] = true; } /** * Load and boot all of the remaining deferred providers. */ public function loadDeferredProviders() { // We will simply spin through each of the deferred providers and register each // one and boot them if the application has booted. This should make each of // the remaining services available to this application for immediate use. foreach ($this->deferredServices as $service => $provider) { $this->loadDeferredProvider($service); } $this->deferredServices = []; } /** * Load the provider for a deferred service. * * @param string $service */ public function loadDeferredProvider($service) { if (! isset($this->deferredServices[$service])) { return; } $provider = $this->deferredServices[$service]; // If the service provider has not already been loaded and registered we can // register it with the application and remove the service from this list // of deferred services, since it will already be loaded on subsequent. if (! isset($this->loadedProviders[$provider])) { $this->registerDeferredProvider($provider, $service); } } /** * Register a deferred provider and service. * * @param string $provider * @param string $service */ public function registerDeferredProvider($provider, $service = null) { // Once the provider that provides the deferred service has been registered we // will remove it from our local list of the deferred services with related // providers so that this container does not try to resolve it out again. if ($service) { unset($this->deferredServices[$service]); } $this->register($instance = new $provider($this)); if (! $this->booted) { $this->booting(function () use ($instance) { $this->bootProvider($instance); }); } } /** * Resolve the given type from the container. * * (Overriding Container::make) * * @param string $abstract * @param array $parameters * @return mixed */ public function make($abstract, array $parameters = []) { $abstract = $this->getAlias($abstract); if (isset($this->deferredServices[$abstract])) { $this->loadDeferredProvider($abstract); } return parent::make($abstract, $parameters); } /** * Determine if the given abstract type has been bound. * * (Overriding Container::bound) * * @param string $abstract * @return bool */ public function bound($abstract) { return isset($this->deferredServices[$abstract]) || parent::bound($abstract); } /** * Determine if the application has booted. * * @return bool */ public function isBooted() { return $this->booted; } /** * Boot the application's service providers. * * @return void */ public function boot() { if ($this->booted) { return; } // Once the application has booted we will also fire some "booted" callbacks // for any listeners that need to do work after this initial booting gets // finished. This is useful when ordering the boot-up processes we run. $this->fireAppCallbacks($this->bootingCallbacks); array_walk($this->serviceProviders, function ($p) { $this->bootProvider($p); }); $this->booted = true; $this->fireAppCallbacks($this->bootedCallbacks); } /** * Boot the given service provider. * * @param ServiceProvider $provider * @return mixed */ protected function bootProvider(ServiceProvider $provider) { if (method_exists($provider, 'boot')) { return $this->call([$provider, 'boot']); } } /** * Register a new boot listener. * * @param mixed $callback * @return void */ public function booting($callback) { $this->bootingCallbacks[] = $callback; } /** * Register a new "booted" listener. * * @param mixed $callback * @return void */ public function booted($callback) { $this->bootedCallbacks[] = $callback; if ($this->isBooted()) { $this->fireAppCallbacks([$callback]); } } /** * Call the booting callbacks for the application. * * @param array $callbacks * @return void */ protected function fireAppCallbacks(array $callbacks) { foreach ($callbacks as $callback) { call_user_func($callback, $this); } } /** * Get the path to the cached "compiled.php" file. * * @return string */ public function getCachedCompilePath() { return $this->basePath().'/bootstrap/cache/compiled.php'; } /** * Get the path to the cached services.json file. * * @return string */ public function getCachedServicesPath() { return $this->basePath().'/bootstrap/cache/services.json'; } /** * Determine if the application is currently down for maintenance. * * @return bool */ public function isDownForMaintenance() { return $this->config('offline'); } /** * Get the service providers that have been loaded. * * @return array */ public function getLoadedProviders() { return $this->loadedProviders; } /** * Get the application's deferred services. * * @return array */ public function getDeferredServices() { return $this->deferredServices; } /** * Set the application's deferred services. * * @param array $services * @return void */ public function setDeferredServices(array $services) { $this->deferredServices = $services; } /** * Add an array of services to the application's deferred services. * * @param array $services * @return void */ public function addDeferredServices(array $services) { $this->deferredServices = array_merge($this->deferredServices, $services); } /** * Determine if the given service is a deferred service. * * @param string $service * @return bool */ public function isDeferredService($service) { return isset($this->deferredServices[$service]); } /** * Register the core class aliases in the container. */ public function registerCoreContainerAliases() { $aliases = [ 'app' => ['Flarum\Foundation\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'], 'blade.compiler' => 'Illuminate\View\Compilers\BladeCompiler', 'cache' => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'], 'cache.store' => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'], 'config' => ['Illuminate\Config\Repository', 'Illuminate\Contracts\Config\Repository'], 'db' => 'Illuminate\Database\DatabaseManager', 'events' => ['Illuminate\Events\Dispatcher', 'Illuminate\Contracts\Events\Dispatcher'], 'files' => 'Illuminate\Filesystem\Filesystem', 'filesystem' => ['Illuminate\Filesystem\FilesystemManager', 'Illuminate\Contracts\Filesystem\Factory'], 'filesystem.disk' => 'Illuminate\Contracts\Filesystem\Filesystem', 'filesystem.cloud' => 'Illuminate\Contracts\Filesystem\Cloud', 'hash' => 'Illuminate\Contracts\Hashing\Hasher', 'mailer' => ['Illuminate\Mail\Mailer', 'Illuminate\Contracts\Mail\Mailer', 'Illuminate\Contracts\Mail\MailQueue'], 'validator' => ['Illuminate\Validation\Factory', 'Illuminate\Contracts\Validation\Factory'], 'view' => ['Illuminate\View\Factory', 'Illuminate\Contracts\View\Factory'], ]; foreach ($aliases as $key => $aliases) { foreach ((array) $aliases as $alias) { $this->alias($key, $alias); } } } /** * Flush the container of all bindings and resolved instances. */ public function flush() { parent::flush(); $this->loadedProviders = []; } }