Lazy services #4146

Merged
merged 14 commits into from Apr 15, 2013

Conversation

Projects
None yet
5 participants
Member

Ocramius commented Mar 29, 2013

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

composer.json
@@ -11,16 +11,19 @@
"require": {
"php": ">=5.3.3"
},
+ "minimum-stability": "dev",
@Ocramius

Ocramius Mar 29, 2013

Member

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

@weierophinney

weierophinney Mar 29, 2013

Owner

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.

@Ocramius

Ocramius Mar 29, 2013

Member

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

@weierophinney

weierophinney Mar 29, 2013

Owner

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

@igorw

igorw Mar 30, 2013

Contributor

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 Ocramius referenced this pull request in symfony/symfony Mar 30, 2013

Closed

Lazy services - service proxies #7527

8 of 8 tasks complete
Member

Ocramius commented Mar 30, 2013

@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 :(

Member

Ocramius commented Mar 30, 2013

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

Member

Ocramius commented Apr 12, 2013

@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

1 check failed

default The Travis build failed
Details

@Ocramius Ocramius referenced this pull request in zendframework/zf2-documentation Apr 15, 2013

Merged

Feature/lazy services #839

Contributor

richardjennings commented Apr 16, 2013

Very nice !

@Ocramius Ocramius deleted the Ocramius:feature/proxy-delegate-services branch May 1, 2013

fabpot added a commit to symfony/symfony that referenced this pull request May 6, 2013

merged branch Ocramius/feature/proxy-manager-bridge (PR #7890)
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

merged branch Ocramius/feature/proxy-manager-bridge (PR #7890)
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

merged branch Ocramius/feature/proxy-manager-bridge (PR #7890)
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
Member

localheinz commented May 30, 2013

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

Just trying to wrap my head around this.

Member

Ocramius commented May 30, 2013

@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

Merge pull request zendframework/zendframework#4146 from Ocramius/fea…
…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 join this conversation on GitHub. Already have an account? Sign in to comment