Skip to content

Commit

Permalink
Merge pull request #18 from mindplay-dk/provider-dependencies
Browse files Browse the repository at this point in the history
Provider Requirements
  • Loading branch information
mindplay-dk authored Mar 10, 2024
2 parents 040ef85 + 4905706 commit 25d5dc4
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 1,008 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ jobs:
php:
- '8.0'
- '8.1'
- '8.2'
coverage:
- none
include:
- php: '8.2'
- php: '8.3'
coverage: xdebug
steps:
- uses: shivammathur/[email protected]
Expand All @@ -28,7 +29,7 @@ jobs:
fetch-depth: 2 # required by Scrutinizer
- run: composer install --no-progress --no-ansi --no-interaction --dev --prefer-dist
- run: php test/test.php
- if: github.repository == 'mindplay-dk/unbox' && matrix.coverage == 'xdebug'
- if: matrix.coverage == 'xdebug'
uses: sudo-bot/action-scrutinizer@latest
with:
cli-args: '--format=php-clover test/build/clover.xml'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
vendor
.*
!.git*
/composer.lock
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ ref(string $name) : BoxedValueInterface # create a boxed referenc

registerFallback(ContainerInterface $container) # register a fallack container

requires(string $requirement, string $description) # defines a Requirement
provides(string $requirement, string $description) # fulfills an abstract Requirement

createContainer() : Container # create a bootstrapped Container instance
```

Expand Down Expand Up @@ -524,6 +527,82 @@ for a quick development setup. Even if somebody wanted to override some of the r
in e.g. your default development setup, they can of course still do that, e.g. by calling
`register()` again to override components as needed.

##### Provider Requirements

In large, modular architectures, you may have many Providers with inter-dependencies, which
can become difficult to manage at scale.

Since Providers exist *outside* the realm of the Container, the concept of Requirements can
be used to define verifiable provider interdependencies, which will be checked at the time
when `createContainer()` is called.

Requirements may be defined by calling `requires()`, and more than one Provider may specify the
same Requirement - possibly for different reasons, which may be described using the optional
`$description` argument.

Requirements may be fulfilled by calling either `register()` or `provides()`.

###### Component Requirements

Providers may depend on the consumer to manually register a component.

For example, the following Provider requires you to register a `PDO` connection instance:

```php
class MyProvider implements ProviderInterface
{
public function register(ContainerFactory $factory)
{
$factory->requires(PDO::class, "a PDO instance connected to a MySQL database");

// ...
}
}
```

Attempting to bootstrap this Provider, without manually registering the `PDO` instance, will
generate an Exception, as soon as `createContainer()` is called - which is much easier to debug
than the `NotFoundException` you would otherwise get, and which might not occur until you try
to resolve a component that actually depends on the database connection.

###### Abstract Requirements

Providers may have abstract Requirements - something that can't be expressed by a simple
component dependency.

For example, the following Provider requires you to simply indicate that you've bootstrapped
a "payment gateway" - whatever that means to the Provider in question:

```php
class MyProvider implements ProviderInterface
{
public function register(ContainerFactory $factory)
{
$factory->requires("acme.payment-gateway", "please refer to acme's documentation");

// ...
}
}
```

Another provider needs to explicitly indicate fulfillment of this abstract Requirement:

```php
class MyPaymentProvider implements ProviderInterface
{
public function register(ContainerFactory $factory)
{
$factory->provides("acme.payment-gateway");

// ...
}
}
```

Note that abstract Requirements should be a last resort - component dependencies are
*generally* simpler and easier to understand. This feature exists primarily to support
complex, large-scale modular frameworks.

### Fallback Containers

You can use this feature to build layered architecture with different component life-cycles.
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Upgrading
=========

#### 3.1.0

This feature-release introduces requirements (via `requires` and `provides`) and requires PHP 8.0 or later.

#### 3.0.0

This release removes backwards compatibility with the legacy package `container-interop/container-interop`
Expand Down
7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
},
"require-dev": {
"mindplay/benchpress": "^0.1",
"mindplay/testies": "^1.0",
"mindplay/testies": "^1.1.2",
"php-di/php-di": "^7.0.5",
"pimple/pimple": "^3.5",
"phpunit/php-code-coverage": "^9.2.5"
"phpunit/php-code-coverage": "^9 || ^10 || ^11"
},
"scripts": {
"test": "XDEBUG_MODE=coverage php test/test.php"
},
"autoload": {
"psr-4": {
Expand Down
Loading

0 comments on commit 25d5dc4

Please sign in to comment.