Closed Ragazzo closed 10 years ago
@samdark absolutely not. it was only about public property components
in object config and not that custom creating, see no benefits of such custom. because how will you determine simple strings from components? This feature only for making features from Module
to Component
.
@samdark
$rocket = Yii::createObject(
'class' => 'Rocket',
'components' => [
'engine' => [
'class' => 'app\rocket\gasoline\Engine',
'fuel' => 'Gas',
],
],
);
$roket->engine->start();
//or in your case
$roket->fly();
That's much more interesting. If I still want a rocket to fly w/o starting engine directly I need the class itself to be:
class Rocket extends Component
{
public function fly()
{
return $this->getComponent('engine')->work();
}
}
Pros:
Cons:
Since I can't find any significant cons I think it should be done.
@samdark Probably make sence.
is it time to celebrate then? yes, no?
So the only question left is the form of it i.e. should it be component method or a separate container as @qiangxue already mentioned.
@samdark
Cons:
- No IDE autocomplete (solvable via phpdoc easily).
- ???
About 2 a little:
class Rocket extends Component
{
public function fly()
{
return $this->getComponent('engine')->work();
}
}
$rocket = new Rocket();
$rocket->setComponent('bomb');
$rocket->fly();
Rockets created with another approaches does not have such troubles. I do not say that feature is bad. I just say that move it to Component
is bad idea. Need some another ideas about implementation. For example for Module
its really make sence to have ability set any components. For Rocket
no. Maybe we need trait, maybe interface, do not know yet.
@creocoder I don't get it. You can enforce interface if you want via checking if getComponent
returns what you need and throwing exception otherwise.
@samdark
I don't get it
Will try to explain. Rockets created using:
class Rocket extends Component
{
public function setEngine($engine) {
...
}
public function fly()
{
...
}
}
Simplier and reliable.
That's what I did in https://github.com/yiisoft/yii2/issues/503#issuecomment-32597577 exactly.
Proposal of DI container interface: https://gist.github.com/qiangxue/0af159376ecaf2e78e4a
Based on this interface, will implement a ContainerTrait
which will be used by Module
(and thus Application
) to turn a module/application into a DI container. Will also provide a Container
class out-of-box so that you can use it alone. If you prefer to turn your existing class into a container, just use the trait.
The container supports both constructor injection and setter injection. It also serves as service locator. Other injections will not be supported as they are not commonly used.
Please help review the interface design. Thanks!
Ok, can we change !
for not shared, and make shared without this mark? Because it is common that !
is not
and as for shared
i thouth it is for not shared components, so i find it confusing. Overall i am not sure that we need such big implementation (good to have but not must
), because Yii::$app by itslef with setComponents/getComponents
acts like a ServiceLocator, and usually users will be using it rather that di trait.
However can you clarify you example with DI in constructor? So, user writed interface or class typehint, and what else he need to do? where we should write:
use yii\di\Container;
use yii\di\Instance;
$container = new Container;
$container->components = [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => '...',
],
'UserFinderInterface' => [
'class' => 'UserFinder',
'db' => Instance::of('db', $container),
],
];
and should we? i thought it will be resolved automatically, no?
With this feature can we inject dependency into Controller through constructor for example ? Will framework core use that component ?
@Ragazzo As I wrote, Module
(and thus Application
) will use this DI trait, which will replace their current implementation of getComponents()
, etc. The design of DI trait is made such that all existing syntax of components
is unchanged. Only change is getComponent()
is renamed to get()
. The example I gave is to show how to use DI container alone. You may replace it with Yii::$app
. When you configure components
in app config, you are essentially configuring the DI container.
@yupe Yes.
There is some drawback by using DI container. It is mainly related with the performance degradation caused by constructor injection because the container needs to use reflection to detect dependencies. That means, the instantiation of every application component will be slightly slower.
I think we should not resolve constructor dependecies with DI - container. Can you point docs from what you were reading and implementing current solution? I got breif look into L4 IoC and their container, however there is nothing special in it. But as for me resolving constructor deps. with type hint is not needed.
also, @qiangxue am i correct to understand that not shared components will be created each time ? how do you plan to create them since they can be not yii\base\Component
? Will your container have ability for binding closures ?
Constructor dependency is the most used DI. See the writing from the original author of DI: http://martinfowler.com/articles/injection.html L4 implements that too.
There is no requirement regarding the base class of an object to be handled by this DI implementation.
Yes, it supports closures (there's a simple example.)
yes, i know that L4 supports constructor DI. Not sure if we should do this, because it will force users to dump everything in DI container, for example https://github.com/yiisoft/yii2/blob/master/framework/db/Connection.php#L392 users will be putting some db.connection
in DI container and then writing typehint everywhere, i think this is not right, since your container will be like huge number of components and nothing more.
Can you show here example with closure? https://gist.github.com/qiangxue/0af159376ecaf2e78e4a
Also should Yii::$app->container
component be created? or how do you plan to use it ? more specic examples will be great to understand. For example in L4, it is simple App::bind()
, in Yii what it should be?
Yes, I agree with you. I'm also not sure whether we should support constructor injection. The original author of DI compares fairly all kinds of DI. It's a good read.
https://gist.github.com/qiangxue/0af159376ecaf2e78e4a#file-containerinterface-php-L157
No, there's no Yii::$app->container
, because Yii::$app
itself is a container.
As for me, IoC solution through DI that is implemented in simple manual injection into constructor and setters are fine, autoresolving is not just feeling right, and if needed they can simply get it from container by itself.
Overall it is fine by me, since the only thing i wanted was ability to set components on every other component.
Can you also show how can we register components (i mean any components, not Yii Component
class) with get
/ set
methods from config? for example for Yii components we can simply specify components
property and setComponents
setter will be called, how then specify correctly things for get
/ set
properties?
Or do you have plan/idea of simply specifying them somewhere in ioc_di.php
config file, that will be included (require_once
) after application instance is created and user can bind there everything he need?
When you are configuring components
in app config, you are configuring the DI container (Yii::$app
is the global DI container). There's nothing new here except the new syntax introduced as described in the following. The syntax is compatible with the current one with new extensions.
https://gist.github.com/qiangxue/0af159376ecaf2e78e4a#file-containerinterface-php-L143
no, when i set components
i set Yii components (they will be created with Yii::createObject()
), how can set through config not yii components, but for example some custom classes and other things with set
and get
methods? as i said, should we have separated config for this, it will be simple plain file, where user will register everything that is needed ? Or maybe do we need something like L4 service providers ? not sure about the last one though )
UPD sorry, my misunderstanding, you are right, however can you show how components will be created? with Yii::createObject
or what method? How then handle other not yii objects ?
As I said components
is the one you use to configure "application components" and classes. The latter is the new thing introduced. An "application component" is essentially a service or component in the DI terminology. For this reason, Application
is indeed a service locator. There's no need to separate config file or whatever.
so, all services should be just put in components
config setting ? also how to resolve creating not yii components? i think user can use different classes and other things, how can we check if component should be created with Yii::createComponent
or by some other thing ?
There's no Yii::createComponent()
.
The "application components" are simply named shared objects, while the syntax classname
and !classname
declare anonymous shared and non-shared objects.
i understand by when set
is called, then instance should be created, and in docs to interface there is a notice about Yii::createObject
(sorry, i mixed it with Yii1 similar method), and still how you will resolve this situation ? https://gist.github.com/qiangxue/0af159376ecaf2e78e4a#file-containerinterface-php-L184
Nope, an object is not created until get()
is called.
What exactly is your question? It seems we are looping around. You may view Yii::createObject()
as the new
operator except that the former will respect Yii::$objectConfig
.
my question is: how object will be created if i register it like this:
class MyCustomObject
{
}
`components` => [
'customObject' => 'MyCustomObject',
],
it also can be different classes from other fw and etc. If they will be created with Yii::createObject
that means that some array will be passed to __construct()
but objects may not expect such behavior, that is what i am talking about. Maybe we will need services init. based on env, such idea was spoken long time ago too - main thing is that service should know how it should be init. and created, this is implemented with service init. layer (read it as class
), that will create object and init it as needed.
Yes, if you give a config array for an object, it will be passed to the constructor. We may do it better by checking if Object
is the base class. If not, the config array will be applied after the object is created.
Maybe we will need services init.
More details?
yes, i have some thoughts but i think that this can be something overcomplicated and i discarted it. So if i will need custom object creating i always can use callback function for this purpose and put all such things in separated file like /config/ioc.php
and in it:
Yii::$app->setComponent('custom', function ($container) {
$custom = MyCustomObject();
$custom->setSomething('some-value');
....
return $custom;
});
so if this is a valid use-case then we should provide docs for this as an example i think ?
Also we may consider to implement something like L4 service providers - thing to group similar providers in class, because some init. may be needed for them, based on env. You can look like in L4 it is done, however it is simple class-wrapper as you can see, nothing special.
Such situation can be used when we for example may use different classes and components from other frameworks, and if we use several of such components, we may need some good way to pre-init them on some env. conditions, with that said it is better to organize them into some structure - service provider.
components
is a massive way of configuring the container. You can use set()
to configure individual class. So nothing is special here. Your example of using anonymous function is already supported.
L4 service provider is just a way to run a piece of code from a class during bootstrap time. We already have Application::extensions
which does similar thing.
nope, extenstions
are not the same as providers
for services in L4. Overall it can be something like this:
class CustomProvider extends ServiceProvider
{
public function init()
{
$this->registerServices();
}
public function registerFirstService()
{
....
}
public function registerSecondService()
{
....
}
}
by this we can plug in and configure services as needed, without puttin them in plain file as i described above, this makes services configuring flexible.
Also as for anonymous functions, i know they are supported, i just asked if my example with plain file is correct. If so then maybe we should add later to docs this example.
All features are done. Reopened for documentation purpose.
ArrayAccess
done ?
Nope as it doesn't make sense
On Friday, March 21, 2014, Mark notifications@github.com wrote:
ArrayAccess done ?
— Reply to this email directly or view it on GitHubhttps://github.com/yiisoft/yii2/issues/503#issuecomment-38249635 .
hm, afaik in sf2 you can act with container as array? no?
Yes but it still doesn't make sense
On Friday, March 21, 2014, Mark notifications@github.com wrote:
hm, afaik in sf2 you can act with container as array? no?
— Reply to this email directly or view it on GitHubhttps://github.com/yiisoft/yii2/issues/503#issuecomment-38268606 .
why it does not make sense?
Why array access?
On Friday, March 21, 2014, Mark notifications@github.com wrote:
why it does not make sense?
— Reply to this email directly or view it on GitHubhttps://github.com/yiisoft/yii2/issues/503#issuecomment-38269007 .
Useful for setting components, same issues as for sf2 overall .
Arrayaccess should not be abused. It should be mainly used by classes that represent data collections. Otherwise we would say any classes should implement this as it is convenient to set properties. I don't think sf2 is making a good choice here (other fws are not doing this.) Anyway, let's not spend more time trying to convince each other. We can always implement this in future if there are strong requests for this feature.
Ok )
It would be great if we have ability to set components on some basic class extended from
Component
, so in this way it will be smth. likecomposite
pattern, also @samdark do you remeber this suggestions from russian php forum? Any other thoughts of core-developers? In Yii1 i was missing this functionality, that i can't do smth. like this for example:I think this functionality can be cleared from
Module
and set to theComponent
class.