Laravel核心實體:Foundation/Application.php

Laravel框架中,routing、接收request、發送response幾件事是由Symphony的元件HttpKernel Component來協助完成的。
瞭解這個元件,對於瞭解Laravel原始碼有極大的幫助:The HttpKernel Component
不過,以上文件內容生硬無比。這邊推薦一個極度優質的教學文章:
Create your own framework… on top of the Symfony2 Components
示範如何用Symphony元件寫出自己的框架。讀過之後,不但可以自己寫出簡易的框架,對於瞭解Laravel原始碼也很有幫助。


繼承Container類別:使得能夠用陣列方式操作。
實作HttpKernelInterface介面:規定實作handle函式、傳入request實體並回傳response實體。
實作TerminableInterface介面:規定實作terminate函式、完成一個request/response周期。
實作ResponsePreparerInterface介面:規定實作prepareResponse、readyForResponses函式、用以協助回傳response。

	/**
	 * The Laravel framework version.
	 *
	 * @var string
	 */
	const VERSION = '4.2.10';

	/**
	 * Indicates if the application has "booted".
	 *
	 * @var bool
	 */
	protected $booted = false;

	/**
	 * The array of booting callbacks.
	 *
	 * @var array
	 */
	protected $bootingCallbacks = array();

	/**
	 * The array of booted callbacks.
	 *
	 * @var array
	 */
	protected $bootedCallbacks = array();

	/**
	 * The array of finish callbacks.
	 *
	 * @var array
	 */
	protected $finishCallbacks = array();

	/**
	 * The array of shutdown callbacks.
	 *
	 * @var array
	 */
	protected $shutdownCallbacks = array();

4個陣列紀錄4個時間點要執行的callback:
初始化核心程序前、初始化核心程序後、執行完核心程序後、中止核心程序後。
這邊的「核心程序」是指什麼?請繼續往下看。

	/**
	 * All of the developer defined middlewares.
	 *
	 * @var array
	 */
	protected $middlewares = array();

	/**
	 * All of the registered service providers.
	 *
	 * @var array
	 */
	protected $serviceProviders = array();

	/**
	 * The names of the loaded service providers.
	 *
	 * @var array
	 */
	protected $loadedProviders = array();

	/**
	 * The deferred services and their providers.
	 *
	 * @var array
	 */
	protected $deferredServices = array();

	/**
	 * The request class used by the application.
	 *
	 * @var string
	 */
	protected static $requestClass = 'Illuminate\Http\Request';

	/**
	 * Create a new Illuminate application instance.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @return void
	 */
	public function __construct(Request $request = null)
	{
		$this->registerBaseBindings($request ?: $this->createNewRequest());

		$this->registerBaseServiceProviders();

		$this->registerBaseMiddlewares();
	}

產生request實體並綁定。
綁定幾項service provider實體。
綁定中介程序(往下看,發現函式內容其實是空的)。

建構式允許傳入Request實體當參數,但實際上在bootstrap/start.php裡,並沒有在建構時傳入任何東西。
所以registerBaseBindings永遠是用createNewRequest函式的回傳值當作參數。
這邊寫成這樣是...留給進階使用者擴充空間吧?


	/**
	 * Create a new request instance from the request class.
	 *
	 * @return \Illuminate\Http\Request
	 */
	protected function createNewRequest()
	{
		return forward_static_call(array(static::$requestClass, 'createFromGlobals'));
	}

開發時會用到的Input類別、呼叫它的靜態函式,背後對應到的實體就是在這裡產生的。
關於這個實體所屬的類別,參見:接受請求:Http/Request.php


	/**
	 * Register the basic bindings into the container.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @return void
	 */
	protected function registerBaseBindings($request)
	{
		$this->instance('request', $request);

		$this->instance('Illuminate\Container\Container', $this);
	}

	/**
	 * Register all of the base service providers.
	 *
	 * @return void
	 */
	protected function registerBaseServiceProviders()
	{
		foreach (array('Event', 'Exception', 'Routing') as $name)
		{
			$this->{"register{$name}Provider"}();
		}
	}

	/**
	 * Register the exception service provider.
	 *
	 * @return void
	 */
	protected function registerExceptionProvider()
	{
		$this->register(new ExceptionServiceProvider($this));
	}

	/**
	 * Register the routing service provider.
	 *
	 * @return void
	 */
	protected function registerRoutingProvider()
	{
		$this->register(new RoutingServiceProvider($this));
	}

	/**
	 * Register the event service provider.
	 *
	 * @return void
	 */
	protected function registerEventProvider()
	{
		$this->register(new EventServiceProvider($this));
	}

上面幾個函式內容就是實際在建構式內執行的內容。


從建構式一直到上面為止的寫法確實很OOP,可讀性也很高,但我覺得好大一串...。

	/**
	 * Bind the installation paths to the application.
	 *
	 * @param  array  $paths
	 * @return void
	 */
	public function bindInstallPaths(array $paths)
	{
		$this->instance('path', realpath($paths['app']));

		// Here we will bind the install paths into the container as strings that can be
		// accessed from any point in the system. Each path key is prefixed with path
		// so that they have the consistent naming convention inside the container.
		foreach (array_except($paths, array('app')) as $key => $value)
		{
			$this->instance("path.{$key}", realpath($value));
		}
	}

	/**
	 * Get the application bootstrap file.
	 *
	 * @return string
	 */
	public static function getBootstrapFile()
	{
		return __DIR__.'/start.php';
	}

	/**
	 * Start the exception handling for the request.
	 *
	 * @return void
	 */
	public function startExceptionHandling()
	{
		$this['exception']->register($this->environment());

		$this['exception']->setDebug($this['config']['app.debug']);
	}

	/**
	 * Get or check the current application environment.
	 *
	 * @param  mixed
	 * @return string
	 */
	public function environment()
	{
		if (count(func_get_args()) > 0)
		{
			return in_array($this['env'], func_get_args());
		}

		return $this['env'];
	}

	/**
	 * Determine if application is in local environment.
	 *
	 * @return bool
	 */
	public function isLocal()
	{
		return $this['env'] == 'local';
	}

	/**
	 * Detect the application's current environment.
	 *
	 * @param  array|string  $envs
	 * @return string
	 */
	public function detectEnvironment($envs)
	{
		$args = isset($_SERVER['argv']) ? $_SERVER['argv'] : null;

		return $this['env'] = (new EnvironmentDetector())->detect($envs, $args);
	}

	/**
	 * 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';
	}

	/**
	 * Force register a service provider with the application.
	 *
	 * @param  \Illuminate\Support\ServiceProvider|string  $provider
	 * @param  array  $options
	 * @return \Illuminate\Support\ServiceProvider
	 */
	public function forceRegister($provider, $options = array())
	{
		return $this->register($provider, $options, true);
	}

	/**
	 * Register a service provider with the application.
	 *
	 * @param  \Illuminate\Support\ServiceProvider|string  $provider
	 * @param  array  $options
	 * @param  bool   $force
	 * @return \Illuminate\Support\ServiceProvider
	 */
	public function register($provider, $options = array(), $force = false)
	{
		if ($registered = $this->getRegistered($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) $provider->boot();

		return $provider;
	}

	/**
	 * Get the registered service provider instance if it exists.
	 *
	 * @param  \Illuminate\Support\ServiceProvider|string  $provider
	 * @return \Illuminate\Support\ServiceProvider|null
	 */
	public function getRegistered($provider)
	{
		$name = is_string($provider) ? $provider : get_class($provider);

		if (array_key_exists($name, $this->loadedProviders))
		{
			return array_first($this->serviceProviders, function($key, $value) use ($name)
			{
				return get_class($value) == $name;
			});
		}
	}

	/**
	 * Resolve a service provider instance from the class name.
	 *
	 * @param  string  $provider
	 * @return \Illuminate\Support\ServiceProvider
	 */
	public function resolveProviderClass($provider)
	{
		return new $provider($this);
	}

	/**
	 * Mark the given provider as registered.
	 *
	 * @param  \Illuminate\Support\ServiceProvider
	 * @return void
	 */
	protected function markAsRegistered($provider)
	{
		$this['events']->fire($class = get_class($provider), array($provider));

		$this->serviceProviders[] = $provider;

		$this->loadedProviders[$class] = true;
	}

	/**
	 * Load and boot all of the remaining deferred providers.
	 *
	 * @return void
	 */
	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 = array();
	}

	/**
	 * Load the provider for a deferred service.
	 *
	 * @param  string  $service
	 * @return void
	 */
	protected function loadDeferredProvider($service)
	{
		$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
	 * @return void
	 */
	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)
			{
				$instance->boot();
			});
		}
	}

	/**
	 * Resolve the given type from the container.
	 *
	 * (Overriding Container::make)
	 *
	 * @param  string  $abstract
	 * @param  array   $parameters
	 * @return mixed
	 */
	public function make($abstract, $parameters = array())
	{
		$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);
	}

	/**
	 * "Extend" an abstract type in the container.
	 *
	 * (Overriding Container::extend)
	 *
	 * @param  string   $abstract
	 * @param  \Closure  $closure
	 * @return void
	 *
	 * @throws \InvalidArgumentException
	 */
	public function extend($abstract, Closure $closure)
	{
		$abstract = $this->getAlias($abstract);

		if (isset($this->deferredServices[$abstract]))
		{
			$this->loadDeferredProvider($abstract);
		}

		return parent::extend($abstract, $closure);
	}

	/**
	 * Register a "before" application filter.
	 *
	 * @param  \Closure|string  $callback
	 * @return void
	 */
	public function before($callback)
	{
		return $this['router']->before($callback);
	}

	/**
	 * Register an "after" application filter.
	 *
	 * @param  \Closure|string  $callback
	 * @return void
	 */
	public function after($callback)
	{
		return $this['router']->after($callback);
	}

	/**
	 * Register a "finish" application filter.
	 *
	 * @param  \Closure|string  $callback
	 * @return void
	 */
	public function finish($callback)
	{
		$this->finishCallbacks[] = $callback;
	}

	/**
	 * Register a "shutdown" callback.
	 *
	 * @param  callable  $callback
	 * @return void
	 */
	public function shutdown(callable $callback = null)
	{
		if (is_null($callback))
		{
			$this->fireAppCallbacks($this->shutdownCallbacks);
		}
		else
		{
			$this->shutdownCallbacks[] = $callback;
		}
	}

	/**
	 * Register a function for determining when to use array sessions.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function useArraySessions(Closure $callback)
	{
		$this->bind('session.reject', function() use ($callback)
		{
			return $callback;
		});
	}

	/**
	 * 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;

		array_walk($this->serviceProviders, function($p) { $p->boot(); });

		$this->bootApplication();
	}

	/**
	 * Boot the application and fire app callbacks.
	 *
	 * @return void
	 */
	protected function bootApplication()
	{
		// 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);

		$this->booted = true;

		$this->fireAppCallbacks($this->bootedCallbacks);
	}

	/**
	 * 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(array($callback));
	}

	/**
	 * Run the application and send the response.
	 *
	 * @param  \Symfony\Component\HttpFoundation\Request  $request
	 * @return void
	 */
	public function run(SymfonyRequest $request = null)
	{
		$request = $request ?: $this['request'];

		$response = with($stack = $this->getStackedClient())->handle($request);

		$response->send();

		$stack->terminate($request, $response);
	}

這是入口檔案(public/index.php)初始化Laravel實體之後,唯一呼叫的函式。
「執行本次應用然後送出response給client端。」

wich函數是Laravel自定義的輔助函數:
Return the given object. Useful for method chaining constructors in PHP 5.3.x.

而getStackedClient則是回傳stacked HTTP kernel,將加密處理、cookie處理、session處理放進一個stack內準備給handle依序處理。關於這部份請參見Symphony框架:
The HttpKernel Component
Laravel的request與response程序就是利用Symphony的函式庫,完成一個按照步驟將request轉成response的過程。

最後按照Symphony函式庫的規範,send結果之後,將stack給terminate掉。

Symphony這樣設計是讓系統能夠儘快先send結果給使用者的瀏覽器,然後再執行一些像是發送email的緩慢程序。

入口檔案並沒有傳入任何request實體當參數。原作在此處保留了擴充性給進階使用者。


	/**
	 * Get the stacked HTTP kernel for the application.
	 *
	 * @return  \Symfony\Component\HttpKernel\HttpKernelInterface
	 */
	protected function getStackedClient()
	{
		$sessionReject = $this->bound('session.reject') ? $this['session.reject'] : null;

		$client = (new Builder)
                    ->push('Illuminate\Cookie\Guard', $this['encrypter'])
                    ->push('Illuminate\Cookie\Queue', $this['cookie'])
                    ->push('Illuminate\Session\Middleware', $this['session'], $sessionReject);

		$this->mergeCustomMiddlewares($client);

		return $client->resolve($this);
	}

	/**
	 * Merge the developer defined middlewares onto the stack.
	 *
	 * @param  \Stack\Builder
	 * @return void
	 */
	protected function mergeCustomMiddlewares(Builder $stack)
	{
		foreach ($this->middlewares as $middleware)
		{
			list($class, $parameters) = array_values($middleware);

			array_unshift($parameters, $class);

			call_user_func_array(array($stack, 'push'), $parameters);
		}
	}

	/**
	 * Register the default, but optional middlewares.
	 *
	 * @return void
	 */
	protected function registerBaseMiddlewares()
	{
		//
	}

	/**
	 * Add a HttpKernel middleware onto the stack.
	 *
	 * @param  string  $class
	 * @param  array  $parameters
	 * @return $this
	 */
	public function middleware($class, array $parameters = array())
	{
		$this->middlewares[] = compact('class', 'parameters');

		return $this;
	}

	/**
	 * Remove a custom middleware from the application.
	 *
	 * @param  string  $class
	 * @return void
	 */
	public function forgetMiddleware($class)
	{
		$this->middlewares = array_filter($this->middlewares, function($m) use ($class)
		{
			return $m['class'] != $class;
		});
	}

	/**
	 * Handle the given request and get the response.
	 *
	 * Provides compatibility with BrowserKit functional testing.
	 *
	 * @implements HttpKernelInterface::handle
	 *
	 * @param  \Symfony\Component\HttpFoundation\Request  $request
	 * @param  int   $type
	 * @param  bool  $catch
	 * @return \Symfony\Component\HttpFoundation\Response
	 *
	 * @throws \Exception
	 */
	public function handle(SymfonyRequest $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
	{
		try
		{
			$this->refreshRequest($request = Request::createFromBase($request));

			$this->boot();

			return $this->dispatch($request);
		}
		catch (\Exception $e)
		{
			if ($this->runningUnitTests()) throw $e;

			return $this['exception']->handleException($e);
		}
	}

在run函式中被呼叫。
由HttpKernelInterface所規範、實作request轉成response的函式。
先refreshRequest將request洗乾淨(???)
然後執行boot將整個應用程序「開機」。實際的轉換工作由dispatch負責。
前面提到的The HttpKernel Component,如果沒弄懂,會很難搞懂這邊為何要一層包一層。

	/**
	 * Handle the given request and get the response.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @return \Symfony\Component\HttpFoundation\Response
	 */
	public function dispatch(Request $request)
	{
		if ($this->isDownForMaintenance())
		{
			$response = $this['events']->until('illuminate.app.down');

			if ( ! is_null($response)) return $this->prepareResponse($response, $request);
		}

		if ($this->runningUnitTests() && ! $this['session']->isStarted())
		{
			$this['session']->start();
		}

		return $this['router']->dispatch($this->prepareRequest($request));
	}

被handle呼叫、實際進行request轉成response的函式。
先防衛性檢查主應用是否正在「下線維護」狀態,然後決定是否啟動session(???)。
最後由router決定將request丟往哪邊處理、並且回傳response。
參考:分配工作:Routing/Router.php

	/**
	 * Terminate the request and send the response to the browser.
	 *
	 * @param  \Symfony\Component\HttpFoundation\Request  $request
	 * @param  \Symfony\Component\HttpFoundation\Response  $response
	 * @return void
	 */
	public function terminate(SymfonyRequest $request, SymfonyResponse $response)
	{
		$this->callFinishCallbacks($request, $response);

		$this->shutdown();
	}

terminate函式只是為了執行'finish callback'而存在(如果有的話)。
然後再呼叫shutdown去執行'shutdown callback'。至於兩種callback的使用時機...大部份情況下是不會用到的。
可以參考:
Request Lifecycle
Understanding the Request Lifecycle

得到更多資訊。

	/**
	 * Refresh the bound request instance in the container.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @return void
	 */
	protected function refreshRequest(Request $request)
	{
		$this->instance('request', $request);

		Facade::clearResolvedInstance('request');
	}

	/**
	 * Call the "finish" callbacks assigned to the application.
	 *
	 * @param  \Symfony\Component\HttpFoundation\Request  $request
	 * @param  \Symfony\Component\HttpFoundation\Response  $response
	 * @return void
	 */
	public function callFinishCallbacks(SymfonyRequest $request, SymfonyResponse $response)
	{
		foreach ($this->finishCallbacks as $callback)
		{
			call_user_func($callback, $request, $response);
		}
	}

	/**
	 * 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);
		}
	}

	/**
	 * Prepare the request by injecting any services.
	 *
	 * @param  \Illuminate\Http\Request  $request
	 * @return \Illuminate\Http\Request
	 */
	public function prepareRequest(Request $request)
	{
		if ( ! is_null($this['config']['session.driver']) && ! $request->hasSession())
		{
			$request->setSession($this['session']->driver());
		}

		return $request;
	}

	/**
	 * Prepare the given value as a Response object.
	 *
	 * @param  mixed  $value
	 * @return \Symfony\Component\HttpFoundation\Response
	 */
	public function prepareResponse($value)
	{
		if ( ! $value instanceof SymfonyResponse) $value = new Response($value);

		return $value->prepare($this['request']);
	}

	/**
	 * Determine if the application is ready for responses.
	 *
	 * @return bool
	 */
	public function readyForResponses()
	{
		return $this->booted;
	}

	/**
	 * Determine if the application is currently down for maintenance.
	 *
	 * @return bool
	 */
	public function isDownForMaintenance()
	{
		return file_exists($this['config']['app.manifest'].'/down');
	}

	/**
	 * Register a maintenance mode event listener.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function down(Closure $callback)
	{
		$this['events']->listen('illuminate.app.down', $callback);
	}

	/**
	 * Throw an HttpException with the given data.
	 *
	 * @param  int     $code
	 * @param  string  $message
	 * @param  array   $headers
	 * @return void
	 *
	 * @throws \Symfony\Component\HttpKernel\Exception\HttpException
	 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
	 */
	public function abort($code, $message = '', array $headers = array())
	{
		if ($code == 404)
		{
			throw new NotFoundHttpException($message);
		}

		throw new HttpException($code, $message, null, $headers);
	}

	/**
	 * Register a 404 error handler.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function missing(Closure $callback)
	{
		$this->error(function(NotFoundHttpException $e) use ($callback)
		{
			return call_user_func($callback, $e);
		});
	}

	/**
	 * Register an application error handler.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function error(Closure $callback)
	{
		$this['exception']->error($callback);
	}

	/**
	 * Register an error handler at the bottom of the stack.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function pushError(Closure $callback)
	{
		$this['exception']->pushError($callback);
	}

	/**
	 * Register an error handler for fatal errors.
	 *
	 * @param  \Closure  $callback
	 * @return void
	 */
	public function fatal(Closure $callback)
	{
		$this->error(function(FatalErrorException $e) use ($callback)
		{
			return call_user_func($callback, $e);
		});
	}

	/**
	 * Get the configuration loader instance.
	 *
	 * @return \Illuminate\Config\LoaderInterface
	 */
	public function getConfigLoader()
	{
		return new FileLoader(new Filesystem, $this['path'].'/config');
	}

	/**
	 * Get the environment variables loader instance.
	 *
	 * @return \Illuminate\Config\EnvironmentVariablesLoaderInterface
	 */
	public function getEnvironmentVariablesLoader()
	{
		return new FileEnvironmentVariablesLoader(new Filesystem, $this['path.base']);
	}

	/**
	 * Get the service provider repository instance.
	 *
	 * @return \Illuminate\Foundation\ProviderRepository
	 */
	public function getProviderRepository()
	{
		$manifest = $this['config']['app.manifest'];

		return new ProviderRepository(new Filesystem, $manifest);
	}

	/**
	 * Get the service providers that have been loaded.
	 *
	 * @return array
	 */
	public function getLoadedProviders()
	{
		return $this->loadedProviders;
	}

	/**
	 * Set the application's deferred services.
	 *
	 * @param  array  $services
	 * @return void
	 */
	public function setDeferredServices(array $services)
	{
		$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]);
	}

	/**
	 * Get or set the request class for the application.
	 *
	 * @param  string  $class
	 * @return string
	 */
	public static function requestClass($class = null)
	{
		if ( ! is_null($class)) static::$requestClass = $class;

		return static::$requestClass;
	}

	/**
	 * Set the application request for the console environment.
	 *
	 * @return void
	 */
	public function setRequestForConsoleEnvironment()
	{
		$url = $this['config']->get('app.url', 'http://localhost');

		$parameters = array($url, 'GET', array(), array(), array(), $_SERVER);

		$this->refreshRequest(static::onRequest('create', $parameters));
	}

	/**
	 * Call a method on the default request class.
	 *
	 * @param  string  $method
	 * @param  array  $parameters
	 * @return mixed
	 */
	public static function onRequest($method, $parameters = array())
	{
		return forward_static_call_array(array(static::requestClass(), $method), $parameters);
	}

	/**
	 * Get the current application locale.
	 *
	 * @return string
	 */
	public function getLocale()
	{
		return $this['config']->get('app.locale');
	}

	/**
	 * Set the current application locale.
	 *
	 * @param  string  $locale
	 * @return void
	 */
	public function setLocale($locale)
	{
		$this['config']->set('app.locale', $locale);

		$this['translator']->setLocale($locale);

		$this['events']->fire('locale.changed', array($locale));
	}

	/**
	 * Register the core class aliases in the container.
	 *
	 * @return void
	 */
	public function registerCoreContainerAliases()
	{
		$aliases = array(
			'app'            => 'Illuminate\Foundation\Application',
			'artisan'        => 'Illuminate\Console\Application',
			'auth'           => 'Illuminate\Auth\AuthManager',
			'auth.reminder.repository' => 'Illuminate\Auth\Reminders\ReminderRepositoryInterface',
			'blade.compiler' => 'Illuminate\View\Compilers\BladeCompiler',
			'cache'          => 'Illuminate\Cache\CacheManager',
			'cache.store'    => 'Illuminate\Cache\Repository',
			'config'         => 'Illuminate\Config\Repository',
			'cookie'         => 'Illuminate\Cookie\CookieJar',
			'encrypter'      => 'Illuminate\Encryption\Encrypter',
			'db'             => 'Illuminate\Database\DatabaseManager',
			'events'         => 'Illuminate\Events\Dispatcher',
			'files'          => 'Illuminate\Filesystem\Filesystem',
			'form'           => 'Illuminate\Html\FormBuilder',
			'hash'           => 'Illuminate\Hashing\HasherInterface',
			'html'           => 'Illuminate\Html\HtmlBuilder',
			'translator'     => 'Illuminate\Translation\Translator',
			'log'            => 'Illuminate\Log\Writer',
			'mailer'         => 'Illuminate\Mail\Mailer',
			'paginator'      => 'Illuminate\Pagination\Factory',
			'auth.reminder'  => 'Illuminate\Auth\Reminders\PasswordBroker',
			'queue'          => 'Illuminate\Queue\QueueManager',
			'redirect'       => 'Illuminate\Routing\Redirector',
			'redis'          => 'Illuminate\Redis\Database',
			'request'        => 'Illuminate\Http\Request',
			'router'         => 'Illuminate\Routing\Router',
			'session'        => 'Illuminate\Session\SessionManager',
			'session.store'  => 'Illuminate\Session\Store',
			'remote'         => 'Illuminate\Remote\RemoteManager',
			'url'            => 'Illuminate\Routing\UrlGenerator',
			'validator'      => 'Illuminate\Validation\Factory',
			'view'           => 'Illuminate\View\Factory',
		);

		foreach ($aliases as $key => $alias)
		{
			$this->alias($key, $alias);
		}
	}

}

by 阿川先生