Home » Code » Laravel中的Container、Service Provider、Contracts、Facades

Laravel中的Container、Service Provider、Contracts、Facades

本文简易模拟实现一下Laravel里边标题提到的概念。以编写一个加密功能为示例,最终实现的是Hash::make(‘123456’)。

Container就是IoC容器,也称服务容器,参考上一篇。整个Laravel应用$app,就是Container的一个实例。它的类是Application,位于\Illuminate\Foundation\Application。我简易的写一个如下:

namespace Application;

use Container\Container;

class Application extends Container
{
	public function registerProvider(array $providers)
	{
		foreach ($providers as $provider) {
			(new $provider($this))->register();
		}
	}

	public function setAlias(array $alias)
	{
		foreach ($alias as $key => $value) {
			class_alias($value, $key);
		}
	}
}

接着直接实例化一个应用$app = new Application(),有了应用就需要注册各种默认的服务提供者,比如路由解析、数据库之类的服务的提供者,也就是往容器里边注入依赖,一个意思。在这里,我只注册一个HashServiceProvider得了。需要注册哪些服务提供者在Laravel中是写在config/app.php中的providers数组中,我简单的直接写在入口文件:

$app = new Application();

$providers = array(
	'hashing\HashServiceProvider',
);

$app->registerProvider($providers);

定义一个服务,首先需要定义契约,也就是接口,规定这个服务需要提供什么功能。这里的HashServiceProvider需要的功能就定义在Hasher.php这个Contracts中:

namespace Contracts;

Interface Hasher
{
	public function make($value);

	public function verify($value, $hashed_value);
}

简单的就两个功能,加密以及验证。有了接口就得有实现,MD5Hasher.php实现这个接口:

namespace Hashing;

use Contracts\Hasher;

class MD5Hasher implements Hasher
{
	public function make($value)
	{
		return md5($value);
	}

	public function verify($value, $hashed_value)
	{
		return md5($value) === $hashed_value;
	}
}

接下来需要将这个实现注册为服务提供者。先来看一下服务提供者的基类:

namespace ServiceProvider;

abstract class ServiceProvider
{
	protected $app;

	public function __construct($app)
	{
		$this->app = $app;
	}

	abstract public function register();
}

它是一个抽象类,子必须实现register注册功能,除此外本身没有什么功能,就是将容器存到它的一个属性上而已。每一个服务提供者都保持着对$app容器的引用,以方便的注入与取出依赖。对于具体的HashServiceProvider,它需要实现register方法完成依赖注入:

namespace hashing;

use ServiceProvider\ServiceProvider;

class HashServiceProvider extends ServiceProvider
{
	public function register()
	{

		$this->app->singleton('hash', function() {
			return new MD5Hasher();
		});
	}
}

至此,服务提供者已经注入,直接取出就可以使用了:$hashed_password = $app->make(‘hash’)->make(‘123456’)。但这样子太麻烦,请出最后的门面Facade来美化,最后目标是实现Hash::make(‘123456’)这样子使用。

对于Facade基类Facade.php:

namespace Facade;

class Facade
{
	protected static $app;

	protected static function resolveFacadeInstance()
	{
		$name = static::getFacadeAccessor();
		return static::$app->make($name);
	}

	protected static function getFacadeAccessor()
	{
		die('must be implemented by sub class');
	}

	public static function setFacadeApplication($app)
	{
		static::$app = $app;
	}

	public static function __callStatic($method, $args)
	{
		$instance = static::resolveFacadeInstance();
		return call_user_func_array(array($instance, $method), $args);
	}
}

它主要有两个方法,一个是将全局容器$app注入到它属性上的set方法,另一个就是由子类具体的“门面”实现的提供容器入口的getFacadeAccessor()。比如这里的Hash.php:

namespace Facade;

use Facade\Facade;

class Hash extends Facade
{
	public static function getFacadeAccessor()
	{
		return 'hash';
	}
}

有了门面入口,使用就简单了:$hashed_password = Facade\Hash::make(‘123456’)。但是,我们希望还能再简单一点,也就是Hash::make(‘123456’),简单,给个别名即可。这在Laravel中别名是写在config/app.php中的alias数组,现在我也直接写到入口了,我们的入口变成:

$app = new Application();

$providers = array(
	'hashing\HashServiceProvider',
);

$app->registerProvider($providers);

Facade\Facade::setFacadeApplication($app);

$alias = array(
	'Hash' => 'Facade\Hash',
);
$app->setAlias($alias);

现在,我们终于可以直接Hash::make(‘123456’)了。回顾这个过程,创建容器->实现接口成为服务->注册成为服务提供者->设置门面->使用。将上边的所有文件从入口引入,完整的入口如下:

require 'container.php';//Container\Container,容器

require 'Application.php';//Application\Application,应用程序,容器的子类

require 'Hasher.php';//Contracts\Hasher,契约/接口

require 'ServiceProvider.php';//ServiceProvider\ServiceProvider,服务提供者基类

require 'HashServiceProvider.php';//hashing\HashServiceProvider,hash服务提供者

require 'MD5Hasher.php';//Hashing\MD5Hasher,Hasher契约的一个具体实现,这是使用md5实现

require 'Facade.php';//Facade\Facade,门面基类,实现根据入口名从容器获取对象

require 'Hash.php';//Facade\Hash,门面子类,返回从容器获取对象的入口名

use Application\Application;

$app = new Application();

$providers = array(
	'hashing\HashServiceProvider',
);

$app->registerProvider($providers);

Facade\Facade::setFacadeApplication($app);

$alias = array(
	'Hash' => 'Facade\Hash',
);
$app->setAlias($alias);

$hashed_password = Hash::make('123456');

var_dump($hashed_password);//string(32) "e10adc3949ba59abbe56e057f20f883e"

以上,即是依靠这些天看Laravel文档得到的经验进行的简单模拟。可以看到Laravel中使用了很多高大上的概念,也因此让人觉得不好上手(以上也是我参考其他文章自己猜的,对不对还不知道)。玩这么多到底有没有用呢,个人是不清楚,但它确实是国外最受欢迎的框架。至少,它每个功能每个路由都要明确注册,确实是有利于后期维护的。

参考链接:
http://yansu.org/2014/12/06/ioc-and-facade-in-laravel.html
http://ju.outofmemory.cn/entry/144667

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.