Рейтинг:1

Возврат закрытия с завода

флаг cn

У меня есть службы, которые зависят от запрашиваемой информации. я пытаюсь создать Фабрика Фабрика который будет иметь контейнер внедрения зависимостей, возвращающий фабрику (анонимную функцию), которая создаст нужные мне службы. Итак, в основном что-то вроде этого:

класс FooFactoryFactory {
  общедоступная статическая функция create(ContainerInterface $container): callable {
     вернуть статическую функцию (запрос $request) использовать ($container) {
       $raz = запрос-> получить ('param_raz');
       вернуть новый бар(
           $container->get(LoggerInterface::class),
           $ раз,
       );
     };
  }

}

Проблема у меня в том, что Закрытие правильно создан, но класс контейнера внедрения зависимостей de Drupal пытается установить свойство для Закрытие. Это не может работать и заканчивается фатальной ошибкой:

Объект закрытия не может иметь свойств

Вот место, где это происходит:

// Drupal\Component\DependencyInjection\Container::createService() - Строка 283-293
если (isset($определение['свойства'])) {
  если ($ определение ['свойства'] instanceof \ stdClass) {
    $definition['properties'] = $this->resolveServicesAndParameters($definition['properties']);
  }
  foreach ($определение['свойства'] as $key => $value) {
     $service->{$key} = $значение; // <-- Не будет работать для закрытия
  }
}
...

Итак вопросы у меня:

  • Это по дизайну? Это означает, что это что-то нежелательное с точки зрения практики кодирования?
  • Ошибки можно избежать, если мне удастся убедиться, что сервис, который я хочу создать, не имеет характеристики это определение. Я просто не знаю, как этого добиться.

Спасибо за помощь в этом вопросе.

РЕДАКТИРОВАТЬ: Это в основном то, чего я пытаюсь достичь:


  закрытие.завода.обслуживание:
    класс: Drupal\some_module\ClosureFactoryService
  закрытие.сервис:
    класс: Закрытие
    factory: [ '@close.factory.service', getCallable ]
класс ClosureFactoryService {
  публичная функция getCallable(): Закрытие {
    вернуть статическую функцию (int $arg): строка {
      вернуть sprintf('val %d', $arg);
    };
  }
}

Я получил: Объект закрытия не может иметь свойства в Drupal\Component\DependencyInjection\Container->createService() (строка 288...

4uk4 avatar
флаг cn
Если ваша служба зависит от информации о запросе, введите `@request_stack`. Чтобы понять ваш дизайн, можете ли вы предоставить весь код, в частности, класс Bar и определение службы, вызывающее ошибку. Почему вы используете именно этот фабричный метод? Можете ли вы предоставить больше контекста?
флаг cn
Я только что отредактировал вопрос, чтобы иметь исходный код, как в `Drupal\Component\DependencyInjection\Container.php`. @ 4k4: в примере это почти так. Службы, которые мне нужны, чтобы иметь зависимости, извлеченные из контейнера, и зависят от контейнера. Это что-то, что не может быть реализовано таким образом?
apaderno avatar
флаг us
В Drupal службы определяются в файле .services.yml, что @4k4 попросил показать. Кроме того, служба, определенная в этом файле, не реализует никакого метода `create()`. Внедрение зависимостей, используемое Drupal, которое является внедрением зависимостей Symfony, ожидает, что `create()` вернет экземпляр этого класса, а не замыкание.
флаг cn
Привет @apaderno, спасибо за ответ. Я не упомянул объявление сервисов в файлах `*.yml`, потому что это не то место, где у меня возникла проблема. Передо мной стоит задача вернуть анонимную (заводскую) функцию из следующего класса Drupal `Drupal\Component\DependencyInjection\Container.php`.Я разместил фрагмент кода в этом классе, из-за которого он терпит неудачу. «Закрытие» правильно создано в упомянутом классе Drupal, но следующие строки заставляют «Container.php» выдавать ошибку. Я проверю контейнерную реализацию Symfony.
apaderno avatar
флаг us
Эта информация необходима; по крайней мере, необходимо понять, предназначен ли этот класс для службы, определенной в файле .services.yml модуля. Таким образом, ответ может быть более полезным.
Рейтинг:4
флаг us

В Drupal класс службы, определенный в файле модуля .services.yml, не нужно реализовывать. создать (контейнерный интерфейс $ контейнер). Даже не требуется реализовывать конкретный интерфейс PHP.

См. один из сервисов, реализованных ядром Drupal, например path_alias.manager оказание услуг.

path_alias.manager:
  класс: Drupal\path_alias\AliasManager
  аргументы:
   - '@path_alias.repository'
   - '@path_alias.whitelist'
   - '@language_manager'
   - '@cache.data'

Псевдоним менеджер класс, который реализует эту службу, не реализует никаких Создайте() метод; это просто реализовать конструктор, с параметрами, определенными в том же порядке, что и аргументы службы.

публичная функция __construct($alias_repository, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {
  $this->pathAliasRepository = $alias_repository;
  $this->languageManager = $language_manager;
  $this->белый список = $белый список;
  $this->кэш = $кэш;
}

Классы, реализующие создать (контейнерный интерфейс $ контейнер) и которые реализуют ContainerInjectionInterface, например CronForm класс, не возвращайте замыкание из создать (контейнерный интерфейс $ контейнер); на самом деле они возвращают только экземпляр самого себя. Видеть CronForm::создать().

общедоступная статическая функция create(ContainerInterface $container) {
  вернуть новый статический ($ контейнер-> получить ('config.factory'),
    $контейнер->получить('состояние'),
    $контейнер->получить('cron'),
    $container->получить('date.formatter'),
    $container->получить('module_handler')
  );
}

Если вы хотите реализовать фабричный сервис в Drupal, вам следует взять cache_factory сервис в качестве примера для написания кода.

cache_factory:
  класс: Drupal\Core\Cache\CacheFactory
  аргументы:
    - '@настройки'
    - '%cache_default_bin_backends%'
  звонки:
    - [setContainer, ['@service_container']]

Служба, использующая эту службу в качестве фабрики, — это, например, кеш.рендер оказание услуг.

кеш.рендер:
  класс: Drupal\Core\Cache\CacheBackendInterface
  теги:
    - {имя: cache.bin}
  фабрика:
    - '@cache_factory'
    - получать
  аргументы:
    - оказывать

фабрика ключ определяет, какая служба является фабричной службой и какой метод вызывается для этой фабричной службы; в аргументы ключ определяет аргументы, передаваемые этому методу. В этом случае он говорит Drupal создать экземпляр кеш.рендер службы путем создания экземпляра cache_factory обслуживание и звонки получить('рендеринг') на этом объекте.

флаг cn
Спасибо за подробный ответ. Я отредактировал свой вопрос, чтобы представить, чего я пытаюсь достичь. Я думаю, что это невозможно с текущим контейнером Drupal. Или я что-то массово делаю не так.
Jaypan avatar
флаг de
Вы не предоставили вариант использования того, что вы пытаетесь сделать (или проблему, которую вы пытаетесь исправить / избежать), а только проблемы, с которыми вы столкнулись при попытке ее реализовать. Я подозреваю, что вы берете свой предыдущий опыт PHP и пытаетесь применить его, чтобы сделать что-то, что по-другому обрабатывается с использованием инфраструктуры Drupal. Возможно, вы могли бы больше объяснить свой вариант использования, а не свою реализацию. В противном случае трудно сказать, делаете ли вы это "неправильно" (иначе - не в Drupally) или нет.
apaderno avatar
флаг us
@Jaypan Я так понимаю, dickwan просто привык к тому, как DI работает с разными библиотеками/фреймворками.Например, в PHP-DI совершенно нормально иметь фабрику сервисов, реализованную с помощью объекта `Closure` (или любого вызываемого объекта), но то же самое недопустимо в Drupal.
apaderno avatar
флаг us
@dickwan Как вы узнали, Drupal ожидает, что он может добавлять свойства к объекту, возвращаемому для службы. Поскольку объекты «Замыкание» не могут иметь свойств, их нельзя использовать. То, что я описал в ответе, — это способ Drupal для реализации сервиса и фабрики сервисов. Имейте в виду, что экземпляры класса, реализующего [`__invoke()`](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke), являются вызываемыми объектами для PHP. , и они могут иметь свойства, чего и ожидает Drupal. Если `$callable` является одним из этих объектов, то `$result = $callable();` будет вполне корректным кодом.
флаг cn
@apaderno: Спасибо. Я действительно был немного удивлен, что подход, который я хотел использовать, не работает. Я думаю, что искал причину этого специфического поведения фреймворка. Итак, я полагаю, что Framework ведет себя так, как ожидалось, и, вероятно, невозможно создать службу без свойств.
apaderno avatar
флаг us
@dickwan Drupal использует компоненты Symfony, но не во всех случаях ведет себя как Symfony.Знание того, что Drupal использует Symfony, может помочь в понимании того, как работает Drupal, но все же необходимо взглянуть на код Drupal, чтобы полностью понять Drupal.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.