Giter Site home page Giter Site logo

ray.di's Introduction

ray.di's People

Contributors

andrew-demb avatar craigjbass avatar fivestar avatar ggnet avatar harikt avatar holyshared avatar jamolkhon avatar koriym avatar kuma-guy avatar kumamidori avatar lorenzo avatar mackstar avatar madapaja avatar mugeso avatar naokitsuchiya avatar nishigori avatar sasezaki avatar scrutinizer-auto-fixer avatar tudborg avatar vlakarados avatar yuya-takeyama avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ray.di's Issues

__PHP_Incomplete_Class E_NOTICE when aop files are deleted

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ファイルが消去された場合にアンシリアライズでオブジェクトが生成されない問題

Composer does not handle autoloading correct

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

Avoid class_exists calls on explicit binding

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

NotInstantiable exception

Injector::create(new FooModule)->get('AbstractBar');

cause error

Fatal error: Cannot instantiate abstract class

should throw exception

Interface to interface binding

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.

Get injecton logger

for development purpose.

developer can know which object (with hash key) is injected, which setter method are called.

Di Compiler

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.

Singleton scope doesn't work with overode module

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));

APC=on時にgetInstance()でエラー

仕様なのかも知れませんが、一つの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

Cannot inject array value on setter injection

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.

Bound instance can't be taken by injector

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.

AbstracModule::install() duplicates module settings.

$this->install(new FooModule($this));

このようにモジュールをインストールすると現在のモジュールの設定がFooModuleに引き継がれた後、現在のモジュールにインポトされ設定が二重になってしまいます。
重複したDI設定は追加分が無視され想定した動作をしますが、インターセプターが銃重複する問題が解決できていません。

binding duplication

Install other module include "toInstance()" make binding duplication.
It works. but it's a wast of memory.

Get binding information by (string) injector.

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.

Ignore sencond or lator module install.

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 does'nt have target class

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'

Lazy module activation

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;
    }

Interceptors are not called for setter injection at run-time but called at compile-time

@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)
{
    ...
}

これを実行すると、バインディングタイムにより異なる振る舞いを示すことがわかりました。

  1. コンパイルタイム - オブジェクト作成時にユーザーのインターセプターが呼び出される。意図した振る舞い。
  2. ランタイム - オブジェクト作成時にユーザーのインターセプターは呼び出されない。意図しない振る舞い。

私の調べた範囲では、以下のことが原因らしいとわかりました。

  1. コンパイルタイム: Injector::getInstance() のセッターが呼ばれる前にアスペクトが織り込まれているため、オブジェクト作成時にユーザーのインターセプターが呼び出される。
  2. ランタイム: DependencyFactory::get() では、先にセッターが呼ばれインターセプターはあとで織り込まれるため、オブジェクト作成時にユーザーのインターセプターは呼び出されない。

質問は以下のとおりです。

  1. コンパイルタイムおよびランタイムで振る舞いが異なるのは仕様かどうか?
  2. 仕様でない(バインディングタイムによらず同じ振る舞いが仕様である)場合、以下のいずれが正しいのか?
    • インジェクション時にインターセプター呼び出しを行う
    • インジェクション時にインターセプター呼び出しを行わない

Translation

@mackstar READMEにCachealbe class exampleを追加しました。チェックお願いします。

Di compiler log

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)

Some improvements

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:

    1. Create correct bindings
    2. Remove Inject annotation
    3. Clear cache
    4. Reload(must show an error - Uncaught exception 'Ray\Di\Exception\NotBinded' with message 'Bind not found. argument #0...)
    5. Add Inject annotation
    6. Relaod(shows still the exception)

    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

  • It isn't possible to declare the binding with a leading back slash:
<?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:*'

Injection logger

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

Cannot get latest version with composer

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

  • Installing doctrine/common (2.3.x-dev bb0aebb)
    Cloning bb0aebbf234db52df476a2b473d434745b34221c
  • Installing ray/aop (dev-master 2d118d3)
    Cloning 2d118d33454a2c2006b83a2d62ae9270eea33413
  • Installing aura/di (dev-master 8730acf)
    Cloning 8730acf19625fb74fca9f5ec8a0f80012c754442
  • Installing ray/di (dev-master 1.0.0-beta1)
    Cloning 1.0.0-beta1

Cheers,
Christian

Singleton scope doesn't work

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

preDestroy notification causes memory leak

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.

DI Compiler Injection

//  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)
  • AOP is not currently supported.
  • @PostConstruction is not currently supported.
  • @PreDestroy is not currently supported.

[#73] Cacheable Module

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();

A cached dependency doesn't call its constructor

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');

bindInterceptor()したクラスを使うときに、毎回/tmpにクラスファイルが作られてしまう

インターセプターがバインドされたクラスが一時ファイルとしてテンポラリディレクトリに作成されるのですが、Injector#getInstance()する度に作成されてしまい困っています。

テストで実装したソースはこちらです。
https://github.com/vectorxenon/Ray.Di-aop-test

main.phpを実行すると次のようなファイルが作成されます。

$ ls /tmp/*.php
/tmp/YukiServiceFooServiceRay00000000086d911800000000331f3cbbAop.php
/tmp/YukiServiceFooServiceRay00000000086d917f00000000331f3cbbAop.php

何か設定方法/使い方がおかしいのでしょうか?

Performance Improvements

Following Injektor's vein of performance improvements

  • Pre-compiled factory-pattern classes that do the heavy-lifting instantiation of objects.
  • Deprecate use of memcached as a performance improver in favour of this method.

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;
   }

}

Doesn't work with class typehint.

class Foo{
    public function __construct(ConcreteClass $obj)
    {
        $this->obj = $obj;
    }
}

var_dump(($injector->getInstance('Foo')->obj) instanceof ConcreteClass);

should be true.

@Scope("Singleton") ignored when using DiCompiler?

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) => 

Original injector used in case of compile 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.

Optional default value

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.

Getting a new instance from a compiled container

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?

Moduleクラスの作り方と使い方について

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つです。

  1. インジェクトするクラスのModuleだけ指定する
  2. Namedアノテーションを使う

インジェクトするクラスのModuleだけ指定する

コンテナから生成するクラス用のModuleクラスを指定すると、中で何が指定されていて、何がバッティングするか分からないので、バインドが1つだけのModuleクラスを指定したほうがいいのではないか。

$injector = Injector::create(
    [
        new FooModule(),
        new BarModule(),
        new BazModule(),
        new HogeModule(),
        new FugaModule(),
    ]
);

Namedアノテーションを使う

それぞれの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クラスをどう使っていったらいいのでしょうか。

Annotation class should use 'Doctrine\Common\Annotations\Reader' not DocParser.

Current Ray\Di\Annotation class use 'Doctrine\Common\Annotations\DocParser' (lower level library for Reader).

The reason for this is.

  • with Reader, Annotation must state with FQN, directly or with use statement. This sounds too much boilerplate code at first.

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を使うべきです。

Exception name

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.

A code-gen for dependency injection

Container::$container

how to create Ray\Di\FakeCarInterface-* instance is stored in Container::$container
as follows.

Ray\Di\FakeCarInterface-*インスタンスをどのように生成するかは以下のようにContainer::$containerに格納されています。
2015-02-21 23 22 52

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');

constructorInject does not handle toProvider bindings correctly

<?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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.