本文简易模拟实现一下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