Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Lazy services #4146

Merged

Conversation

Ocramius
Copy link
Member

This PR is based on #4145

It introduces two factories that define delegates capable of generating lazy services. For this, an optional dependency to ProxyManager was introduced.

If this one is ok, I'll start writing docs for it.

Current usage is via config - in an MVC application, define following:

return array(
    'lazy_services' => array(
        // needed, defines a map of service name to class name
        'map'                   => array('foo' => 'stdClass'),
        // namespace of the proxies to be generated
        'proxies_namespace'     => 'MyAppProxy',
        // whether to generate or not the proxies at runtime
        // if false, an autoloader will be registered
        'auto_generate_proxies' => true,
        // where should the proxies be saved?
        'proxies_target_dir'    => 'foo/bar',
        // should the generated proxies be written to disk?
        'write_proxy_files' => false,
    ),
    'service_manager' => array(
        'invokables' => array(
            'foo' => 'stdClass',
        ),
        'factories' => array(
            'foo-delegate' => 'Zend\ServiceManager\Proxy\LazyServiceFactoryFactory',
        ),
        'delegates' => array(
            'foo' => 'foo-delegate',
        ),
    ),
);

Documentation PR at zendframework/zf2-documentation#832

Implemented also for symfony at symfony/symfony#7527

@@ -11,16 +11,19 @@
"require": {
"php": ">=5.3.3"
},
"minimum-stability": "dev",
Copy link
Member Author

Choose a reason for hiding this comment

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

This is because of a (current) dependency to a dev-version of jms/cg. I'll ping the author to get it stable asap

Copy link
Member

Choose a reason for hiding this comment

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

Actually, it's up to individual users who want to install proxy-manager to add the stability flag. For those of us doing development, we'll get that stability; it's only the end-user consumers that need to be aware of that. That said, a stable tag and/or docs indicating this would be good.

Copy link
Member Author

Choose a reason for hiding this comment

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

@weierophinney yes, this is here only to make travis happy. I'll revert it as soon as I got feedback

Copy link
Member

Choose a reason for hiding this comment

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

Odd -- I'd expect travis would be using the --dev flag when installing composer deps...

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI, --dev is not related to stability, it's related to require-dev only. Confusing, I know, but two separate concepts with similar terminology.

@Ocramius
Copy link
Member Author

@weierophinney @ralphschindler there is a problem with the license of CG-library which makes this PR impossible until either:

  • I convert ProxyManager to use Zend\Code
  • CG-lib changes license

So please stand by :(

@Ocramius
Copy link
Member Author

Ok, I fixed the issue by making ProxyManager depend on Zend\Code in Ocramius/ProxyManager#18, but now composer would refuse to resolve dependencies because ocramius/proxy-manager requires "zendframework/zend-code": "2.*", while the current version I am on is "dev-feature/proxy-delegate-services"

EDIT: spawned composer/composer#1748

@Ocramius
Copy link
Member Author

@weierophinney travis really doesn't like me :) The failure is on cache stuff (unrelated, probably timing issue).

For the rest, this and #4145 are good to go - this one is based on the branch of #4145.

@ghost ghost assigned weierophinney Apr 15, 2013
weierophinney added a commit that referenced this pull request Apr 15, 2013
weierophinney added a commit that referenced this pull request Apr 15, 2013
@weierophinney weierophinney merged commit 3299c82 into zendframework:develop Apr 15, 2013
@richardjennings
Copy link
Contributor

Very nice !

@Ocramius Ocramius deleted the feature/proxy-delegate-services branch May 1, 2013 18:15
fabpot added a commit to symfony/symfony that referenced this pull request May 6, 2013
This PR was squashed before being merged into the master branch (closes #7890).

Discussion
----------

ProxyManager Bridge

As of @beberlei's suggestion, I re-implemented #7527 as a new bridge to avoid possible hidden dependencies.

Everything is like #7527 except that the new namespace (and possibly package/subtree split) `Symfony\Bridge\ProxyManager` is introduced

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #6140 (supersedes) #5012 #6102 (maybe) #7527 (supersedes)
| License       | MIT (attached code) - BSD-3-Clause (transitive dependency)
| Doc PR        | Please pester me to death so I do it

This PR introduces lazy services along the lines of zendframework/zendframework#4146

It introduces an **OPTIONAL** dependency to [ProxyManager](https://github.com/Ocramius/ProxyManager) and transitively to [`"zendframework/zend-code": "2.*"`](https://github.com/zendframework/zf2/tree/master/library/Zend/Code).

## Lazy services: why? A comprehensive example

For those who don't know what this is about, here's an example.

Assuming you have a service class like following:

```php
class MySuperSlowClass
{
    public function __construct()
    {
        // inject large object graph or do heavy computation
        sleep(10);
    }

    public function doFoo()
    {
        echo 'Foo!';
    }
}
```

The DIC will hang for 10 seconds when calling:

```php
$container->get('my_super_slow_class');
```

With this PR, this can be avoided, and the following call will return a proxy immediately.

```php
$container->getDefinitions('my_super_slow_class')->setLazy(true);
$service = $container->get('my_super_slow_class');
```

The 10 seconds wait time will be delayed until the object is actually used:

```php
$service->doFoo(); // wait 10 seconds, then 'Foo!'
```

A more extensive description of the functionality can be found [here](https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md).

## When do we need it?

Lazy services can be used to optimize the dependency graph in cases like:

 * Webservice endpoints
 * Db connections
 * Objects that cause I/O in general
 * Large dependency graphs that are not always used

This could also help in reducing excessive service location usage as I've explained [here](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).

## Implementation quirks of this PR

There's a couple of quirks in the implementation:

 * `Symfony\Component\DependencyInjection\CompilerBuilder#createService` is now public because of the limitations of PHP 5.3
 * `Symfony\Component\DependencyInjection\Dumper\PhpDumper` now with extra mess!
 * The proxies are dumped at the end of compiled containers, therefore the container class is not PSR compliant anymore

Commits
-------

78e3710 ProxyManager Bridge
fabpot added a commit to symfony/dependency-injection that referenced this pull request May 6, 2013
This PR was squashed before being merged into the master branch (closes #7890).

Discussion
----------

ProxyManager Bridge

As of @beberlei's suggestion, I re-implemented #7527 as a new bridge to avoid possible hidden dependencies.

Everything is like #7527 except that the new namespace (and possibly package/subtree split) `Symfony\Bridge\ProxyManager` is introduced

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #6140 (supersedes) #5012 #6102 (maybe) #7527 (supersedes)
| License       | MIT (attached code) - BSD-3-Clause (transitive dependency)
| Doc PR        | Please pester me to death so I do it

This PR introduces lazy services along the lines of zendframework/zendframework#4146

It introduces an **OPTIONAL** dependency to [ProxyManager](https://github.com/Ocramius/ProxyManager) and transitively to [`"zendframework/zend-code": "2.*"`](https://github.com/zendframework/zf2/tree/master/library/Zend/Code).

## Lazy services: why? A comprehensive example

For those who don't know what this is about, here's an example.

Assuming you have a service class like following:

```php
class MySuperSlowClass
{
    public function __construct()
    {
        // inject large object graph or do heavy computation
        sleep(10);
    }

    public function doFoo()
    {
        echo 'Foo!';
    }
}
```

The DIC will hang for 10 seconds when calling:

```php
$container->get('my_super_slow_class');
```

With this PR, this can be avoided, and the following call will return a proxy immediately.

```php
$container->getDefinitions('my_super_slow_class')->setLazy(true);
$service = $container->get('my_super_slow_class');
```

The 10 seconds wait time will be delayed until the object is actually used:

```php
$service->doFoo(); // wait 10 seconds, then 'Foo!'
```

A more extensive description of the functionality can be found [here](https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md).

## When do we need it?

Lazy services can be used to optimize the dependency graph in cases like:

 * Webservice endpoints
 * Db connections
 * Objects that cause I/O in general
 * Large dependency graphs that are not always used

This could also help in reducing excessive service location usage as I've explained [here](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).

## Implementation quirks of this PR

There's a couple of quirks in the implementation:

 * `Symfony\Component\DependencyInjection\CompilerBuilder#createService` is now public because of the limitations of PHP 5.3
 * `Symfony\Component\DependencyInjection\Dumper\PhpDumper` now with extra mess!
 * The proxies are dumped at the end of compiled containers, therefore the container class is not PSR compliant anymore

Commits
-------

78e3710 ProxyManager Bridge
fabpot added a commit to symfony/http-kernel that referenced this pull request May 6, 2013
This PR was squashed before being merged into the master branch (closes #7890).

Discussion
----------

ProxyManager Bridge

As of @beberlei's suggestion, I re-implemented #7527 as a new bridge to avoid possible hidden dependencies.

Everything is like #7527 except that the new namespace (and possibly package/subtree split) `Symfony\Bridge\ProxyManager` is introduced

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #6140 (supersedes) #5012 #6102 (maybe) #7527 (supersedes)
| License       | MIT (attached code) - BSD-3-Clause (transitive dependency)
| Doc PR        | Please pester me to death so I do it

This PR introduces lazy services along the lines of zendframework/zendframework#4146

It introduces an **OPTIONAL** dependency to [ProxyManager](https://github.com/Ocramius/ProxyManager) and transitively to [`"zendframework/zend-code": "2.*"`](https://github.com/zendframework/zf2/tree/master/library/Zend/Code).

## Lazy services: why? A comprehensive example

For those who don't know what this is about, here's an example.

Assuming you have a service class like following:

```php
class MySuperSlowClass
{
    public function __construct()
    {
        // inject large object graph or do heavy computation
        sleep(10);
    }

    public function doFoo()
    {
        echo 'Foo!';
    }
}
```

The DIC will hang for 10 seconds when calling:

```php
$container->get('my_super_slow_class');
```

With this PR, this can be avoided, and the following call will return a proxy immediately.

```php
$container->getDefinitions('my_super_slow_class')->setLazy(true);
$service = $container->get('my_super_slow_class');
```

The 10 seconds wait time will be delayed until the object is actually used:

```php
$service->doFoo(); // wait 10 seconds, then 'Foo!'
```

A more extensive description of the functionality can be found [here](https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md).

## When do we need it?

Lazy services can be used to optimize the dependency graph in cases like:

 * Webservice endpoints
 * Db connections
 * Objects that cause I/O in general
 * Large dependency graphs that are not always used

This could also help in reducing excessive service location usage as I've explained [here](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).

## Implementation quirks of this PR

There's a couple of quirks in the implementation:

 * `Symfony\Component\DependencyInjection\CompilerBuilder#createService` is now public because of the limitations of PHP 5.3
 * `Symfony\Component\DependencyInjection\Dumper\PhpDumper` now with extra mess!
 * The proxies are dumped at the end of compiled containers, therefore the container class is not PSR compliant anymore

Commits
-------

78e3710 ProxyManager Bridge
@localheinz
Copy link
Member

Would it make sense to add and implement a LazyServiceProviderInterface in modules, then?

Just trying to wrap my head around this.

@Ocramius
Copy link
Member Author

@localheinz this logic applies to ANY service manager and abstract plugin manager ;)

weierophinney added a commit to zendframework/zend-servicemanager that referenced this pull request May 15, 2015
…ture/proxy-delegate-services

Lazy services
weierophinney added a commit to zendframework/zend-servicemanager that referenced this pull request May 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants