Skip to content

Container compiler#149

Closed
mnapoli wants to merge 21 commits intomasterfrom
feature/Compiler
Closed

Container compiler#149
mnapoli wants to merge 21 commits intomasterfrom
feature/Compiler

Conversation

@mnapoli
Copy link
Copy Markdown
Member

@mnapoli mnapoli commented Jan 25, 2014

Compile the container to PHP code for best performances.

TODO:

  • ContainerCompiler
  • ValueDefinitionCompiler
  • AliasDefinitionCompiler
  • ClassDefinitionCompiler
  • CallbackDefinitionCompiler
  • Lazy reading/loading of definition sources (read definitions lazily bc the compiled container doesn't need them)
  • Work on performances: mnapoli/di-benchmark
  • Compile all known definitions upstart in a single file
  • Lazy injection

What would also be needed:

  • environment support (i.e. to have one compiled container per environment, else it's gonna mess up)
  • differentiate static configuration (files, annotations, autowiring) from dynamic (Container::set()) -> dynamic shouldn't be compiled

Also instead of maintaining 2 ways of resolving definitions (DefinitionResolver and DefinitionCompiler), it might be easier to just eval() the code generated by the compiler on the fly. This generated code can be cached as definitions are cached today. I think it would be even faster than today.

I did mess some things up with git, I should have put some commits in the 4.0 branch instead of this one :/

@mnapoli mnapoli mentioned this pull request Jan 25, 2014
8 tasks
@ghost
Copy link
Copy Markdown

ghost commented Feb 5, 2014

Would this allow for compiler passes like in Symfony? The main use case that I'm interested is that you could tag several services and then add them all to another class.

Really like the look of 4.0 and enjoyed your blog post about it. Makes a lot of sense. Keep up the great work!

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

Would this allow for compiler passes like in Symfony? The main use case that I'm interested is that you could tag several services and then add them all to another class.

@polothy Right now this doesn't support compiler passes. Maybe it should, that's a great idea.

However, regarding tagged services, I am not convinced that this would be the best solution. I'd rather see a feature in the container than in the compiler only. Symfony's container works only when compiled (or at least tagged services). PHP-DI is supposed to work without being compiled.

I am sure we can find a great solution for "tagged services" that would work not compiled and that would be overridable and extensible.

The way I have used (and seen used) tagged services is only to inject collections of services/objects. I.e., most of the time, it is to call a addFoo($foo) multiple times, or call a addFoos(array $foos) once with an array of services. Do you know other use cases?

Really like the look of 4.0 and enjoyed your blog post about it. Makes a lot of sense. Keep up the great work!

Thank you!

@mnapoli mnapoli modified the milestone: 4.1 Feb 5, 2014
@anton-siardziuk
Copy link
Copy Markdown

Hi! What if I have much amount of classes loaded by reflection definition source? Can they be compiled?

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

@m00t Yes. Every definition that is not directly set on the container (i.e. $container->set($id, …)) is cached (as of today) and compiled (in this pull request).

So reflection, annotation or explicit definition: no difference in performance.

@anton-siardziuk
Copy link
Copy Markdown

Cool! Do you have any estimates when it will be tagged?

@ghost
Copy link
Copy Markdown

ghost commented Feb 5, 2014

The way I have used (and seen used) tagged services is only to inject collections of services/objects. I.e., most of the time, it is to call a addFoo($foo) multiple times, or call a addFoos(array $foos) once with an array of services. Do you know other use cases?

@mnapoli Yes, this is the main use case for me at least. I was also using a configurator, but I don't think that needs to necessarily be handled by PHP-DI. Would it be possible to tell PHP-DI to inject all instances of Foo into something? Or for each Foo, call setFoo? Perhaps that could be too greedy at times, hrm.

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

@m00t I am working on benchmarking this to "prove" it improves performances. But it turns out it's really hard :/ because:

  • it relies on an opcache (else there is I/O time reading files from disk, which would happen in production), so it needs to run behind a webserver
  • it's hard coming up with a representative dependency graph
  • my current bench uses apache, and the web page is so fast it's not really reliable, and I believe the time spent in PHP-DI only is not really representative of the server response time (network, apache, ... overhead)

So no real estimate, however I'll be working on this in the next weeks to have it in 4.1.

@polothy Yes that's what I'm thinking about. Maybe some way of defining a "collection" (≠ "object" or "value"), and injecting a collection in a method either by calling it once with an array, or calling it several times.

too greedy

yeah that could be a problem, especially if you have several decoupled modules (or bundles f.e.), you might want to:

  1. add an item to a collection (i.e. same as tagging a service)
  2. override an item that is in a collection (or is that really needed...?)

@ghost
Copy link
Copy Markdown

ghost commented Feb 5, 2014

yeah that could be a problem, especially if you have several decoupled modules (or bundles f.e.)

@mnapoli Hrm, yeah interesting. Each could have a collection of Foos that they have implemented and need to use. But I wouldn't want Module X to get Module Y's Foos (If that makes sense haha).

But at a higher level, I may want all instances of Bars from every module.

@anton-siardziuk
Copy link
Copy Markdown

What if make compilation process more symfony-di way: lets compile one huge file and then just use it. It should eliminate all IO problems I think

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

@m00t The problem is with autowiring or annotation, the container has no way of knowing which classes will ever be asked. In Symfony, all definitions are explicitely written down, so the container knows what to compile.

In the solution implemented in this PR, each definition is compiled to a separate file. I think it shouldn't make a lot of difference with an Opcache anyway, but it's hard to test that.

@anton-siardziuk
Copy link
Copy Markdown

@mnapoli Take a look at zend-di, it scans all directory with sources and create definitions for classes, and then they can be dumped to one huge container

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

@m00t Interesting. I wonder though how it decides which classes might ever be injected. Or does it try to detect calls to get() (or here, @Inject too)?

@anton-siardziuk
Copy link
Copy Markdown

@mnapoli I think it doesnt try to detect them, just dump definitions for all found classes

@anton-siardziuk
Copy link
Copy Markdown

@mnapoli I created some simple proof of concept for one-huge-container-file-compiler. Please, take a look if you are interested in: https://github.com/m00t/php-di-compiler-poc

@mnapoli
Copy link
Copy Markdown
Member Author

mnapoli commented Feb 5, 2014

@m00t Neat! This is very interesting, if you have time feel free to work on a PR, else I'll look into that in the next weeks.

@anton-siardziuk
Copy link
Copy Markdown

@mnapoli I will try my best, but can not promise that I will do that

@mnapoli mnapoli mentioned this pull request Feb 22, 2014
Merged
8 tasks
@mnapoli mnapoli removed this from the 4.1 milestone Mar 31, 2014
@mnapoli mnapoli closed this Oct 5, 2014
@mnapoli mnapoli mentioned this pull request May 28, 2017
31 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants