mmghv / dependency-pocket
PHP 中的依赖注入新形式
Requires
- php: >=5.4.0
This package is auto-updated.
Last update: 2023-05-20 20:41:08 UTC
README
PHP 中 "依赖注入" 的新形式
不要与依赖注入容器或类似Symfony DI 容器、Pimple或Laravel 服务容器混淆。
DependencyPocket不是以这种方式作为DI 容器,它更是一种新的依赖注入形式。本质上,有两种依赖注入方式:构造函数注入和setter 注入,我想将这种技术视为第三种类型,即口袋注入。
安装
使用 composer
composer require mmghv/dependency-pocket "~0.2"
使用
我们在类中使用这个口袋来持有依赖项,我们首先创建一个新的口袋
use mmghv\DependencyPocket; // ... $this->pocket = new DependencyPocket();
然后添加依赖项,我们首先定义它们(定义每个依赖项的名称和类型)
$this->pocket->define([ 'dep1', // allow any type 'dep2' => 'string', // primitive type 'dep3' => 'array', // primitive type 'dep4' => 'App\Model\Article' // class or interface ]);
然后设置我们的依赖项(设置依赖项的值)
$this->pocket->set([ 'dep1' => true, 'dep2' => 'some value', 'dep3' => [1, 2], 'dep4' => $myArticle ]);
然后当我们想要获取一个依赖项时,我们只需这样做
$dep = $this->pocket->get('myDep'); // or $dep = $this->pocket->myDep;
或者我们可以使用属性重载来轻松地从我们的类(或子类)访问依赖项
public function __get($name) { if ($this->pocket->has($name)) { return $this->pocket->get($name); } else { throw new \Exception("Undefined property: [$name]"); } }
何时使用及使用技巧
基本上,当你的类有很多依赖项,但并非所有这些依赖项都是必需的,你不想在构造函数中包含所有这些依赖项,但仍需要一种在子类和测试中轻松更改它们的方法时很有用。
想象一下,你有一个具有5个依赖项的类,但其中只有2个是必要的,其他3个可以设置为某些默认值。然后你扩展这个类,子类可以将这两个必要依赖项之一解析为默认值,并需要替换父类中的一个可选依赖项。你需要能够做到这一点,并希望你的最终类在构造函数中只有一个依赖项,但仍然能够从任何未来的子类中更改任何默认值,以及测试中能够模拟任何这些依赖项的能力。
使用DependencyPocket,你可以像下面这样实现这一点,而无需为每个依赖项使用setter,并且在测试中使用依赖项的模拟比使用setter 注入方法更容易
// Manager.php namespace App\Managers; use Illuminate\Contracts\Foundation\Application; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use mmghv\DependencyPocket; class Manager { protected $pocket; /** * Define class dependencies. */ protected function defineDependencies() { if ($this->pocket) { return true; } $this->pocket = new DependencyPocket(); $this->pocket->define([ 'app' => 'Laravel\Lumen\Application', 'model' => 'Illuminate\Database\Eloquent\Model', 'validator' => 'Illuminate\Contracts\Validation\Factory', 'request' => 'Illuminate\Http\Request', 'redirectUrl' => 'string', ]); } /** * Create new manager. * * @param Application $app * @param Model $model * @param array $dependencyPocket */ public function __construct(Application $app, Model $model, array $dependencyPocket = []) { $this->defineDependencies(); $this->pocket->set($dependencyPocket += [ 'app' => $app, 'model' => $model, 'validator' => $app->make('validator'), // default value 'request' => $app->make('request'), // default value 'redirectUrl' => $app->make('session')->previousUrl(), // default value ]); } }
// ArticleManager.php namespace App\Managers; use Illuminate\Contracts\Foundation\Application; use App\Models\Article; class ArticleManager extends Manager { /** * Define any additional class dependencies, Declare this function * only when you need to define new dependencies. */ protected function defineDependencies() { if (parent::defineDependencies()) { return true; } $this->pocket->define([ // define any new dependencies for this class ]); } /** * Create new article-manager. * * @param Application $app * @param array $dependencyPocket */ public function __construct(Application $app, array $dependencyPocket = []) { // always call this first $this->defineDependencies(); // default value for $model dependency $model = new Article(); // call parent construct and pass dependencies parent::__construct($app, $model, $dependencyPocket += [ 'validator' => new CustomValidator(), // replace default dependency value // add any new dependencies for this calss, needs to be defined first in 'defineDependencies()' ]); } }
然后当我们实例化ArticleManager类时,我们只需要传递一个依赖项,如下所示
$manager = new ArticleManager($app);
但我们也易于替换我们想要的任何默认依赖项,如下所示
$manager = new ArticleManager($app, [ 'model' => $anotherModel, 'validator' => $anotherValidator ]);
贡献
这是一个相对较新的技术,因此任何贡献(建议、对所使用技术的改进)都受到欢迎。使用PSR-2标准,并应在PR的情况下对任何更改进行测试。
许可 & 版权
版权所有 © 2016,Mohamed Gharib。在MIT许可下发布。