Skip to content

[ProxyManagerBridge][DI] allow proxifying interfaces with "lazy: Some\ProxifiedInterface" #27697

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
Jul 9, 2018

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Jun 24, 2018

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

By adding <tag name="proxy" interface="Some\ProxifiedInterface" /> to your service definitions, this PR allows generating interface-based proxies.

This would allow two things:

  • generating lazy proxies for final classes
  • wrapping a service into such proxy to forbid using the methods that are on a specific implementation but not on some interface - the proxy acting as a safe guard.

The generated proxies are always lazy, so that lazy=true/false makes no difference.

As a shortcut, you can also use lazy: Some\ProxifiedInterface to do the same (yaml example this time.)

@unkind
Copy link
Contributor

unkind commented Jun 24, 2018

The generated proxies are always lazy, so that lazy=true/false makes no difference.

Maybe throw exception for lazy: false then?

@nicolas-grekas
Copy link
Member Author

throw exception for lazy: false

I'm going to do this good idea. I'm also wondering about allowing lazy: Some\Interface at the loader level. Would be equivalent to lazy: true + adding the tag. WDYT?

@nicolas-grekas
Copy link
Member Author

Waiting for a resolution of Ocramius/ProxyManager#419

Status: needs work

@nicolas-grekas nicolas-grekas changed the title [ProxyManagerBridge] allow proxifying interfaces with <tag name="proxy" interface="Some\ProxifiedInterface" /> [ProxyManagerBridge][DI] allow proxifying interfaces with "lazy: Some\ProxifiedInterface" Jun 25, 2018
@nicolas-grekas nicolas-grekas force-pushed the di-proxy branch 5 times, most recently from 287c039 to 534200c Compare June 26, 2018 10:06
@nicolas-grekas
Copy link
Member Author

Waiting for a resolution of Ocramius/ProxyManager#419

Not needed actually, and won't be fixed in ProxyManager v2.1 which is in security-only fixes mode, but is the version compatible with PHP 7.1, thus Symfony 4.

This PR deals with the issue on its own. Ready.

Status: needs review

@@ -22,6 +23,11 @@ class LazyLoadingValueHolderFactoryV2 extends BaseFactory
{
private $generator;

public function getProxifiedClass(Definition $definition): ?string
{
return $this->getGenerator()->getProxifiedClass($definition);
Copy link
Member

Choose a reason for hiding this comment

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

that looks weird to me. getProxifiedClass(Definition $definition) is not part of ProxyGeneratorInterface used as return type.

Copy link
Member

Choose a reason for hiding this comment

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

I suggest keeping this logic in RuntimeInstantiator instead (or in a separate class if needed elsewhere)

if (!$definition->isLazy()) {
throw new \InvalidArgumentException(sprintf('Invalid definition for service of class "%s": setting the "proxy" tag on a service requires it to be "lazy".', $definition->getClass()));
}
if (!isset($definition->getTag('proxy')[0]['interface'])) {
Copy link
Member

Choose a reason for hiding this comment

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

what if we have multiple tags ? Should we support implementing multiple interfaces ?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's not supported by ProxyManager AFAIK (ping @Ocramius)
Should we throw?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, implementing multiple interfaces is not supported. I tried doing so when playing around with DCI implementations, but never pursued, because the use-case is too narrow compared with the added complexity.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks for the confirmation, this situation is now throwing

Copy link
Member Author

@nicolas-grekas nicolas-grekas Jun 27, 2018

Choose a reason for hiding this comment

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

I managed to make it work actually, now supports proxying several interfaces :)
When they're not compatible, a PHP fatal error is thrown at compile time, there is no other way around.

* @return $this
*/
final public function lazy(bool $lazy = true)
final public function lazy($lazy = true)
{
$this->definition->setLazy($lazy);
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't this always set a bool here ?

Copy link
Member

Choose a reason for hiding this comment

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

same in all loaders btw

@@ -267,7 +267,10 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults)
foreach (array('class', 'public', 'shared', 'synthetic', 'lazy', 'abstract') as $key) {
if ($value = $service->getAttribute($key)) {
$method = 'set'.$key;
$definition->$method(XmlUtils::phpize($value));
$definition->$method($value = XmlUtils::phpize($value));
if ('lazy' === $key && \is_string($value)) {
Copy link
Member

Choose a reason for hiding this comment

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

as lazy has a special logic, I would rather remove it from the loop and handle it on its own (as done for some other properties)

@stof
Copy link
Member

stof commented Jun 26, 2018

Do we still need LazyLoadingValueHolderFactoryV1 in master ?

@nicolas-grekas
Copy link
Member Author

@stof comments addressed, thanks.

@nicolas-grekas nicolas-grekas force-pushed the di-proxy branch 4 times, most recently from d6a94d7 to 5094785 Compare June 28, 2018 05:17
@fabpot
Copy link
Member

fabpot commented Jul 9, 2018

Thank you @nicolas-grekas.

@fabpot fabpot merged commit 1d9f1d1 into symfony:master Jul 9, 2018
fabpot added a commit that referenced this pull request Jul 9, 2018
…ith "lazy: Some\ProxifiedInterface" (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[ProxyManagerBridge][DI] allow proxifying interfaces with "lazy: Some\ProxifiedInterface"

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

By adding `<tag name="proxy" interface="Some\ProxifiedInterface" />` to your service definitions, this PR allows generating interface-based proxies.

This would allow two things:
- generating lazy proxies for final classes
- wrapping a service into such proxy to forbid using the methods that are on a specific implementation but not on some interface - the proxy acting as a safe guard.

The generated proxies are always lazy, so that lazy=true/false makes no difference.

As a shortcut, you can also use `lazy: Some\ProxifiedInterface` to do the same (yaml example this time.)

Commits
-------

1d9f1d1 [ProxyManagerBridge][DI] allow proxifying interfaces with "lazy: Some\ProxifiedInterface"
@nicolas-grekas nicolas-grekas deleted the di-proxy branch July 22, 2018 20:25
@nicolas-grekas nicolas-grekas modified the milestones: next, 4.2 Nov 1, 2018
This was referenced Nov 3, 2018
fabpot added a commit that referenced this pull request May 27, 2022
…ers and pass it to child definitions (nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[DependencyInjection] Fix "proxy" tag: resolve its parameters and pass it to child definitions

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

Forgotten in #27697

"proxy" tags are special: they must follow like the "lazy" attribute of definitions.

Commits
-------

4cc3b3d [DependencyInjection] Fix "proxy" tag: resolve its parameters and pass it to child definitions
nicolas-grekas added a commit that referenced this pull request Jul 12, 2022
…oxies out of the box (nicolas-grekas)

This PR was merged into the 6.2 branch.

Discussion
----------

[DependencyInjection] Use lazy-loading ghost object proxies out of the box

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #35345
| License       | MIT
| Doc PR        | -

This PR builds on #46751. It also replaces #46458.

Instead of using ProxyManager to make lazy services actually lazy, using `LazyGhostObjectTrait` from #46751 allows doing so *out of the box* - aka without the need to install any optional dependencies.

When a virtual proxy is required (typically when using [the `proxy` tag](#27697)), ProxyManager is still required (and the dep remains optional.)

But for most services, using `LazyGhostObjectTrait` just works \o/

Commits
-------

58a1848 [DependencyInjection] Use lazy-loading ghost object proxies out of the box
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull request Jul 12, 2022
…oxies out of the box (nicolas-grekas)

This PR was merged into the 6.2 branch.

Discussion
----------

[DependencyInjection] Use lazy-loading ghost object proxies out of the box

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #35345
| License       | MIT
| Doc PR        | -

This PR builds on #46751. It also replaces #46458.

Instead of using ProxyManager to make lazy services actually lazy, using `LazyGhostObjectTrait` from #46751 allows doing so *out of the box* - aka without the need to install any optional dependencies.

When a virtual proxy is required (typically when using [the `proxy` tag](symfony/symfony#27697)), ProxyManager is still required (and the dep remains optional.)

But for most services, using `LazyGhostObjectTrait` just works \o/

Commits
-------

58a184826a [DependencyInjection] Use lazy-loading ghost object proxies out of the box
symfony-splitter pushed a commit to symfony/http-kernel that referenced this pull request Jul 12, 2022
…oxies out of the box (nicolas-grekas)

This PR was merged into the 6.2 branch.

Discussion
----------

[DependencyInjection] Use lazy-loading ghost object proxies out of the box

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #35345
| License       | MIT
| Doc PR        | -

This PR builds on #46751. It also replaces #46458.

Instead of using ProxyManager to make lazy services actually lazy, using `LazyGhostObjectTrait` from #46751 allows doing so *out of the box* - aka without the need to install any optional dependencies.

When a virtual proxy is required (typically when using [the `proxy` tag](symfony/symfony#27697)), ProxyManager is still required (and the dep remains optional.)

But for most services, using `LazyGhostObjectTrait` just works \o/

Commits
-------

58a184826a [DependencyInjection] Use lazy-loading ghost object proxies out of the box
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull request Jul 28, 2023
…oxies out of the box (nicolas-grekas)

This PR was merged into the 6.2 branch.

Discussion
----------

[DependencyInjection] Use lazy-loading ghost object proxies out of the box

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #35345
| License       | MIT
| Doc PR        | -

This PR builds on #46751. It also replaces #46458.

Instead of using ProxyManager to make lazy services actually lazy, using `LazyGhostObjectTrait` from #46751 allows doing so *out of the box* - aka without the need to install any optional dependencies.

When a virtual proxy is required (typically when using [the `proxy` tag](symfony/symfony#27697)), ProxyManager is still required (and the dep remains optional.)

But for most services, using `LazyGhostObjectTrait` just works \o/

Commits
-------

58a184826a [DependencyInjection] Use lazy-loading ghost object proxies out of the box
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.

6 participants