Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/4.0' into 4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed Dec 12, 2013
2 parents bf5d2b5 + 0edef76 commit a07850d
Show file tree
Hide file tree
Showing 157 changed files with 4,205 additions and 6,720 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PHP-DI is a Container that makes [*Dependency Injection*](http://en.wikipedia.org/wiki/Dependency_injection)
as practical as possible.
PHP-DI is a Dependency Injection Container made for humans.

PHP-DI also tries to avoid falling into the trap of the "Service Locator" antipattern and help you do *real* dependency injection.
Its goal is to help you using [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection)
in your application, without writing boilerplate code or falling into the trap of the "Service Locator" antipattern.

[![Latest Stable Version](https://poser.pugx.org/mnapoli/php-di/v/stable.png)](https://packagist.org/packages/mnapoli/php-di) [![Total Downloads](https://poser.pugx.org/mnapoli/php-di/downloads.png)](https://packagist.org/packages/mnapoli/php-di)

Expand All @@ -12,9 +12,8 @@ PHP-DI also tries to avoid falling into the trap of the "Service Locator" antipa
* Supports different configuration alternatives to suit every taste:
* **Reflection**: zero configuration, intelligent guessing
* **Annotations**: modern, practical and simple
* **PHP code**: if you like complete control and auto-completion
* **PHP code**: for complete control and auto-completion
* **PHP array**: allows you to store it in a configuration file
* **YAML**: elegant and concise
* **Performances**: supports a large number of Caches
* Lazy injection: lazy-loading of dependencies
* Supports constructor injection, setter injection and property injection
Expand Down Expand Up @@ -62,7 +61,7 @@ Here is how a code using DI will roughly work:
* Foo calls Bar
* Bar does something

This is the pattern of **Inversion of Control**. The control of the dependencies is **inversed** from one being called to the one calling.
This is the pattern of **Inversion of Control**. The control of the dependencies is **inverted** from one being called to the one calling.

The main advantage: the one at the end of the caller chain is always **you**. So you can control every dependencies: you have a complete control on how your application works. You can replace a dependency by another (one you made for example).

Expand Down
23 changes: 23 additions & 0 deletions change-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,38 @@

## 4.0

Major changes:

* The configuration format has changed

BC-breaks:

* XML and JSON definitions have been removed
* `ContainerSingleton` has been removed
* You cannot configure an injection as lazy anymore, you can only configure a container entry as lazy
* The Container constructor now takes mandatory parameters. Use the ContainerBuilder to create a Container.
* Removed `ContainerBuilder::setDefinitionsValidation()` (no definition validation anymore)

All changes:

* [#115](https://github.com/mnapoli/PHP-DI/issues/115) Added `Container::has()`
* [#127](https://github.com/mnapoli/PHP-DI/issues/127) Added support for cases where PHP-DI is wrapped by another container (like Acclimate): PHP-DI can now use the wrapping container to perform injections
* [#128](https://github.com/mnapoli/PHP-DI/issues/128) Configure entry aliases
* [#110](https://github.com/mnapoli/PHP-DI/issues/110) XML definitions are not supported anymore
* [#122](https://github.com/mnapoli/PHP-DI/issues/122) JSON definitions are not supported anymore
* `ContainerSingleton` has finally been removed
* Added `ContainerBuilder::buildDevContainer()` to get started with a default container very easily.

## 3.5.1

* FIXED [#126](https://github.com/mnapoli/PHP-DI/issues/126): `Container::set` without effect if a value has already been set and retrieved

## 3.5

Read the [news entry](news/05-php-di-3-5.md).

* Importing `@Inject` and `@Injectable` annotations is now optional! It means that you don't have to write `use DI\Annotation\Inject` anymore
* FIXED [#124](https://github.com/mnapoli/PHP-DI/issues/124): `@Injects` annotation conflicts with other annotations

## 3.4

Expand Down
12 changes: 8 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
"DI": "src/",
"IntegrationTests": "tests/",
"UnitTests": "tests/"
}
},
"files": [
"src/DI/functions.php"
]
},
"require": {
"php": ">=5.3.0",
"php": ">=5.3.3",
"doctrine/annotations": "1.*",
"doctrine/cache": "1.*",
"mnapoli/phpdocreader": "1.0.*",
"myclabs/php-enum": "1.*",
"symfony/yaml": "2.*",
"ocramius/proxy-manager": "~0.3"
"ocramius/proxy-manager": "~0.3",
"symfony/yaml": "2.*"
}
}
12 changes: 11 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# Documentation

## Introduction

* [Introduction to dependency injection with an example](example.md)
* [Getting started](getting-started.md)
* [Best practices guide](best-practices.md)

## PHP-DI's documentation

* [Configure the container](container-configuration.md)
* [Define injections](definition.md)
* [Definition overriding](definition-overriding.md)
* [Lazy injection](lazy-injection.md)
* [Scopes](scopes.md)

Extra topics:
## Extra topics

* [Performances](performances.md)
* [Inject on an existing instance](inject-on-instance.md)
* [Injections depending on the environment](environments.md)

## Internals

* [Contribute](../CONTRIBUTING.md)
* [How PHP-DI works](how-it-works.md)

Expand Down
160 changes: 160 additions & 0 deletions doc/best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Best practices

This is an opinionated guide on how to use PHP-DI and dependency injection for the best.

While it may not cover every case, and satisfy everybody, it can serve as a canvas to help you
getting started with dependency injection.

If you disagree with anything explained in that guide, that's OK. It's opinionated, and you
should make your own opinion on all these subjects ;). It shouldn't prevent you to use PHP-DI
the way you want.

## Rules for using a container and dependency injection

Here are some basic rules to follow:

1. never get an entry from the container directly (always use dependency injection)
2. more generally, write code decoupled from the container
3. type-hint against interfaces, configure which implementation to use in the container's configuration


## Writing controllers

Using dependency injection in controllers is usually where it is the most painful.

If we take Symfony 2 as an example (but this generally applies to every framework), here are your options:

- inject the container in the controllers, and call `$container->get(...)`

This is bad, see the rule n°1.

- inject dependencies in the constructor ([controller as a service in Symfony](http://symfony.com/doc/current/cookbook/controller/service.html))

This is painful when you have more than 5 dependencies, and your constructor is
[15 lines of boilerplate code](http://www.whitewashing.de/2013/06/27/extending_symfony2__controller_utilities.html)

- **inject dependencies in properties**

This is the solution we recommend.

Example:

```php
class UserController
{
/**
* @Inject
* @var FormFactoryInterface
*/
private $formFactory;

public function createForm($type, $data, $options)
{
// $this->formFactory->...
}
}
```

As you can see, this solution requires very few code, is simple to understand and benefits from IDE support
(autocompletion, refactoring, …).

Property injection is generally frowned upon, and for good reasons:

- injecting in a private property breaks encapsulation
- it is not an explicit dependency: there is no contract saying your class need the property to be set to work
- if you use PHP-DI's annotations to mark the dependency to be injected, your class is dependent on the container (see the 2nd rule above)

BUT

if you follow general best practices on how to write your application, your controllers
will not contain business logic (only routing calls to the models and binding returned values to view).

So:

- you will not unit-test it (that doesn’t mean you won’t write functional tests on the interface though)
- you will not need to reuse it elsewhere
- if you change the framework, you may have to rewrite it (or parts of it) anyway
(because most dependencies like Request, Response, templating system, etc. will have changed)

This solution offers many benefits for no major drawback, so
**we recommend using annotations in controllers**.


## Writing services

Given a service is intended to be reused, tested and independent of your framework, **we do not recommend
using annotations for injecting dependencies**.

Instead, we recommend using **constructor injection and autowiring**:

```php
class OrderService implements OrderServiceInterface
{
private $paymentService;

public function __construct(PaymentServiceInterface $paymentService)
{
$this->paymentService = $paymentService;
}

public function processOrder($order)
{
$this->paymentService->...
}
}
```

By using autowiring (enabled by default), you save yourself binding every parameter
of the constructor in the configuration. PHP-DI will guess which object it needs to inject by checking
the types of your parameters.

Side note: as explained in rule n°3, we recommend **type-hinting against interfaces**. In that case,
you will need to map interfaces to the implementation the container should use in the configuration:

```php
<?php
// config.php
return [
// ...
OrderServiceInterface::class => DI\object(OrderService::class),
];
```


## Using libraries

When using libraries, like loggers, ORMs, … you sometimes need to configure them.

In that case, we advise you to define these dependencies in your configuration file.
We also recommend using an anonymous function when the configuration gets a bit complex.

**The anonymous function allows you to write real PHP code**, which is great, because you
can use the library's documentation, you get IDE support, and you are a PHP developer so
you already know the language ;).

Here is an example with [Monolog](https://github.com/Seldaek/monolog), a PHP logger:

```php
<?php
// config.php

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

return [
// ...

Psr\Log\LoggerInterface::class => DI\factory(function () {
$logger = new Logger('mylog');

$fileHandler = new StreamHandler('path/to/your.log', Logger::DEBUG);
$fileHandler->setFormatter(new LineFormatter());
$logger->pushHandler($fileHandler);

return $logger;
}),
];
```

Of course, as you can see, we used the PSR-3 interface for injections. That way we can replace Monolog
with any PSR-3 logger anytime we want, just by changing this configuration.
52 changes: 30 additions & 22 deletions doc/container-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,17 @@ PHP-DI's container is preconfigured for "plug'n'play", i.e. development environm
$container = new \DI\Container();
```

By default, PHP-DI will use `Reflection` and `Annotation` [definition sources](definition.md).
By default, PHP-DI will use `Autowiring` and `Annotation` [definition sources](definition.md).

To improve performances, you may want to use a simple Array cache. This kind of cache is cleared on every request, so you will not need to empty it manually when you change the code. It is also recommended to enable definition validation to detect errors as soon as possible.
To improve performances, you may want to use a simple Array cache. This kind of cache is cleared on every request, so you will not need to empty it manually when you change the code.

```php
$builder = new \DI\ContainerBuilder();
$builder->setDefinitionCache(new Doctrine\Common\Cache\ArrayCache());
$builder->setDefinitionsValidation(true);

$container = $builder->build();
```

### Adding a definition file

If you defining injections in a file, use the container builder:

```php
use DI\Definition\FileLoader\YamlDefinitionFileLoader;

$builder->addDefinitionsFromFile(new YamlDefinitionFileLoader('config/di.yml'));
```

If you don't want to use Reflection or Annotations, disable them:

```php
$builder->useReflection(false);
$builder->useAnnotations(false);
```

Read more about [definitions](definition.md).

## Production environment
Expand All @@ -48,7 +30,6 @@ In production environment, you will of course favor speed:
$builder = new \DI\ContainerBuilder();
$builder->setDefinitionCache($cache);
$builder->writeProxiesToFile(true, 'tmp/proxies');
$builder->setDefinitionsValidation(false);

$container = $builder->build();
```
Expand All @@ -63,7 +44,34 @@ If you want to use PHP-DI's container as a simple container, you will want to di
$builder = new \DI\ContainerBuilder();
$builder->useReflection(false);
$builder->useAnnotations(false);
$builder->setDefinitionsValidation(false);

$container = $builder->build();
```

## Using PHP-DI with other containers

If you want to use several containers at once, for example to use PHP-DI in ZF2 or Symfony 2, you can
use a tool like [Acclimate](https://github.com/jeremeamia/acclimate).

You will just need to tell PHP-DI to look into the composite container, else PHP-DI will be unaware
of Symfony's container entries.

Example with Acclimate:

```php
$container = new CompositeContainer();

// Add Symfony's container
$container->addContainer($acclimate->adaptContainer($symfonyContainer));

// Configure PHP-DI container
$builder = new ContainerBuilder();
$builder->wrapContainer($container);

// Add PHP-DI container
$phpdiContainer = $builder->build();
$container->addContainer($acclimate->adaptContainer($phpdiContainer));

// Good to go!
$foo = $container->get('foo');
```
Loading

0 comments on commit a07850d

Please sign in to comment.