nucleuslabs / dependency-injector
Requires
- php: ^5.6 || ^7.0
Requires (Dev)
- phpunit/phpunit: ^5.6
- symfony/var-dumper: ^2.7
README
与其他依赖注入器不同,这不是一个DI容器。它为您调用方法,用任何缺失的参数填充,而不是强迫您手动从容器中取出。
概述
零配置
$di = new DependencyInjector();
$di->construct(\DateTime::class); // constructs a new DateTime object using default parameters (i.e. current time)
$di->call('time'); // calls time()
不激动人心吗?真正的魔法发生在您使用类型提示参数时
class MyController {
private $req;
public function __construct(Request $req) {
dump(__METHOD__, $req);
$this->req = $req;
}
public function action(Response $res) {
dump(__METHOD__, $res);
}
}
class Request {
private $uri;
public function __construct(Uri $uri) {
dump(__METHOD__, $uri);
$this->uri = $uri;
}
}
class Response {
}
class Uri {
private $uriParts;
public function __construct($uriString = null) {
dump(__METHOD__, $uriString);
if(!strlen($uriString)) {
$this->uriParts = parse_url('http://username:password@hostname:9090/path?arg=value#anchor'); // TODO: get from $_REQUEST
} else {
$this->uriParts = parse_url($uriString);
}
}
}
$di = new \mpen\DI\DependencyInjector();
$di->call('\MyController::action');
这是它所做的事情
- 尝试调用
\MyController::action
- 注意到
\MyController::action
是非静态的,所以它尝试构造一个新的MyController
MyController
需要一个Request
,所以它尝试创建一个Request
需要一个Uri
Uri
需要$uriString
,但我们没有提供,所以它使用默认值(null
)- 现在我们有了完全实例化的
MyController
,我们可以调用->action()
,但它需要一个Response
- 构造一个
Response
并调用方法
如果还不清楚,下面是输出结果
Uri::__construct"
null
"Request::__construct"
Uri {#18
-uriParts: array:8 [
"scheme" => "http"
"host" => "hostname"
"port" => 9090
"user" => "username"
"pass" => "password"
"path" => "/path"
"query" => "arg=value"
"fragment" => "anchor"
]
}
"MyController::__construct"
Request {#14
-uri: Uri {#18
-uriParts: array:8 [
"scheme" => "http"
"host" => "hostname"
"port" => 9090
"user" => "username"
"pass" => "password"
"path" => "/path"
"query" => "arg=value"
"fragment" => "anchor"
]
}
}
"MyController::action"
Response {#7}
注册全局变量
在上面的例子中,如果我们有一个URI并想用这个而不是默认值,我们可以在应用生命周期的早期注册它
$di->registerGlobal('uriString', 'http://example.com:3000');
然后每次遇到名为 $uriString
的参数而没有其他值提供时,它将检查全局变量中是否存在,并使用它而不是默认值!
如果您想将 $_GET
和/或 $_POST
变量注册为全局变量,以便将它们用作控制器操作的默认值,例如(如果您使用MVC),则可以这样做。
注册类
或者,您也可以注册一个类
$di->registerObject(new Uri('http://google.com'));
现在当 DependencyInjector
遇到 Uri
时,它将使用您注册的实例,而不是尝试自己构造一个。
注册回调
如果您不想在需要之前实例化对象,并且想完全控制其创建方式,您可以注册一个回调
$di->registerCallback(Uri::class, function() {
return new Uri('https://bing.com');
});
注册接口
如果您的函数针对接口进行了类型提示,DI将如何知道要替换哪个类?为此,您可以注册一个接口!
$di->registerInterface(\Psr\Http\Message\RequestInterface::class, \GuzzleHttp\Psr7\Request::class);
请注意,您也可以使用此方法注册抽象类和继承类。
名称匹配
如果您不使用单例,怎么办?例如,如果您有两个不同的数据库连接?您可以使用 $namePatt
选项来注册它们,以便在参数名称与正则表达式模式匹配时注入。
$di->registerObject($appDb, '~app(?!\\p{Ll})~A');
$di->registerObject($pcsDb, '~pcs(?!\\p{Ll})~A');
现在当函数参数以 "app" 开头时,将提供 $appDb
,当参数以 "pcs" 开头时,将提供 $pcsDb
。
如果两个模式都不匹配,DI将像通常一样尝试自己构造数据库对象,这可能会失败,因为它不知道您的DSN。
如果您不希望发生这种情况,您始终可以 $di->registerCallback(YourDB::class, ...)
作为后备选项。在这种情况下,您可能只想抛出一个异常。虽然这个DI很强大,但它不能黑客您的数据库并从空中窃取您的凭据 :-)
更多...
查看单元测试以获取更多示例。
选项
选项 | 类型 | 默认 | 描述 |
---|---|---|---|
cacheObjects | bool | true | 为未来重用缓存构造的对象 |
全局变量 | 数组 | [] | 全局关键字参数(如果参数名匹配则自动注入) |
memoizeFunctions | bool | true | 未实现 |
memoizeMethods | bool | false | 未实现 |
coercePosArgs | bool | true | 如果位置参数不匹配,则隐式转换为正确的类型 |
coerceKwArgs | bool | true | 如果关键字参数不匹配,则隐式转换为正确的类型 |
coerceGlobals | bool | false | 如果全局变量不匹配,则隐式转换为正确的类型 |
coerceCallback | 可调用 | construct | 如果没有为特定类型注册回调,则用于强制转换的默认函数。如果未提供,将尝试使用构造函数,传入一个位置参数。 |
propagateKwArgs | bool | false | 匹配关键字参数与顶层调用(false )还是递归构造依赖项(true )? |
许可证
MIT.