All Projects → nette → Di

nette / Di

Licence: other
💎 Flexible, compiled and full-featured Dependency Injection Container with perfectly usable autowiring and support for all new PHP 7 features.

Projects that are alternatives of or similar to Di

Cookbook
🎶 Cookbook for Nette Framework (@nette) & Contributte (@contributte). Read it while its HOT!
Stars: ✭ 30 (-95.35%)
Mutual labels:  nette, nette-framework, dependency-injection
orm
🔥 Well-integrated Doctrine ORM for Nette Framework
Stars: ✭ 51 (-92.09%)
Mutual labels:  nette, nette-framework
logging
💥 Universal logging support to Tracy / Nette Framework (@nette)
Stars: ✭ 18 (-97.21%)
Mutual labels:  nette, nette-framework
Latte
☕ Latte: the intuitive and fast template engine for those who want the most secure PHP sites.
Stars: ✭ 616 (-4.5%)
Mutual labels:  nette, nette-framework
Bootstrap
🅱 The simple way to configure and bootstrap your Nette application.
Stars: ✭ 524 (-18.76%)
Mutual labels:  nette, nette-framework
application
✨ Extra contrib to nette/application (@nette)
Stars: ✭ 23 (-96.43%)
Mutual labels:  nette, nette-framework
codeception
▶️ Integration of Nette Framework to Codeception.
Stars: ✭ 27 (-95.81%)
Mutual labels:  nette, nette-framework
fileupload
🆙 File uploads on steroids for Nette Framework (@nette). Implements blueimp/jquery-file-upload.
Stars: ✭ 28 (-95.66%)
Mutual labels:  nette, nette-framework
migrations
🏃 Doctrine Migrations for Nette Framework
Stars: ✭ 36 (-94.42%)
Mutual labels:  nette, nette-framework
psr7-http-message
💫 PSR #7 [HTTP Message Interface] to Nette Framework (@nette)
Stars: ✭ 17 (-97.36%)
Mutual labels:  nette, nette-framework
Forms
📝 Generating, validating and processing secure forms in PHP. Handy API, fully customizable, server & client side validation and mature design.
Stars: ✭ 272 (-57.83%)
Mutual labels:  nette, nette-framework
NetteAdapterForSymfonyBundles
[DEPRECATED due to only 20 downloads per 2 years] Read an article about this idea
Stars: ✭ 15 (-97.67%)
Mutual labels:  nette, dependency-injection
reCAPTCHA
‼️ Google reCAPTCHA (security) for Nette Framework \ Forms
Stars: ✭ 35 (-94.57%)
Mutual labels:  nette, nette-framework
slim-nette-extension
Nette Extension for Slim API micro-framework using middlewares.
Stars: ✭ 17 (-97.36%)
Mutual labels:  nette, nette-framework
NiftyGrid
DataGrid for Nette Framework
Stars: ✭ 34 (-94.73%)
Mutual labels:  nette, nette-framework
playground
📚 Examples, projects, webprojects, skeletons for Nette Framework (@nette) from community members. Included @contributte @apitte @nettrine projects.
Stars: ✭ 23 (-96.43%)
Mutual labels:  nette, nette-framework
Mail
📧 Handy email creation and transfer library for PHP with both text and MIME-compliant support.
Stars: ✭ 288 (-55.35%)
Mutual labels:  nette, nette-framework
web-project
Standard Web Project: a simple skeleton application using the Nette
Stars: ✭ 88 (-86.36%)
Mutual labels:  nette, nette-framework
image-storage
🌠 Image storage for Nette framework
Stars: ✭ 27 (-95.81%)
Mutual labels:  nette, nette-framework
command-line
⌨ Command line options and arguments parser.
Stars: ✭ 35 (-94.57%)
Mutual labels:  nette, nette-framework

Nette Dependency Injection (DI)

Downloads this Month Tests Coverage Status Latest Stable Version License

Introduction

Purpose of the Dependecy Injection (DI) is to free classes from the responsibility for obtaining objects that they need for its operation (these objects are called services). To pass them these services on their instantiation instead.

Nette DI is one of the most interesting part of framework. It is compiled DI container, extremely fast and easy to configure.

Documentation can be found on the website.

Support Me

Do you like Nette DI? Are you looking forward to the new features?

Buy me a coffee

Thank you!

Installation

The recommended way to install is via Composer:

composer require nette/di

It requires PHP version 8.0.

Usage

Let's have an application for sending newsletters. The code is maximally simplified and is available on the GitHub.

We have the object representing email:

class Mail
{
	public $subject;
	public $message;
}

An object which can send emails:

interface Mailer
{
	function send(Mail $mail, $to);
}

A support for logging:

interface Logger
{
	function log($message);
}

And finally, a class that provides sending newsletters:

class NewsletterManager
{
	private $mailer;
	private $logger;

	function __construct(Mailer $mailer, Logger $logger)
	{
		$this->mailer = $mailer;
		$this->logger = $logger;
	}

	function distribute(array $recipients)
	{
		$mail = new Mail;
		...
		foreach ($recipients as $recipient) {
			$this->mailer->send($mail, $recipient);
		}
		$this->logger->log(...);
	}
}

The code respects Dependency Injection, ie. each object uses only variables which we had passed into it.

Also, we have a ability to implement own Logger or Mailer, like this:

class SendMailMailer implements Mailer
{
	function send(Mail $mail, $to)
	{
		mail($to, $mail->subject, $mail->message);
	}
}

class FileLogger implements Logger
{
	private $file;

	function __construct($file)
	{
		$this->file = $file;
	}

	function log($message)
	{
		file_put_contents($this->file, $message . "\n", FILE_APPEND);
	}
}

DI container is the supreme architect which can create individual objects (in the terminology DI called services) and assemble and configure them exactly according to our needs.

Container for our application might look like this:

class Container
{
	private $logger;
	private $mailer;

	function getLogger()
	{
		if (!$this->logger) {
			$this->logger = new FileLogger('log.txt');
		}
		return $this->logger;
	}

	function getMailer()
	{
		if (!$this->mailer) {
			$this->mailer = new SendMailMailer;
		}
		return $this->mailer;
	}

	function createNewsletterManager()
	{
		return new NewsletterManager($this->getMailer(), $this->getLogger());
	}
}

The implementation looks like this because:

  • the individual services are created only on demand (lazy loading)
  • doubly called createNewsletterManager will use the same logger and mailer instances

Let's instantiate Container, let it create manager and we can start spamming users with newsletters :-)

$container = new Container;
$manager = $container->createNewsletterManager();
$manager->distribute(...);

Significant to Dependency Injection is that no class depends on the container. Thus it can be easily replaced with another one. For example with the container generated by Nette DI.

Nette DI

Nette DI is the generator of containers. We instruct it (usually) with configuration files. This is configuration that leads to generate nearly the same class as the class Container above:

services:
	- FileLogger( log.txt )
	- SendMailMailer
	- NewsletterManager

The big advantage is the shortness of configuration.

Nette DI actually generates PHP code of container. Therefore it is extremely fast. Developer can see the code, so he knows exactly what it is doing. He can even trace it.

Usage of Nette DI is very easy. Save the (above) configuration to the file config.neon and let's create a container:

$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function($compiler) {
    $compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;

and then use container to create object NewsletterManager and we can send e-mails:

$manager = $container->getByType(NewsletterManager::class);
$manager->distribute(['[email protected]', ...]);

The container will be generated only once and the code is stored in cache (in directory __DIR__ . '/temp'). Therefore the loading of configuration file is placed in the closure in $loader->load(), so it is called only once.

During development it is useful to activate auto-refresh mode which automatically regenerate the container when any class or configuration file is changed. Just in the constructor ContainerLoader append true as the second argument:

$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', true);

Services

Services are registered in the DI container and their dependencies are automatically passed.

services:
	manager: NewsletterManager

All dependencies declared in the constructor of this service will be automatically passed. Constructor passing is the preferred way of dependency injection for services.

If we want to pass dependencies by the setter, we can add the setup section to the service definition:

services:
	manager:
		factory: NewsletterManager
		setup:
			- setAnotherService

Class of the service:

class NewsletterManager
{
	private $anotherService;

	public function setAnotherService(AnotherService $service)
	{
		$this->anotherService = $service;
	}

...

We can also add the inject: yes directive. This directive will enable automatic call of inject* methods and passing dependencies to public variables with @inject annotations:

services:
	foo:
		factory: FooClass
		inject: yes

Dependency Service1 will be passed by calling the inject* method, dependency Service2 will be assigned to the $service2 variable:

class FooClass
{
	private $service1;

	// 1) inject* method:

	public function injectService1(Service1 $service)
	{
		$this->service1 = $service1;
	}

	// 2) Assign to the variable with the @inject annotation:

	/** @inject @var Service2 */
	public $service2;
}

However, this method is not ideal, because the variable must be declared as public and there is no way how you can ensure that the passed object will be of the given type. We also lose the ability to handle the assigned dependency in our code and we violate the principles of encapsulation.

Factories

We can use factories generated from an interface. The interface must declare the returning type in the @return annotation of the method. Nette will generate a proper implementation of the interface.

The interface must have exactly one method named create. Our factory interface could be declared in the following way:

interface IBarFactory
{
	/**
	 * @return Bar
	 */
	public function create();
}

The create method will instantiate an Bar with the following definition:

class Bar
{
	private $logger;

	public function __construct(Logger $logger)
	{
		$this->logger = $logger;
	}
}

The factory will be registered in the config.neon file:

services:
	- IBarFactory

Nette will check if the declared service is an interface. If yes, it will also generate the corresponding implementation of the factory. The definition can be also written in a more verbose form:

services:
	barFactory:
		implement: IBarFactory

This full definition allows us to declare additional configuration of the object using the arguments and setup sections, similarly as for all other services.

In our code, we only have to obtain the factory instance and call the create method:

class Foo
{
	private $barFactory;

	function __construct(IBarFactory $barFactory)
	{
		$this->barFactory = $barFactory;
	}

	function bar()
	{
		$bar = $this->barFactory->create();
	}
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].