Наконец, я придумал свою собственную реализацию контейнера DI, представив новое настраиваемое свойство:
public $inheritableDefinitions = [];
Полный код класса:
<?php
namespace app\di;
/**
* @inheritdoc
*/
class Container extends \yii\di\Container
{
/**
* @var array inheritable object configurations indexed by class name
*/
public $inheritableDefinitions = [];
/**
* @inheritdoc
*
* @param string $class
* @param array $params
* @param array $config
*
* @return object the newly created instance of the specified class
*/
protected function build($class, $params, $config) {
$config = $this->mergeInheritedConfiguration($class, $config);
return parent::build($class, $params, $config);
}
/**
* Merges configuration arrays of parent classes into configuration of newly created instance of the specified class.
* Properties defined in child class (via configuration or property declarations) will not get overridden.
*
* @param string $class
* @param array $config
*
* @return array
*/
protected function mergeInheritedConfiguration($class, $config) {
if (empty($this->inheritableDefinitions)) {
return $config;
}
$inheritedConfig = [];
/** @var \ReflectionClass $reflection */
list($reflection) = $this->getDependencies($class);
foreach ($this->inheritableDefinitions as $parentClass => $parentConfig) {
if ($class === $parentClass) {
$inheritedConfig = array_merge($inheritedConfig, $parentConfig);
} else if (is_subclass_of($class, $parentClass)) {
/** @var \ReflectionClass $parentReflection */
list($parentReflection) = $this->getDependencies($parentClass);
// The "@" is necessary because of possible (and wanted) array to string conversions
$notInheritableProperties = @array_diff_assoc($reflection->getDefaultProperties(),
$parentReflection->getDefaultProperties());
// We don't want to override properties defined specifically in child class
$parentConfig = array_diff_key($parentConfig, $notInheritableProperties);
$inheritedConfig = array_merge($inheritedConfig, $parentConfig);
}
}
return array_merge($inheritedConfig, $config);
}
}
Вот как его можно использовать для достиженияНастройка LinkPager описана в вопросе:
'container' => [
'inheritableDefinitions' => [
'yii\widgets\LinkPager' => ['maxButtonCount' => 5],
],
],
Теперь, если я создам класс FancyLinkPager
, который расширяет yii\widgets\LinkPager
, контейнер DI объединит конфигурацию по умолчанию:
$pagination = \Yii::createObject(Pagination::class);
$linkPager = \Yii::createObject(['class' => LinkPager::class, 'pagination' => $pagination]);
$fancyLinkPager = \Yii::createObject(['class' => FancyLinkPager::class, 'pagination' => $pagination]);
$linkPager->maxButtonCount; // 5 as configured
$fancyLinkPager->maxButtonCount; // 5 as configured - hurrah!
Iтакже учли комментарий qiangxue о явной установке значений свойств по умолчанию в определении класса, поэтому, если мы объявим класс FancyLinkPager
так:
class FancyLinkPager extends LinkPager
{
public $maxButtonCount = 18;
}
, установка свойства будетсоблюдайте:
$linkPager->maxButtonCount; // 5 as configured
$fancyLinkPager->maxButtonCount; // 18 as declared
Чтобы поменять контейнер DI по умолчанию в вашем приложении, вы должны явно указать Yii::$container
где-нибудь в сценарии ввода:
Yii::$container = new \app\di\Container();