Kaspi/di-container — это легковесный контейнер внедрения зависимостей для PHP >= 8.0.
composer require kaspi/di-container- Autowire - контейнер автоматически создаёт и внедряет зависимости.
- Поддержка "zero configuration for dependency injection" - когда ненужно объявлять зависимость в определениях контейнера. Если класс не имеет зависимостей или зависит только от других конкретных классов, контейнеру не нужно указывать, как разрешить этот класс.
- Поддержка Php-атрибутов для конфигурирования сервисов в контейнере.
- Поддержка тегов (tags) для определений и сервисов в контейнере.
Определения классов:
// src/Services/Envelope.php
namespace App\Services;
// Класс для создания сообщения
class Envelope {
public function subject(string $subject): static {
// ...
return $this;
}
public function message(string $message): static {
// ...
return $this;
}
}// src/Services/Mail.php
namespace App\Services;
// Сервис отправки почты
class Mail {
public function __construct(private Envelope $envelope) {}
public function envelop(): Envelope {
return $this->envelope;
}
public function send(): bool {
// отправка сообщения
}
}// src/Models/Post.php
namespace App\Models;
// Модель данных — пост в блоге.
class Post {
public string $title;
// ...
}// src/Controllers/PostController.php
namespace App\Controllers;
use App\Services\Mail;
use App\Models\Post;
// Контроллер для обработки действия.
class PostController {
public function __construct(private Mail $mail) {}
public function send(Post $post): bool {
$this->mail->envelop()
->subject('Publication success')
->message('Post <'.$post->title.'> was published.');
return $this->mail->send();
}
}use App\Controllers\PostController;
use App\Models\Post;
use Kaspi\DiContainer\DiContainerFactory;
// Создать контейнер.
$container = (new DiContainerFactory())->make();
// more code...
//Заполняем модель данными.
$post = new Post();
$post->title = 'Publication about DiContainer';
// получить класс PostController с внедренным сервисом Mail и выполнить метод "send"
$postController = $container->get(PostController::class);
$postController->send($post);Note
Контейнер "пытается" самостоятельно определить запрашиваемую зависимость - является ли это классом или callable типом.
DiContainer выполнит следующие действия для App\Controllers\PostController:
$post = new App\Controllers\PostController(
new App\Services\Mail(
new App\Services\Envelope()
)
);Tip
Реализация кода в примере
Другой вариант для примера выше можно использовать для получения результата DiContainer::call():
use App\Controllers\PostController;
use App\Models\Post;
$post = new Post();
$post->title = 'Publication about DiContainer';
// ...
// получить класс PostController с внедренным сервисом Mail и выполнить метод "send"
$container->call(
definition: [PostController::class, 'send'],
arguments: ['post' => $post]
);Tip
Больше информации о DiContainer::call()
Note
Примеры использования пакета kaspi/di-container в репозитории
Для конфигурирования контейнера используется класс
Kaspi\DiContainer\DiContainerConfig::class
который реализует интерфейс
Kaspi\DiContainer\Interfaces\DiContainerConfigInterface
use Kaspi\DiContainer\{DiContainerConfig, DiContainer};
$diConfig = new DiContainerConfig(
// Ненужно объявлять каждую зависимость.
// Если класс, функция или интерфейс существуют
// и может быть запрошен через автозагрузку (например через composer),
// то объявлять каждое определение необязательно.
useZeroConfigurationDefinition: true,
// Использовать Php-атрибуты для конфигурирования зависимостей контейнера.
useAttribute: true,
// Возвращать всегда одни и тот же объект (singleton pattern).
isSingletonServiceDefault: false,
);
// передать настройки в контейнер
$container = new DiContainer(config: $diConfig);Можно использовать фабрику для создания контейнера с настроенными по умолчанию параметрами:
use Kaspi\DiContainer\DiContainerFactory;
$container = (new DiContainerFactory())->make();Конструктор фабрики:
DiContainerFactory::__construct(
?Kaspi\DiContainer\Interfaces\DiContainerConfigInterface $config = null
)Tip
Можно передать другую конфигурацию контейнера в фабрику.
Зарегистрировать сконфигурированные определения (сервисы) в контейнере:
DiContainerFactory::make(
iterable $definitions = []
): \Kaspi\DiContainer\Interfaces\DiContainerInterfaceNote
Некоторые интерфейсы или классы всегда возвращают текущий контейнер зависимостей. При разрешении зависимости для интерфейсов и классов:
Psr\Container\ContainerInterface::classKaspi\DiContainer\Interfaces\DiContainerInterface::classKaspi\DiContainer\DiContainer::class
будет получен текущий class Kaspi\DiContainer\DiContainer::class
use Kaspi\DiContainer\DiContainerFactory;
function testFunc(\Psr\Container\ContainerInterface $c) {
return $c;
}
$container = (new DiContainerFactory())->make();
var_dump($container->call('testFunc') instanceof DiContainer); // trueuse Kaspi\DiContainer\DiContainerFactory;
use Psr\Container\ContainerInterface;
class TestClass {
public function __construct(
public ContainerInterface $container
) {}
}
$container = (new DiContainerFactory())->make();
var_dump($container->get(TestClass::class)->container instanceof DiContainer); // trueСобирает определения для контейнера зависимостей из разных конфигурационных файлов (dependency definitions), и выполняет "импорт" классов из директорий.
Подробное описание использования DefinitionsLoader.
- 🐘 DiContainer с конфигурированием в стиле php определений.
- #️⃣ DiContainer c конфигурированием через PHP атрибуты.
- 📦 DiContainer::call() для вызова чистых
callableтипов и дополнительных определений. - 🔖 Тэгирование определений и сервисов.
Прогнать тесты без подсчёта покрытия кода
composer testЗапуск тестов с проверкой покрытия кода тестами
./vendor/bin/phpunitДля статического анализа используем пакет PHPStan.
composer stat./vendor/bin/phpstanДля приведения кода к стандартам используем php-cs-fixer который объявлен в dev зависимости composer-а
composer fixerУказать образ с версией PHP можно в файле .env в ключе PHP_IMAGE.
По умолчанию контейнер собирается с образом php:8.0-cli-alpine.
docker-compose builddocker-compose run --rm php composer install🔔 Если установлен make в системе:
make installЗапуск тестов без отчёта о покрытии кода:
docker-compose run --rm php vendor/bin/phpunit --no-coverage🔔 Если установлен make в системе:
make testПрогнать тесты с отчётом о покрытии кода:
docker-compose run --rm php vendor/bin/phpunit🔔 Если установлен make в системе:
make test-cover⛑ pезультаты будут в папке
.coverage-html
docker-compose run --rm php vendor/bin/phpstanесли установлен make в системе:
make statЕсли установлен make – запуск проверки code-style, stat analyzer, tests:
make allМожно работать в shell оболочке в docker контейнере:
docker-compose run --rm php sh