Ray.Di is DI and AOP framework for PHP inspired by Google Guice.
ray-di / ray.di Goto Github PK
View Code? Open in Web Editor NEWA dependency injection framework for PHP
Home Page: https://ray-di.github.io
License: MIT License
A dependency injection framework for PHP
Home Page: https://ray-di.github.io
License: MIT License
Ray.Di is DI and AOP framework for PHP inspired by Google Guice.
the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition in bear/resource/src/BEAR/Resource/Request.php on line 148
This E_NOTICE error raised when serialized object holds un-autoloadable class. (=Deleted class. Generated aop class files in many case.)
Rather than user should maintained generated aop files not deleted, It's better that AOP class files are re-created when *unserialize' function detect aop class files are not available.
自動生成されたAOPファイルが消去された場合にアンシリアライズでオブジェクトが生成されない問題
This cause AOP class dir problem.
In the case of requesting an interface with Injector::getInstance() and bound class need dependency, it raise NotBound exception because of that dependency does not have proper configuration.
Hi,
I have found an issue with the installation of Ray.Di under windows. The problem is that composer creates the directory ray/aop with a lower case letter and the directory Ray/Di with an upper case letter at the beginning. This does works on linux due its case sensitive manner, but on windows composer creates only one directory ray due its case insensitive manner.
The problem is that the autoloader references to the directory Ray with an upper case letter. And this doesn't exists under windows. Because the packages are installed in ray/aop and ray/Di.
I think the best would be to let composer create only one directory ray for both packages.
Cheers,
Christian
Hi,
when I look through your code, I see several class_exists calls in the class AbstractModule. This forces all bindings to be loaded on definition time. I think this is bad, because if you have a huge list of bindings, then all classes will be loaded on definition time and not on binding time. So I think in most cases more classes will be load as actually needed.
What do you think?
Cheers,
Christian
Injector::create(new FooModule)->get('AbstractBar');
cause error
Fatal error: Cannot instantiate abstract class
should throw exception
This feature enable to bind interface to interface.
example 1)
bindings
$this->bind('Ray\Di\Mock\DbInterface')->to('Ray\Di\Mock\UserDb');
$this->bind('Ray\Di\Mock\ChildDbInterface')->to('Ray\Di\Mock\DbInterface');
consumer
$instance = $this->injector->getInstance('Ray\Di\Mock\ChildDbInterface');
// \Ray\Di\Mock\UserDb
example 2)
/**
* @Inject
*/
public function __construct(FooInterface $foo)
/**
* @Inject
* @Named("my_app")
*/
public function __construct(FooInterface $foo)
$this->bind('FooInterface')->toProvider('FooProvider'); // *1
$this->bind('FooInterface')->annotatedWith("my_app")->to('FooInterface');
Suppose binding of FooInterface changed (*1), @Inject @nAmed("my_app") binding stays in same.
for development purpose.
developer can know which object (with hash key) is injected, which setter method are called.
Here is the how to use di compiler.
$cache = new ApcCache;
$cacheKey = 'context-key';
$tmpDir = '/tmp';
$moduleProvider = function() {
return new DiaryAopModule;
};
$injector = DiCompiler::create($moduleProvider, $cache, $cacheKey, $tmpDir);
$injector->getInstance('Ray\Di\DiaryInterface');
DiCompiler's logger stored all necessary information to instantiate object graph such as constructor arguments, setter method and its arguments, init method, interceptors and on each Inject request.
Then Di compiler compile object with injection log without injector or binding module.
Please check the performance. It's fast.
exception message should be like a ...
typehint='FooInterface', annotate='bar_annotate' for $fooName in class 'FooClass'
Singleton scope is ignored in the case of some module installed with '$this to override bindings.
OK
$this->install(new ProvideModule\ResourceView\HalModule);
$this->install(new Package\Module\Database\Dbal\DbalModule);
NG
$this->install(new ProvideModule\ResourceView\HalModule);
$this->install(new Package\Module\Database\Dbal\DbalModule($this));
仕様なのかも知れませんが、一つのPHPファイルにクラスを記述すると、エラーが発生します。
(APCがOFFの場合にはエラーは発生しません。)
PHP Fatal error: Maximum function nesting level of '100' reached, aborting! in /test/vendor/Ray/Di/src/Ray/Di/ApcConfig.php on line 40
ApcConfigでのファイルの読み込み時に無限ループに陥ってしまっているように見えます。
エラーになるコードと、発生するエラーメッセージ:
https://gist.github.com/3127484
なお、同一のプログラムでも、下記のようにクラスごとにファイルを分割するとエラーにはなりません。
https://gist.github.com/3127506
Hi,
at this time the method Injector::getInstance() ends in a fatal error if an interface name is given. Instead it should return the bound instance to this interface.
Cheers,
Christian
I have the following setter injection code:
<?php
use Ray\Di\AbstractModule;
use Ray\Di\Injector;
use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;
class FooBar
{
protected $config;
/**
* @Inject
* @Named("config")
*/
public function setConfig($config)
{
$this->config = $config;
}
public function dump()
{
var_dump($this->config);
}
}
class AppModule extends AbstractModule
{
protected function configure()
{
$this->bind()
->annotatedWith('config')
->toInstance(['key' => 'value']);
}
}
$injector = Injector::create([new AppModule()]);
$obj = $injector->getInstance('FooBar');
$obj->dump();
Now if I execute the code.
The results is:
string(5) "value"
But I expect as below:
array(1) {
'key' =>
string(5) "value"
}
I think that is a bug in Ray\Di\Injector::setterMethod.
class Injector
{
// ...
/**
* @param array $setter
* @param $object
*/
private function setterMethod(array $setter, $object)
{
foreach ($setter as $method => $value) {
// does the specified setter method exist?
if (method_exists($object, $method)) {
if (!is_array($value)) {
// call the setter
$object->$method($value);
} else {
call_user_func_array([$object, $method], $value);
}
}
}
}
Thank you.
module
$this->bind('AInterface')->to(A);
retrieve
$a = $injector->getInstance('AInterface');
no problem.
module
$this->bind('AInterface')->toInstance(new A);
retrieve
$a = $injector->getInstance('AInterface');
this cause exception.
$this->install(new FooModule($this));
このようにモジュールをインストールすると現在のモジュールの設定がFooModuleに引き継がれた後、現在のモジュールにインポトされ設定が二重になってしまいます。
重複したDI設定は追加分が無視され想定した動作をしますが、インターセプターが銃重複する問題が解決できていません。
Because they don't share same container.
Injector in requetInjection() should use same container with client.module.
Install other module include "toInstance()" make binding duplication.
It works. but it's a wast of memory.
To know binding information, (string) $injector produce string like this;
bind('BEAR\Resource\ResourceInterface')->to('BEAR\Resource\Resource')
bind('')->annotatedWith('greeting_msg')->toInstance((string) Hola)
bind('Doctrine\Common\Annotations\Reader')->toProvider(BEAR\Framework\Module\Provider\CachedReaderProvider)
This enable to log in bootstrap for binding information.
class MainModule extends AbstractModule
{
protected function configure()
{
$this->install(new SomeModule(1));
$this->install(new SomeModule(2));
}
}
SomeModule(2)) is ignored. SomeModule(1) is installed.
in this example MainModule do nothing, because SomeModule is already install with arg 0.
class PreModule extends AbstractModule
{
protected function configure()
{
$this->install(new SomeModule(0));
$this->install(new MainModule);
}
}
You can override existing module setting, just install it before original module install.
NotBound exception thrown with having no bind. The exception message should be like below.
typehint='', annotate='package_dir' for $packageDir in class 'Sandbox\Resource\Page\Index'
Enable to remove parent::__construct();
when constructor take parameter.
before
public function __construct($context = 'prod')
{
$this->context = $context;
parent::__construct();
}
after
public function __construct($context = 'prod')
{
$this->context = $context;
}
@Inject
に合わせて独自のアノテーションを記述したセッターに対してインターセプターを呼び出すことができないかと試していた際に気がついたことを記述します。
まずモジュールコードは以下のようなものです。
$this->bindInterceptor(
$this->matcher->any(),
$this->matcher->logicalAnd(
$this->matcher->annotatedWith('...\Baz')
$this->matcher->annotatedWith('Ray\Di\Di\Inject'),
),
[$this->requestInjection('...')]
);
クライアントコードは以下のようなものです。
/**
* @Inject
* @Baz
*/
public function setFoo(Bar $bar)
{
...
}
これを実行すると、バインディングタイムにより異なる振る舞いを示すことがわかりました。
私の調べた範囲では、以下のことが原因らしいとわかりました。
質問は以下のとおりです。
@mackstar READMEにCachealbe class exampleを追加しました。チェックお願いします。
$this->bind('Ray\Di\Mock\DbInterface')->toProvider('Ray\Di\Mock\RndDbProvider')->in(Scope::SINGLETON);
is bound in prototype.
usage:
error_log('/path/to/di.log');
/path/to/di.log
[04:04:23] ray/di.install ref:1 class:Ray\Di\Mock\RndDb hash:N5gwFkg
[04:04:23] ray/di.install ref:2 class:Ray\Di\Mock\SingletonInterceptor hash:QDCIYWQ
[04:04:23] ray/di.depends ref:2 __construct:#1
[04:04:23] ray/di.compile class:Ray\Di\Mock\SingletonInterceptorConsumer
[04:04:23] ray/di.install ref:3 class:Ray_Di_Mock_SingletonInterceptorConsumer_cdf70d13ebe60c232d505be90746b9a1RayAop hash:JWJYEpA
[04:04:23] ray/di.aspect ref:3 getDb:#2
[04:04:23] ray/di.map ref:3 class:Ray\Di\Mock\SingletonInterceptorConsumer
[04:04:23] ray/di.get class:Ray\Di\Mock\SingletonInterceptorConsumer
[04:04:23] ray/di.compile class:Ray\Di\Mock\SingletonInterceptorConsumer2
[04:04:23] ray/di.install ref:4 class:Ray_Di_Mock_SingletonInterceptorConsumer_cdf70d13ebe60c232d505be90746b9a1RayAop hash:EzBZQwA
[04:04:23] ray/di.aspect ref:4 getDb:#2
[04:04:23] ray/di.map ref:4 class:Ray\Di\Mock\SingletonInterceptorConsumer
[04:04:23] ray/di.install ref:5 class:Ray_Di_Mock_SingletonInterceptorConsumer2_cdf70d13ebe60c232d505be90746b9a1RayAop hash:M5iYl5E
[04:04:23] ray/di.aspect ref:5 getDb:#2
[04:04:23] ray/di.map ref:5 class:Ray\Di\Mock\SingletonInterceptorConsumer2
[04:04:23] ray/di.get class:Ray\Di\Mock\SingletonInterceptorConsumer2
.install
- Add instance to dependency container..depends
- Setter method name and parameters as dependency reference number (or scalar type)..aspect
- bound method name and bound interceptor object reference number.map
- map class name to reference number.get
- get instance from compiler (user access)Hi,
here is a list of some suggested improvements for the lib.
Cache:
Only APC is supported(Why not using Doctrine\Common\Cache)
I've observed a strange caching behaviour. It seems that the lib caches sometimes an inconsistent state. This is reproducible with the following steps:
If I clear the cache and reload the page then it works as expected.
Inject Annotation
The Inject annotation cannot be loaded with an own autoloader implementation. It seems that the doctrine annotation implementation relies on the doctrine autoloader. As a workaround I must include the annotation by hand.
This is the error:
Fatal error: Uncaught exception 'Doctrine\Common\Annotations\AnnotationException' with message '[Semantical Error] The annotation "@ray\Di\Di\Inject" in class util\affiliate\Broker does not exist, or could not be auto-loaded
If the Inject annotation is missing, the lib throws a NotBinded exception. I think in this case it should throw an exception that the annotation is missing.
This is the error:
Fatal error: Uncaught exception 'Ray\Di\Exception\NotBinded' with message 'Bind not found. argument #0($userDAO) in
The FQN @Ray\Di\Di\Inject
does not work. Only the short form Inject
does work as definition. I rather thought that doctrine supports the fully FQN syntax.
Binding
<?php
// Doesn't work
$this->bind('\model\daos\UserDAO')->to('\model\daos\DbUserDAO');
// Does work
$this->bind('model\daos\UserDAO')->to('\model\daos\DbUserDAO');
This is the error:
Uncaught exception 'Ray\Di\Exception\Binding' with message 'model\daos\UserDAO:*'
echo $injector
outputs binding log like this.
bind: annotatedWith:app_dir toInstance:'/Users/akihito/git/BEAR.Package/apps/Sandbox'
bind: annotatedWith:tmp_dir toInstance:'/Users/akihito/git/BEAR.Package/apps/Sandbox/data/tmp'
bind: annotatedWith:log_dir toInstance:'/Users/akihito/git/BEAR.Package/apps/Sandbox/data/log'
bind: annotatedWith:package_dir toInstance:'/Users/akihito/git/BEAR.Package'
bind: annotatedWith:sunday_dir toInstance:'/Users/akihito/git/BEAR.Package/vendor/bear/sunday'
bind:BEAR\Sunday\Extension\Application\AppInterface to:Sandbox\App
bind:BEAR\Sunday\Extension\ApplicationLogger\ApplicationLoggerInterface to:BEAR\Package\Provide\ApplicationLogger\ApplicationLogger
bind:BEAR\Resource\LogWriterInterface toProvider:BEAR\Package\Provide\ApplicationLogger\ResourceLog\WritersProvider
bind:Ray\Di\LoggerInterface to:BEAR\Package\Provide\Application\DiLogger
echo $injector->getLogger()
outputs binding log like this.
class:BEAR\Resource\SignalParam __construct:Aura\Signal\Manager#singleton, BEAR\Resource\Param#singleton
class:Sandbox\Params\FakeTime
class:Sandbox\Params\CurrentTime
class:BEAR\Sunday\Module\Provider\ApcCacheProvider setTmpDir:(string) /Users/akihito/git/BEAR.Package/apps/Sandbox/data/tmp
class:BEAR\Resource\Module\SchemeCollectionProvider setAppName:(string) Sandbox setInjector:Ray\Di\Injector#prototype
class:BEAR\Resource\Factory __construct:BEAR\Resource\SchemeCollection#singleton setSchemeCollection:BEAR\Resource\SchemeCollection#singleton
class:BEAR\Sunday\Module\Code\CachedReaderProvider
class:Guzzle\Parser\UriTemplate\UriTemplate
class:BEAR\Resource\Linker __construct:Doctrine\Common\Annotations\CachedReader#singleton, , Guzzle\Parser\UriTemplate\UriTemplate#singleton
class:BEAR\Resource\NamedParams __construct:BEAR\Resource\SignalParam#singleton
class:BEAR\Package\Provide\ApplicationLogger\ResourceLog\WritersProvider setLogDir:(string) /Users/akihito/git/BEAR.Package/apps/Sandbox/data/log
class:BEAR\Resource\Logger setWriter:BEAR\Package\Provide\ApplicationLogger\ResourceLog\Writer\Collection#prototype
class:BEAR\Sunday\Module\Resource\ResourceLoggerProvider setLoggerClassName:BEAR\Resource\Logger#prototype
Hi,
I cannot get the dev-master branch through composer. It installs always the 1.0.0.-beta1 tag. I have tested this with a clean composer.json.
{
"require": {
"ray/di": "dev-master"
},
"minimum-stability": "dev"
}
Installing dependencies
Cheers,
Christian
Hi,
I have the following binding defined:
<?php
$this->bind('com\mohiva\common\util\EventDispatcher')
->to('\com\mohiva\common\util\DefaultEventDispatcher')
->in(Scope::SINGLETON);
Now if I try to get the instance twice, I get two different objects.
<?php
$eventDispatcher = $injector->getInstance('com\mohiva\common\util\EventDispatcher');
var_dump(spl_object_hash($eventDispatcher));
$eventDispatcher = $injector->getInstance('com\mohiva\common\util\EventDispatcher');
var_dump(spl_object_hash($eventDispatcher));
Prints:
string(32) "00000000431a50c2000000002e406287"
string(32) "00000000431a50c4000000002e406287"
Cheers,
Christian
Objects having @preDestory
notification are kept in memory until end of request execution because they are stored in $preDestroyObjects
property of Injector
class.
This is not a problem if the object is a singleton.
However, when the object is not a singleton, the behavior means memory leak.
// compile injection
$injector = Injector::create([new DiaryModule]);
$diCompiler = new DiCompiler($injector, new CompileLogger(new Logger()));
$diCompiler->compile('Ray\Di\DiaryInterface');
// run-time injection
$instance = $diCompiler->getInstance('Ray\Di\DiaryInterface');
$DiCompiler->compile('Ray\Di\DiaryInterface');
tracks all dependency injection in Injector
then save as injection log. DI compiler
can inject dependencies with it but withoutModule
(binding information) . It is run-time injection. It save memory usage and boost the performance without object caching.
Limitation
toInsntace
binding can't take object parameter (because it can't track instance creation)@PostConstruction
is @PreDestroy
is not currently supported.use Ray\Di\CacheableModule;
$moduleProvider = function() {return new FooModule;};
$module = new CacheableModule($moduleProvider, $cacheKey);
$injector = Injector::create([$module], $cache, $tmpDir);
$app = $injector->getInstance('BEAR\Sunday\Extension\Application\AppInterface');
Extra caching for aop binding.
AbstractModule::enableInvokeCache();
I have an implementation which checks in its constructor if a static property is set or not. Based on this check the property gets initialized with an object instance. It's an simple singleton check. Now after the instance gets retrieved from cache the property is always null, because the constructor will never been executed.
class EventBusImpl implements EventBus {
private static $dispatcher = null;
public function __construct() {
if (self::$dispatcher === null) {
self::$dispatcher = new DefaultEventDispatcher();
}
}
public function getDispatcher() {
return self::$dispatcher;
}
}
The method getDispatcher returns always null.
The binding its the following:
$this->bind('\util\EventBus')->to('\util\EventBusImpl');
インターセプターがバインドされたクラスが一時ファイルとしてテンポラリディレクトリに作成されるのですが、Injector#getInstance()する度に作成されてしまい困っています。
テストで実装したソースはこちらです。
https://github.com/vectorxenon/Ray.Di-aop-test
main.phpを実行すると次のようなファイルが作成されます。
$ ls /tmp/*.php
/tmp/YukiServiceFooServiceRay00000000086d911800000000331f3cbbAop.php
/tmp/YukiServiceFooServiceRay00000000086d917f00000000331f3cbbAop.php
何か設定方法/使い方がおかしいのでしょうか?
Following Injektor's vein of performance improvements
Potential output from compiler
/**
* Autogenerated. Do not modify.
**/
class \ApplicationNamespace\Ray\Di\Compiled\SomeModelFactory extends SomeFactoryAbstract {
/* @var InjectionMethod[] */
private $injectionMethods;
/* @var string[] */
private $postConstructMethods = [ 'onInit' ];
/**
* @return SomeModel
*/
public function instantiate() {
//Define injections
$table = $this->getInstanceOf( 'SomeZendTable' );
$this->injectionMethods[] = new InjectionMethod( 'injectTables', [ $table ] );
$instance = new SomeModel();
//handle injections
$this->inject();
//run postconstruct methods
$this->postConstruct();
$this->isSingleton( 'SomeModel', $instance );
return $instance;
}
}
class Foo{
public function __construct(ConcreteClass $obj)
{
$this->obj = $obj;
}
}
var_dump(($injector->getInstance('Foo')->obj) instanceof ConcreteClass);
should be true.
Been testing out the DiCompiler in a part legacy code part MVC, part DI-MVC environment.
Here is the issue I am having
/**
* @Scope("Singleton")
**/
class SingletonDep {
/**
* @PostConstruct
**/
public function onInit()
{
define ( 'SOME_CONSTANT', $this->compute() );
}
}
SingletonDep
is required by multiple classes, but it doesn't seem to only be constructed once.
object(ErrorException)[279]
protected 'message' => string 'Constant SOME_CONSTANT already defined' (length=34)
private 'string' (Exception) => string '' (length=0)
protected 'code' => int 8
protected 'file' => string '/vagrant/modules/***/***/***.php' (length=37)
protected 'line' => int 89
private 'trace' (Exception) =>
Injector is used in case of DI compiler encountered compile error.
This is defensive function. If user doesn't have a DI compiler compatible binding (like toInsntace(new Foo)
), Di Compiler still get instance with original injector without error.
Exception thrown if BindInterface
has no binding as shown case.
/**
* @Inject
*/
public function __construct(
ContainerInterface $container,
BindInterface $bind = null
)
The default value SHOULD be used when the parameter no proper binding.
I'm trying to do this:
$injector = \Ray\Di\DiCompiler::create(function() { return new Module; }, $cache, 'ray', './tmp');
$a = $injector->getInstance('A');
$a1 = $injector->getInstance('A');
var_dump($a === $a1);
Unfortunately the behaviour is different to using the injector alone:
$injector = Ray\Di\Injector::create([new Module]);
$a = $injector->getInstance('A');
$a1 = $injector->getInstance('A');
var_dump($a === $a1);
using DiCompiler returns true, using Injector returns false. Is it possible to fetch a new instance of an object from a compiled container?
Injector::create()にて指定するModuleクラスで悩んでいます。
現在はインジェクトするクラス毎にModuleクラスを作るようにして、最後に生成するクラス用のModuleを作っています。
ですが、次のように同じクラスだけど違うパラメータのものをインジェクトしたい場合もあり、同じクラスで2つのModuleクラスを作る場合があります。
class Base
{
public $name;
public function __construct($name)
{
$this->name = $name;
}
}
class Foo extends Base {}
class Bar extends Base {}
class Baz extends Base {}
class FooModule extends AbstractModule
{
protected function configure()
{
$this->bind('Foo')->toInstance(new Foo('foo'));
}
}
class HogeModule extends AbstractModule
{
protected function configure()
{
$this->bind('Foo')->toInstance(new Foo('hoge'));
}
}
class BarModule extends AbstractModule
{
protected function configure()
{
$this->bind('Bar')->toInstance(new Bar('bar'));
}
}
class FugaModule extends AbstractModule
{
protected function configure()
{
$this->bind('Bar')->toInstance(new Bar('fuga'));
}
}
class BazModule extends AbstractModule
{
protected function configure()
{
$this->bind('Baz')->toInstance(new Baz('baz'));
}
}
class FooBarService
{
/**
* @var Foo
*/
private $foo;
/**
* @var Bar
*/
private $bar;
/**
* @Inject
* @param Foo $foo
* @param Bar $bar
*/
public function __construct(Foo $foo, Bar $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
public function dump()
{
var_dump($this->foo->name, $this->bar->name);
}
}
class HogeFugaBazService
{
/**
* @var Foo
*/
private $foo;
/**
* @var Bar
*/
private $bar;
/**
* @var Baz
*/
private $baz;
/**
* @Inject
* @param Foo $foo
* @param Bar $bar
* @param Baz $baz
*/
public function __construct(Foo $foo, Bar $bar, Baz $baz)
{
$this->foo = $foo;
$this->bar = $bar;
$this->baz = $baz;
}
public function dump()
{
var_dump($this->foo->name, $this->bar->name, $this->baz->name);
}
}
class FooBarServiceModule extends AbstractModule
{
protected function configure()
{
$this->install(new FooModule());
$this->install(new BarModule());
}
}
class HogeFugaBazServiceModule extends AbstractModule
{
protected function configure()
{
$this->install(new HogeModule());
$this->install(new FugaModule());
$this->install(new BazModule());
}
}
$injector = Injector::create(
[
new HogeFugaBazServiceModule(),
new FooBarServiceModule(),
]
);
$injector->getInstance('HogeFugaBazService')->dump();
この場合の出力結果は
string(3) "foo"
string(3) "bar"
string(3) "baz"
という風に、HogeFugaBazServiceModuleでFooとBarにバインドされているものがFooBarServiceModuleでFooとBarにバインドされているもので上書きされてしまいます。
これはInjector::create()で指定するModuleクラスの順番に依存しているので、Moduleクラスの指定を逆にすると意図通りの結果になります。
$injector = Injector::create(
[
new FooBarServiceModule(),
new HogeFugaBazServiceModule(),
]
);
$injector->getInstance('HogeFugaBazService')->dump();
string(4) "hoge"
string(4) "fuga"
string(3) "baz
もっとModuleクラスが増えてくると、どこでバッティングしているか分からなくなると思うのですが、どのような解決策があるでしょうか?
僕が今のところ考えているのは次の2つです。
コンテナから生成するクラス用のModuleクラスを指定すると、中で何が指定されていて、何がバッティングするか分からないので、バインドが1つだけのModuleクラスを指定したほうがいいのではないか。
$injector = Injector::create(
[
new FooModule(),
new BarModule(),
new BazModule(),
new HogeModule(),
new FugaModule(),
]
);
それぞれのModuleだけ定義してもクラスへのバインドはバッティングしてしまうので、Namedアノテーションも利用してバッティング率を下げてみるのはどうか。
HogeFugaBazServiceの定義は次のように修正
/**
* @Inject
* @Named("hoge=Hoge,fuga=Fuga")
* @param Foo $foo
* @param Bar $fuga
* @param Baz $baz
*/
public function __construct(Foo $hoge, Bar $fuga, Baz $baz)
HogeModuleとFugaModuleを次のように修正
class HogeModule extends AbstractModule
{
protected function configure()
{
$this->bind('Foo')->annotatedWith('Hoge')->toInstance(new Foo('hoge'));
}
}
class FugaModule extends AbstractModule
{
protected function configure()
{
$this->bind('Bar')->annotatedWith('Fuga')->toInstance(new Bar('fuga'));
}
}
こうやってしまうと、今度は、Injector::createで指定されているModuleとコンテナから生成するクラスの因果関係が分かりづらくなってしまいます。
Moduleクラスをどう使っていったらいいのでしょうか。
Current Ray\Di\Annotation class use 'Doctrine\Common\Annotations\DocParser' (lower level library for Reader).
The reason for this is.
However,
This cause more confusion to understand and usage with mixed with regular Doctrine annotation.SHOULD be changed to use 'Doctrine\Common\Annotations\Reader'.
現在Annotationクラスで'Doctrine\Common\Annotations\DocParserを使っていますが、通常のDoctrine Annotationを使った時の使用と動作の混乱を避けるために、\Common\Annotations\Readerを使うべきです。
I would like to hear from someone who is familiar with Guice about Ray.Di's exception names.
@akkie, If you have any suggestion, please give us a PR,
Cheers.
how to create Ray\Di\FakeCarInterface-*
instance is stored in Container::$container
as follows.
Ray\Di\FakeCarInterface-*
インスタンスをどのように生成するかは以下のようにContainer::$container
に格納されています。
DI Compiler
はこの情報をPHPコードに変更します。
<?php
// constructor injection
$instance = new Ray\Di\FakeCar(
new FakeEngine()
);
// setter injection
$instance->setMirrors(
new FakeRightMirror();
new FakeLeftMirror();
)
// @PostConstruct
$instance->init();
プロバイダーにアサインされてるものはプロバイダーのコードを生成します。
$provider = new FakeRightMirrorProvider;
$provider->get();
シングルトンの場合はRay|Di|Singleton
を利用します。
// singleton
Ray\Di\Singleton::to('Ray\Di\FakeItem');
Ray\Di\Singleton::toProvider('Ray\Di\FakeItemProvider');
<?php
class Core extends Ray\Di\AbstractModule {
public function configure()
{
$this->bind( 'Zend_Db_Adapter_Abstract' )
->toProvider( '\Devls\Db\DiProvider' );
}
}
<?php
namespace Devls\Db;
class DiProvider implements \Ray\Di\ProviderInterface {
/**
* @return \Zend_Db_Adapter_Abstract
*/
public function get()
{
return \Devls_Db_ConnectionManager::getCentralConnection();
}
}
<?php
use Ray\Di\Di\Inject;
use Ray\Di\Di\PostConstruct;
use Ray\Di\Di\Scope;
/**
* @Scope("Singleton")
*/
abstract class Core_Table_Base extends Zend_Db_Table_Abstract {
public function __construct( \Zend_Db_Adapter_Abstract $db )
{
parent::__construct( $db );
}
}
Error occurs:
Valid interface is not found. (array ?) Injection requested at argument #0 $config in Zend_Db_Adapter_Abstract constructor.
It's trying to inject to the Zend_Db_Adapter_Abstract constructor, which means that it has ignored the existence of the provider.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.