Skip to content

Added a build method to the kernel to replace Bundle::build() #20107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 22, 2017
Merged

Added a build method to the kernel to replace Bundle::build() #20107

merged 1 commit into from
Feb 22, 2017

Conversation

linaori
Copy link
Contributor

@linaori linaori commented Sep 30, 2016

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #20099
License MIT
Doc PR ~

Adds a DX method to make it easier to omit using an AppBundle in your application.

Old situation

// src/AppBundle.php
class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new SomeCompilerPass());
        $container->addCompilerPass(new AnotherCompilerPass());
        $container->addCompilerPass(new YetAnotherCompilerPass());
    }
}

// src/DependencyInjection/AppExtension.php
class AppExtension extends Extension
{
    public function load(array $config, ContainerBuilder $container)
    {
        $binary = ExecutableResolver::getPath($container->getParameter('kernel.root_dir').'/../');
        $snappyConfig = ['pdf' => ['binary' => realpath($binary)]];

        $container->prependExtensionConfig('knp_snappy', $snappyConfig);
    }
}

New situation

// rm src/AppBundle.php
// rm src/DependencyInjection/AppExtension.php

// app/AppKernel.php
class AppKernel extends Kernel
{
    protected function build(ContainerBuilder $container)
    {
        $binary = ExecutableResolver::getPath($container->getParameter('kernel.root_dir').'/../');
        $snappyConfig = ['pdf' => ['binary' => realpath($binary)]];

        $container->prependExtensionConfig('knp_snappy', $snappyConfig);
        $container->addCompilerPass(new SomeCompilerPass());
        $container->addCompilerPass(new AnotherCompilerPass());
        $container->addCompilerPass(new YetAnotherCompilerPass());
    }
}

Still missing tests, wondering if worth adding in this state first.

@akomm
Copy link

akomm commented Sep 30, 2016

The less often you need to create an Extension in AppBundle, the better.

@fabpot
Copy link
Member

fabpot commented Sep 30, 2016

I like this a lot!

*
* @return string[]
*/
public function getClassesToCompile()
Copy link
Contributor

@GuilhemN GuilhemN Sep 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it still worth it when using php7 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm planning to verify this soon. Yet this PR is right adding it: it's just moving existing things around, and we still support php 5.
If php7 makes this unnecessary, then the whole inline system could be deprecated.
To be proven first, and not in this PR imho.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ener-Getick if not, we could always patch it to only create and load this in php 5

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mostly for new apps (which should use 7.0) so imo it doesn't make sense to allow that if that's not worth it in 7.0 but of course as @nicolas-grekas said, at worst we'll only have to deprecate it later.

@backbone87
Copy link
Contributor

backbone87 commented Sep 30, 2016

Would it be worth to let the Kernel itself be a container extension?

@linaori
Copy link
Contributor Author

linaori commented Sep 30, 2016

@@ -39,12 +39,27 @@ public function process(ContainerBuilder $container)
$classes = array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about $classes = $this->kernel->getClassesToCompile(); and drop all foreaches below.. (ie. is replacing the array_merge with foreach really needed for performance?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I'd just append everything to 1 big array and then do array_merge([], ...$classes), but 5.5 doesn't support that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, assigning everything from the kernel as defaults isn't a bad idea

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. The order seems more logical imo.

@@ -344,6 +344,30 @@ public function setAnnotatedClassCache(array $annotatedClasses)
}

/**
* Return a list of classes to be cached away when the container is build.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns (same for the other method below)

@@ -344,6 +344,30 @@ public function setAnnotatedClassCache(array $annotatedClasses)
}

/**
* Return a list of classes to be cached away when the container is build.
*
* If a class contains annotations, add it to getAnnotatedClassesToCompile()instead.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this is worth a duplicated extra comment, the API looks explicit enough

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its worth mentioning it here, although i would word it like so "Do not add classes that contain annotations, use ... instead"

*
* @param ContainerBuilder $container
*/
protected function build(ContainerBuilder $container)
Copy link
Member

@javiereguiluz javiereguiluz Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the name of this method. According to the Symfony naming conventions I think this should be called buildContainer() because we are not building the kernel, which is what the Kernel class is about.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point

Copy link
Contributor

@ro0NL ro0NL Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So isnt Kernel::buildContainer the method Bundle::build tends to be, ie.. do we still need that? Should it be Bundle::buildContainer?

If you go bundles, you probably (should) go extensions anyway.

If you want a more simple bundle i'd prefer the approach from #19596.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@javiereguiluz buildContainer is already taken

@linaori
Copy link
Contributor Author

linaori commented Oct 3, 2016

@tgalopin in #19205 you've added the annotated class cache and in #18533 you've added the class cache warmer. However, the kernel never received a variant of this. What is this for? I noticed that it never loads the annotated classes but it does load the normal class cache. However, there's also a ClassCacheCacheWarmer that does this. @fabpot do you know what the idea behind this is?

I'm wondering if something is broken here.

    protected function doLoadClassCache($name, $extension)
    {
        if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) {
            ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension);
        }
    }

@linaori
Copy link
Contributor Author

linaori commented Oct 3, 2016

Added tests, everything seems to work now.

@nicolas-grekas
Copy link
Member

@iltar the annotated class requires an optimized composer autoloader to work, this maybe why you don't see it working?

@linaori
Copy link
Contributor Author

linaori commented Oct 3, 2016

@nicolas-grekas I was wondering why the kernel does load the class cache while there's also a class cache warmer, but only a class cache warmer for the annotation variant. It feels like the kernel variant could be removed.

@nicolas-grekas
Copy link
Member

Could be yes, now that the cache warmer should be working as expected in 3.2.
The draw back is that cache warmers aren't always used, but maybe we shouldn't care :)

}

foreach ($extension->getAnnotatedClassesToCompile() as $annotatedClass) {
$annotatedClasses[] = $annotatedClass;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why stop using array_merge rather than a foreach here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically due to performance. Array merge in loops is slow (at least in php5, haven't benchmarked 7 myself): https://github.com/dseguy/clearPHP/blob/master/rules/no-array_merge-in-loop.md

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a performance optimization, you should submit it separately, as it may be worth merging it into 3.2 already.

@linaori
Copy link
Contributor Author

linaori commented Oct 22, 2016

Anything still required for this PR? @javiereguiluz had a comment about the name of the method, but the suggested name is already taken.

@ro0NL
Copy link
Contributor

ro0NL commented Oct 22, 2016

What about a finalize() method as well? Besides it would make sense to rename buildContainer to createContainer, so we can then later use build/finalizeContainer (in favor of build/finalize).

That would fix correct naming.

@@ -595,6 +630,9 @@ protected function prepareContainer(ContainerBuilder $container)
$container->addObjectResource($bundle);
}
}

$this->build($container);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put it after bundles, to match the common convention to register your AppBundle last, which would match making AppKernel last when calling build.

Registering normal compiler passes of the project after the core normal compiler passes is more logical IMO, and would limit the need to use priority overrides.

@@ -447,6 +471,17 @@ protected function initializeBundles()
}

/**
* The extension point similar to the Bundle::build() method.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically, you can already achieve this by overriding Kernel::buildContainer, as long as you take care to start by calling the parent method.

}

foreach ($extension->getAnnotatedClassesToCompile() as $annotatedClass) {
$annotatedClasses[] = $annotatedClass;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a performance optimization, you should submit it separately, as it may be worth merging it into 3.2 already.

@linaori
Copy link
Contributor Author

linaori commented Nov 30, 2016

  • Reverted the changes related to the class cache due to Consider deprecating ClassCollectionLoader & co. #20668
  • Decided to keep the build() method as is:
    • Deprecated/renaming methods in the kernel was not part of this scope (but I do agree naming could be a bit better now)
    • The Kernel build() is exactly the same as the bundle build(), which makes it a lot more intuitive and easier to understand how it works
  • The build() method for the kernel is now called last instead of first as mentioned by @stof.

@nicolas-grekas nicolas-grekas added this to the 3.x milestone Dec 6, 2016
@linaori
Copy link
Contributor Author

linaori commented Jan 9, 2017

Status: Needs Review

@linaori linaori changed the title Added build and class cache to kernel Added a build method to the kernel to replace Bundle::build() Jan 11, 2017
@xabbuh
Copy link
Member

xabbuh commented Jan 12, 2017

👍

Status: Reviewed

@linaori
Copy link
Contributor Author

linaori commented Feb 22, 2017

Failure is unrelated: Symfony\Component\Process\Tests\ProcessTest::testInputStreamWithCallable, PR is merged against the latest master

@fabpot
Copy link
Member

fabpot commented Feb 22, 2017

Thank you @iltar.

@fabpot fabpot merged commit 62e80fc into symfony:master Feb 22, 2017
fabpot added a commit that referenced this pull request Feb 22, 2017
…build() (iltar)

This PR was merged into the 3.3-dev branch.

Discussion
----------

Added a build method to the kernel to replace Bundle::build()

| Q | A |
| --- | --- |
| Branch? | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | no |
| Tests pass? | yes |
| Fixed tickets | #20099 |
| License | MIT |
| Doc PR | ~ |

Adds a DX method to make it easier to omit using an AppBundle in your application.

**Old situation**

``` php
// src/AppBundle.php
class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new SomeCompilerPass());
        $container->addCompilerPass(new AnotherCompilerPass());
        $container->addCompilerPass(new YetAnotherCompilerPass());
    }
}

// src/DependencyInjection/AppExtension.php
class AppExtension extends Extension
{
    public function load(array $config, ContainerBuilder $container)
    {
        $binary = ExecutableResolver::getPath($container->getParameter('kernel.root_dir').'/../');
        $snappyConfig = ['pdf' => ['binary' => realpath($binary)]];

        $container->prependExtensionConfig('knp_snappy', $snappyConfig);
    }
}
```

**New situation**

``` php
// rm src/AppBundle.php
// rm src/DependencyInjection/AppExtension.php

// app/AppKernel.php
class AppKernel extends Kernel
{
    protected function build(ContainerBuilder $container)
    {
        $binary = ExecutableResolver::getPath($container->getParameter('kernel.root_dir').'/../');
        $snappyConfig = ['pdf' => ['binary' => realpath($binary)]];

        $container->prependExtensionConfig('knp_snappy', $snappyConfig);
        $container->addCompilerPass(new SomeCompilerPass());
        $container->addCompilerPass(new AnotherCompilerPass());
        $container->addCompilerPass(new YetAnotherCompilerPass());
    }
}
```

Still missing tests, wondering if worth adding in this state first.

Commits
-------

62e80fc Added build and class cache to kernel
@nicolas-grekas nicolas-grekas modified the milestones: 3.x, 3.3 Mar 24, 2017
@fabpot fabpot mentioned this pull request May 1, 2017
@linaori linaori deleted the feature/appbundle-less branch February 8, 2019 13:38
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.