Kaspi/di-container — это легковесный контейнер внедрения зависимостей для PHP >= 8.1
composer require kaspi/di-container- Autowire – контейнер автоматически создаёт и внедряет зависимости.
- Поддержка "zero configuration for dependency injection" – когда ненужно объявлять зависимость в определениях контейнера. Если класс не имеет зависимостей или зависит только от других конкретных классов, контейнеру не нужно указывать, как разрешить этот класс.
- Поддержка Php-атрибутов для конфигурирования сервисов в контейнере.
- Поддержка тегов (tags) для определений и сервисов в контейнере.
- Компиляция контейнера – генерация настроенного контейнера в PHP-код оптимизированный специально для вашей конфигурации и ваших классов.
Определения классов:
// 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\DiContainerBuilder;
// Создать контейнер.
$container = (new DiContainerBuilder())
->build();
// 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
Реализация кода в примере
Другой вариант для примера выше можно использовать для получения результата метод контейнера 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'],
post: $post
);Tip
Больше информации о методе call()
Note
Примеры использования пакета kaspi/di-container в репозитории
Для конфигурирования контейнера используется класс
\Kaspi\DiContainer\DiContainerConfig
который реализует интерфейс
\Kaspi\DiContainer\Interfaces\DiContainerConfigInterface.
\Kaspi\DiContainer\Interfaces\DiContainerConfigInterface::isUseZeroConfigurationDefinition(): bool;Не нужно указывать контейнеру, как разрешить конкретный PHP-класс если класс не имеет зависимостей, или зависит только от других конкретных классов, или зависит от ранее сконфигурированных классов (интерфейсов).
\Kaspi\DiContainer\Interfaces\DiContainerConfigInterface::isUseAttribute(): bool;Предоставляет возможность конфигурирования определений на базе PHP атрибутов.
\Kaspi\DiContainer\Interfaces\DiContainerConfigInterface::isSingletonServiceDefault(): bool;Для определений в контейнере можно указать как разрешать сервис – возвращать всегда одни и тот же объект
или создавать объект сервиса каждый раз при получении через метод контейнера get().
Для определений контейнера у которых неуказан способ получения через метод контейнера get()
применяется значение по умолчанию из конфигурации.
Пример конфигурации:
use Kaspi\DiContainer\{DiContainerConfig, DiContainerBuilder};
$diConfig = new DiContainerConfig(
useZeroConfigurationDefinition: false,
useAttribute: false,
isSingletonServiceDefault: true,
);
// передать настройки в построитель контейнера
$container = (new DiContainerBuilder(containerConfig: $diConfig))
->build();Некоторые интерфейсы или классы всегда возвращают текущий контейнер зависимостей. При разрешении зависимости для интерфейсов и классов:
Psr\Container\ContainerInterface::classKaspi\DiContainer\Interfaces\DiContainerInterface::classKaspi\DiContainer\DiContainer::class
будет получен текущий контейнер зависимостей.
use Kaspi\DiContainer\DiContainerBuilder;
use Psr\Container\ContainerInterface;
function testFunc(ContainerInterface $c) {
return $c;
}
$container = (new DiContainerBuilder())->build();
var_dump($container->call('testFunc') instanceof DiContainer); // true
var_dump($container->call('testFunc') instanceof ContainerInterface); // trueuse Kaspi\DiContainer\DiContainerBuilder;
use Psr\Container\ContainerInterface;
class TestClass {
public function __construct(
public ContainerInterface $container
) {}
}
$container = (new DiContainerBuilder())->build();
var_dump($container->get(TestClass::class)->container instanceof ContainerInterface); // true- 👷♂️ Инструмент для сборки контейнера зависимостей DiContainerBuilder.
- 🐘 DiContainer с конфигурированием в стиле php определений.
- #️⃣ DiContainer c конфигурированием через PHP атрибуты.
- 📦 Метод контейнера
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.1-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