Download the PHP package aimfeld/zend-di-compiler without Composer

On this page you can find all versions of the php package aimfeld/zend-di-compiler. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package zend-di-compiler

NB: This repo is no longer actively maintained. Since laminas-di has changed a lot as of version 3 and now has better support for ahead-of-time (aot) factory code generation, I suggest using laminas-di directly.

Table of Contents

Introduction

Are you tired of writing tons of factory code (closures) for the Laminas\ServiceManager in your Zend Framework 2 application? Are outdated factory methods causing bugs? This can all be avoided by using ZendDiCompiler!

ZendDiCompiler is a Zend Framework 2 module that uses auto-generated factory code for dependency-injection. It saves you a lot of work, since there's no need anymore for writing Laminas\ServiceManager factory closures and keeping them up-to-date manually.

ZendDiCompiler scans your code using Laminas\Di and creates factory methods automatically. If the factory methods are outdated, ZendDiCompiler updates them in the background. Therefore, you develop faster, avoid bugs due to outdated factory methods, and experience great performance in production!

Features

Caveats

Installation

This module is available on Packagist. In your project's composer.json use:

For PHP 7, install version 2.x, for PHP 5.4-5.6, use version 1.x.

Make sure you have a writable data folder in your application root directory, see ZendSkeletonApplication. Put a .gitignore file in it with the following content:

Add 'ZendDiCompiler' to the modules array in your application.config.php. ZendDiCompiler must be loaded after the modules where it is used:

Usage

Dependency injection container vs. service locator

Is ZendDiCompiler a dependency injection container (DiC) or a service locator (SL)? Well, that depends on where you use it. ZendDiCompiler can be used as a DiC to create your controllers in your module class and inject the controller dependencies from outside. Pure DiC usage implies that ZendDiCompiler is used only during the bootstrap process and disposed before the controller is dispatched. This has been coined the "Register Resolve Release pattern" and is the recommended way by experts like Mark Seemann and others.

As soon as you inject the ZendDiCompiler itself into your controllers and other classes, you are using it as a service locator. The ZF2 MVC architecture is based on controller classes with action methods. Given this architecture, controller dependencies become numerous very quickly. In order to avoid bloated controller constructors, it makes sense to inject ZendDiCompiler as a single dependency into ZF2 controller classes and use it to pull the other dependencies from inside the controllers. This means using it as a service locator, just like Laminas\ServiceManager is typically used.

ZendDiCompiler is also used as a service locator inside of the provided ZendDiCompiler\DiFactory which is very useful for creating runtime objects with dependencies. This avoids a lot of abstract factory code you would otherwise have to write. Besides ZF2 controllers, I recommend not to inject ZendDiCompiler directly anywhere. If you need a service in one of your classes, just ask for it in the constructor. If you need to create runtime objects with dependencies, inject DiFactory or your extended version of it with custom creation methods.

Configuration

ZendDiCompiler uses standard Laminas\Di configuration (which is not well documented yet). To make things easier, see example.config.php for examples of how to specify:

For a full list of configuration options, see module.config.php

ZendDiCompiler creates a GeneratedServiceLocator class in the data directory and automatically refreshes it when constructors change during development. However, if you e.g. change parameters in the instance configuration, you have to manually delete data/GeneratedServiceLocator.php to force a refresh. In your staging and production deployment/update process, make sure that data/GeneratedServiceLocator.php is deleted!

Shared instances

You need to provide shared instances to ZendDiCompiler::addSharedInstances() in your application module's onBootstrap() method in the following cases (also see example below):

Note that ZendDiCompiler provides some default shared instances automatically (see ZendDiCompiler::getDefaultSharedInstances()). The following default shared instances can be constructor-injected without explicitly adding them:

Type preferences

It is common to inject interfaces or abstract classes. Let's have a look at interface injection (for abstract classes, it works the same).

We need to tell ZendDiCompiler which implementing class to inject for ExampleInterface. We specify ExampleImplementor as a type preference for ExampleInterface in our example config:

ZendDiCompiler will now always inject ExampleImplementor for ExampleInterface. Calling ZendDiCompiler::get('ZendDiCompiler\Example\ExampleInterface') will return the ExampleImplementor.

Type preferences can not only be used for interfaces and abstract classes, but for substituting classes in general. They can even be used to deal with non-existing classes:

Calling ZendDiCompiler::get('ZendDiCompiler\Example\NotExists') will return a ZendDiCompiler\Example\Exists instance. Believe it or not, there are actually some good use cases for this.

Examples

All examples sources listed here are included as source code.

Using ZendDiCompiler to create a controller

Let's say we want to use the ZendDiCompiler to create a controller class and inject some dependencies. For illustriation, we also inject the ZendDiCompiler itself into the controller. As mentioned above, it is a moot topic whether this is a good idea or not. But if we decide to use the ZendDiCompiler inside the controller to get other dependencies, we can either inject it in the constructor or pull it from the ZF2 service locator using $this->serviceLocator->get('ZendDiCompiler').

In our example, we have the following classes:

ExampleController

ServiceA with a dependency on ServiceB

ServiceB with a constructor parameter of unspecified type:

ServiceC which requires complicated runtime initialization and will be added as shared instance.

We add the example source directory as a scan directory for ZendDiCompiler. Since ServiceB has a parameter of unspecified type, we have to specify a value to inject. A better approach for ServiceB would be to require the Config in its constructor and retrieve the parameter from there, so we wouldn't need to specify an instance configuration. The configuration for our example looks like this:

Now we can create the ExampleController in our application's module class. For convenience, we retrieve the ZendDiCompiler from the service manager and assign it to a local variable ($this->zendDiCompiler = $sm->get('ZendDiCompiler')). This makes it easier for writing getControllerConfig() or getViewHelperConfig() callbacks.

Since the ServiceC dependency requires some complicated initialization, we need to initialize it and add it as a shared instance to ZendDiCompiler.

Using the DiFactory to create runtime objects with dependencies

It is useful to distinguish two types of objects: services and runtime objects. For services, all parameters should be specified in the configuration (e.g. a config array wrapped in a Laminas\Config\Config object). If class constructors e.g. in third party code require some custom parameters, they can be specified in the instance configuration).

Runtime objects, on the other hand, require at least one parameter which is determined at runtime only. ZendDiCompiler provides ZendDiCompiler\DiFactory to help you create runtime objects and inject their dependencies.

Passing all runtime parameters in a single array

If you follow the convention of passing runtime parameters in a single array named $zdcParams as in RuntimeA, things are very easy (the array name(s) can be configured in module.config.php):

ZendDiCompiler automatically injects ZendDiCompiler\DiFactory as a default shared instance. So we can just use it to create RuntimeA objects in ServiceD. RuntimeA's dependencies (the Config default shared instance and ServiceA) are injected automatically, so you only need to provide the runtime parameters:

Passing custom runtime parameters

If you can't or don't want to follow the convention of passing all runtime parameters in a single $zdcParams array, ZendDiCompiler still is very useful. In that case, you can just extend a custom factory from ZendDiCompiler\DiFactory and add your specific creation methods. RuntimeB requires two separate run time parameters:

So we extend ExampleDiFactory from ZendDiCompiler\DiFactory and write a creation method createRuntimeB:

In ServiceE, we inject our extended factory. If the extended factory is located in a directory scanned by ZendDiCompiler, we don't need to provide it as a shared instance. Now we can create RuntimeB objects as follows:

Generated code and info

Factory code

ZendDiCompiler will automatically generate a service locator in the data/ZendDiCompiler directory and update it if constructors are changed during development. Services can be created/retrieved using ZendDiCompiler::get(). If you need a new dependency in one of your classes, you can just put it in the constructor and ZendDiCompiler will inject it for you.

Just for illustration, this is the generated service locator created by ZendDiCompiler and used in ZendDiCompiler::get().

Code scan log

ZendDiCompiler logs problems found during code scanning in data/ZendDiCompiler/code-scan.log. If you can't retrieve an object from ZendDiCompiler, you will probably find the reason in this log. The most common problem is that you have untyped scalar parameters instead of a Laminas\Di configuration. Here's an example of the code scan log showing some problems:

In case of simple value objects without any service dependencies, I do not use dependency injection but create then with new, e.g. ConditionResult::__construct($isTrue, $isCacheable, $allowFlip = true). These objects are not meant to be created with the DiFactory and therefore, the DEBUG notice can be ignored.

Component dependency info

As a bonus, ZendDiCompiler will write a component-dependency-info.txt file containing information about which of the scanned components depend on which classes.

Scanned classes are grouped into components (e.g. the Laminas\Mvc\MvcEvent class belongs to the Laminas\Mvc component). For every component, all constructor-injected classes are listed. This helps you analyze which components depend on which classes of other components. Consider organizing your components into layers. Each layer should depend on classes of the same or lower layers only. Note that only constructor-injection is considered for this analysis, so the picture might be incomplete.

Here's an example of what you might see:


All versions of zend-di-compiler with dependencies

PHP Build Version
Package Version
Requires php Version >=7.0
laminas/laminas-config Version >=2.6
laminas/laminas-di Version >=2.6
laminas/laminas-servicemanager Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package aimfeld/zend-di-compiler contains the following files

Loading the files please wait ....