Skip to content
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

Initial approach #1

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
vendor
composer.lock
61 changes: 60 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
# Zend-Expressive-Bridge
# Zend-Expressive-Bridge

The easiest way to use PHP-DI with Zend Expressive, seems to:

- use the Dependency Injection Container Builder provided by this library using some default definition files
- add your own Dependency Definitions to the Container Builder
- use the Dependency Injection Container to fetch the Zend Expressive Application
- run the Application
- for example using the PHP internal WebServer: `php -S 0.0.0.0:8080 -t public/`

## In your `./public/index.php`

```php

<?php
// Delegate static file requests back to the PHP built-in WebServer
if (PHP_SAPI === 'cli-server' && is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) {
return false;
}

require __DIR__ . '/../vendor/autoload.php';

/* @var \Interop\Container\ContainerInterface $container */
$container = require __DIR__ . '/../config/container.php';

/* @var \Zend\Expressive\Application $app */
$app = $container->get(\Zend\Expressive\Application::class);
$app->run();

```
## In your `./config/container.php`


```php
<?php
$inProduction = false; //You probably want to use an environment variable for this...
Copy link
Member

Choose a reason for hiding this comment

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

Here is an idea: instead of requiring the containerBuilder.php file that is in vendor/ there could be instead a class, e.g. ExpressiveContainerBuilder, that extends from ContainerBuilder and auto-add the configuration files of this repository.

The advantage I see is it benefits from autoloading so it avoids the long line of include that goes looking in vendor/ (which is kind of unusual), and also the possibility to get rid of all the if(class_exists('Zend\Expressive\Router\AuraRouter')){ in the config files => the ExpressiveContainerBuilder could have methods dedicated to include configuration for libraries.

Here is an example of what it could look like:

// the $inProduction could be used to enable/disable Twig debug or other debug for example
$containerBuilder = new ExpressiveContainerBuilder($inProduction);
$containerBuilder->registerAuraRouter();
$containerBuilder->registerTwigRenderer();
// All methods of the container builder can still be used of course
$containerBuilder->addDefinitions(/* your own definition files */);

It's a bit less automatic than your current solution but relying on class_exists is risky IMO, it's not because a class is installed in vendor/ that you want to use it in your application. And that way it's much more explicit/easier to understand what's happening.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

mm, seems better indeed, will have a look at this!

$containerBuilder = new \DI\ExpressiveContainerBuilder($inProduction);
$containerBuilder->registerXYZRouter(); //Choose your preferred Router
$containerBuilder->registerXYZRenderer(); //Choose your preferred template Renderer
$containerBuilder->writeProxiesToFile($inProduction, __DIR__ . '/../data/cache'); //You probably want to use caching in production

/**
* Add your own, application-specific, Dependency Definitions to the Container Builder
* @link https://zend-expressive.readthedocs.io/en/latest/features/modular-applications/
*/
$pathToDependencyDefinitions = __DIR__ . '/../config/dependencies/{{,*.}global,{,*.}local}.php';
$phpFileProvider = new \Zend\ConfigAggregator\PhpFileProvider($pathToDependencyDefinitions);
$dependencyDefinitions = $phpFileProvider();
foreach ($dependencyDefinitions as $definitions) {
$containerBuilder->addDefinitions($definitions);
}

$container = $containerBuilder->build();

//Assign configuration to container
$config = require __DIR__ . '/../config.php';
$container->set('config', $config);

return $container;
```
28 changes: 28 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "php-di/zend-expressive-bridge",
"description": "PHP-DI integration in Zend Expressive",
"license": "MIT",
"type": "library",
"require": {
"php": "^7.1",
"php-di/php-di": "^6.0",
"zendframework/zend-expressive": "^2.0",
"zendframework/zend-expressive-helpers": "^4.0",
"http-interop/http-server-middleware": "^1.0",
"http-interop/http-middleware": "^0.5.0"
},
"autoload": {
"psr-4": {
"DI\\": "src/DI"
}
},
"suggest": {
"zendframework/zend-config-aggregator": "To easily add application specific Dependency Definitions to the ContainerBuilder",
"zendframework/zend-expressive-aurarouter": "To route requests using Aura.Router",
"zendframework/zend-expressive-fastroute": "To route requests using FastRoute",
"zendframework/zend-expressive-zendrouter": "To route requests using zend-mvc Router",
"zendframework/zend-expressive-platesrenderer": "To use Plates as templating engine",
"zendframework/zend-expressive-twigrenderer": "To use Twig as templating engine",
"zendframework/zend-expressive-zendviewrenderer": "To use zend-view PhpRenderer as templating engine"
}
}
101 changes: 101 additions & 0 deletions src/DI/ExpressiveContainerBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
declare(strict_types=1);

namespace DI;

class ExpressiveContainerBuilder extends ContainerBuilder
{
public function __construct(bool $inProduction = false, string $containerClass = 'DI\Container')
{
parent::__construct($containerClass);

$this->registerDependencies();
$this->registerErrorHandler($inProduction);
$this->registerMiddlewarePipeline();
}

private function registerDependencies(): void
{
$this->addDefinitions([
\Interop\Container\ContainerInterface::class => function (\Interop\Container\ContainerInterface $container) {
return $container;
},
\Zend\Expressive\Application::class => \DI\factory(\Zend\Expressive\Container\ApplicationFactory::class),
\Zend\Expressive\Container\ApplicationFactory::class => \DI\autowire(),
\Zend\Expressive\Helper\ServerUrlHelper::class => \DI\autowire(),
\Zend\Expressive\Helper\UrlHelper::class => \DI\factory(\Zend\Expressive\Helper\UrlHelperFactory::class),
]);
}

private function registerErrorHandler(bool $inProduction): void
{
$this->addDefinitions([
\Zend\Expressive\Container\WhoopsErrorHandlerFactory::class => \DI\autowire()->lazy(),
\Zend\Expressive\Container\WhoopsFactory::class => \DI\autowire()->lazy(),
\Zend\Expressive\Container\WhoopsPageHandlerFactory::class => \DI\autowire()->lazy(),
'Zend\Expressive\Whoops' => \DI\factory(\Zend\Expressive\Container\WhoopsFactory::class),
'Zend\Expressive\WhoopsPageHandler' => \DI\factory(\Zend\Expressive\Container\WhoopsPageHandlerFactory::class),
'Zend\Expressive\FinalHandler' => $inProduction ? \DI\factory(\Zend\Expressive\Container\TemplatedErrorHandlerFactory::class) : \DI\factory(\Zend\Expressive\Container\WhoopsErrorHandlerFactory::class),
]);
}

public function registerAuraRouter(): void
{
$this->addDefinitions([
\Zend\Expressive\Router\RouterInterface::class => \DI\get(\Zend\Expressive\Router\AuraRouter::class),
\Zend\Expressive\Router\AuraRouter::class => \DI\autowire(),
]);
}

public function registerFastRouteRouter(): void
{
$this->addDefinitions([
\Zend\Expressive\Router\RouterInterface::class => \DI\get(\Zend\Expressive\Router\FastRouteRouter::class),
\Zend\Expressive\Router\FastRouteRouter::class => \DI\autowire(),
]);
}

public function registerZendRouter(): void
{
$this->addDefinitions([
\Zend\Expressive\Router\RouterInterface::class => \DI\get(\Zend\Expressive\Router\ZendRouter::class),
\Zend\Expressive\Router\ZendRouter::class => \DI\autowire(),
]);
}

public function registerPlatesRenderer(): void
{
$this->addDefinitions([
\Zend\Expressive\Template\TemplateRendererInterface::class => \DI\factory(\Zend\Expressive\Plates\PlatesRendererFactory::class),
]);
}

public function registerTwigRenderer(): void
{
$this->addDefinitions([
\Zend\Expressive\Template\TemplateRendererInterface::class => \DI\factory(\Zend\Expressive\Twig\TwigRendererFactory::class),
]);
}

/**
* Note that ZendViewRendererFactory::injectHelpers() seems to use an incorrect 'default' HelperPluginManager resulting in:
* Deprecated: Zend\ServiceManager\AbstractPluginManager::__construct now expects a Interop\Container\ContainerInterface instance representing the parent container; please update your code in
*
* @see \Zend\Expressive\ZendView\ZendViewRendererFactory::injectHelpers()
*/
public function registerZendViewRenderer(): void
{
$this->addDefinitions([
\Zend\Expressive\Template\TemplateRendererInterface::class => \DI\factory(\Zend\Expressive\ZendView\ZendViewRendererFactory::class),
\Zend\View\HelperPluginManager::class => \DI\factory(\Zend\Expressive\ZendView\HelperPluginManagerFactory::class),
]);
}

private function registerMiddlewarePipeline(): void
{
$this->addDefinitions([
\Zend\Expressive\Helper\ServerUrlMiddleware::class => \DI\factory(\Zend\Expressive\Helper\ServerUrlMiddlewareFactory::class),
\Zend\Expressive\Helper\UrlHelperMiddleware::class => \DI\factory(\Zend\Expressive\Helper\UrlHelperMiddlewareFactory::class),
]);
}
}