Компонент OptionsResolver
Дата оновлення перекладу 2024-05-03
Компонент OptionsResolver
Компонент OptionsResolver - це поліпшена заміна PHP-функції array_replace. Він дозволяє вам створювати систему опцій з обов'язковими опціями, значеннями за замовчуванням, валідацією (типу, значення), нормалізацією і більше.
Установка
1
$ composer require symfony/options-resolver
Note
Якщо ви встановлюєте цей компонент поза додатком Symfony, вам потрібно підключити
файл vendor/autoload.php
у вашому коді для включення механізму автозавантаження
класів, наданих Composer. Детальніше можна прочитати у цій статті.
Використання
Уявіть, що у вас є клас Mailer
, який має чотири опції: host
,
username
, password
и port
:
1 2 3 4 5 6 7 8 9
class Mailer
{
protected array $options;
public function __construct(array $options = [])
{
$this->options = $options;
}
}
При отриманні доступу до $options
, вам потрібно додати багато рутинного кода, щоб
перевірити, які опції встановлені:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Mailer
{
// ...
public function sendMail($from, $to): void
{
$mail = ...;
$mail->setHost($this->options['host'] ?? 'smtp.example.org');
$mail->setUsername($this->options['username'] ?? 'user');
$mail->setPassword($this->options['password'] ?? 'pa$$word');
$mail->setPort($this->options['port'] ?? 25);
// ...
}
}
Також, значення опцій за замовчуванням закопуються під бізнес-логіку вашого кода. Використайте array_replace, щоб виправити це:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
class Mailer
{
// ...
public function __construct(array $options = [])
{
$this->options = array_replace([
'host' => 'smtp.example.org',
'username' => 'user',
'password' => 'pa$$word',
'port' => 25,
], $options);
}
}
Тепер всі чотири опції точно будуть встановлені, але ви все ще можете
зробити помилу, на кшталт наступної, використовуючи клас Mailer
:
1 2 3
$mailer = new Mailer([
'usernme' => 'johndoe', // 'username' помилково написано як 'usernme'
]);
Помилка не буде відображена. В кращому випадку, баг проявиться під час тестування, але розробник витратить час на пошук проблеми. В гіршому випадку - баг не проявиться, поки не буде розгорнутий у живій системі.
На щастя, клас OptionsResolver допомагає вам виправити цю проблему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
use Symfony\Component\OptionsResolver\OptionsResolver;
class Mailer
{
// ...
public function __construct(array $options = [])
{
$resolver = new OptionsResolver();
$resolver->setDefaults([
'host' => 'smtp.example.org',
'username' => 'user',
'password' => 'pa$$word',
'port' => 25,
]);
$this->options = $resolver->resolve($options);
}
}
Як і раніше, всі опції будуть обовʼязково встановлені. Крім того, викликається UndefinedOptionsException, якщо передається невідома опція:
1 2 3 4 5 6
$mailer = new Mailer([
'usernme' => 'johndoe',
]);
// UndefinedOptionsException: Опція "usernme" не існує.
// Відомі опції: "host", "password", "port", "username"
Залишок вашого кода може отримати доступ до значень опцій без рутинного кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// ...
class Mailer
{
// ...
public function sendMail($from, $to): void
{
$mail = ...;
$mail->setHost($this->options['host']);
$mail->setUsername($this->options['username']);
$mail->setPassword($this->options['password']);
$mail->setPort($this->options['port']);
// ...
}
}
Гарною практикою є розділення конфігурації опції в окремі методи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// ...
class Mailer
{
// ...
public function __construct(array $options = [])
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$this->options = $resolver->resolve($options);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'host' => 'smtp.example.org',
'username' => 'user',
'password' => 'pa$$word',
'port' => 25,
'encryption' => null,
]);
}
}
По-перше ваш код стає простіше читати, особливо, якщо конструктор робить більше, ніж
просто обробляє опції. По-друге, підкласи тепер можуть перевизначати метод
configureOptions()
, щоб підлаштувати конфігурацію опцій:
1 2 3 4 5 6 7 8 9 10 11 12 13
// ...
class GoogleMailer extends Mailer
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'host' => 'smtp.google.com',
'encryption' => 'ssl',
]);
}
}
Обовʼязкові опції
Якщо опція повинна бути встановлена ініціатором виклику, передайте цю
опцію методу setRequired().
Наприклад, щоб зробити опцію host
обовʼязковою, ви можете:
1 2 3 4 5 6 7 8 9 10 11
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver)
{
// ...
$resolver->setRequired('host');
}
}
Якщо ви опустите обовʼязкову опцію, буде викликано MissingOptionsException:
1 2 3
$mailer = new Mailer();
// MissingOptionsException: Обовʼязкова опція "host" відсутня.
Метод setRequired() приймає одне імʼя або масив імен опцій, якщо у вас більше однієї обовʼязкової опції:
1 2 3 4 5 6 7 8 9 10 11
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setRequired(['host', 'username', 'password']);
}
}
Використайте isRequired(), щоб дізнатися, чи є опція обовʼязковою. Ви можете використати getRequiredOptions(), щоб отримати імена всіх обовʼязкових опцій:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// ...
class GoogleMailer extends Mailer
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
if ($resolver->isRequired('host')) {
// ...
}
$requiredOptions = $resolver->getRequiredOptions();
}
}
Якщо ви хочете перевірити, чи все ще відсутня обовʼязкова опція в опціяї за замовчуванням, ви можете використати isMissing(). Різниця між цим та isRequired() полягає в тому, що цей метод поверне "false", якщо обовʼязкова опція вже була встановлена:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setRequired('host');
}
}
// ...
class GoogleMailer extends Mailer
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->isRequired('host');
// => true
$resolver->isMissing('host');
// => true
$resolver->setDefault('host', 'smtp.google.com');
$resolver->isRequired('host');
// => true
$resolver->isMissing('host');
// => false
}
}
Метод getMissingOptions() дозволяє вам отримати доступ до імен всіх відсутніх опцій.
Валідація типу
Ви можете провести додаткові перевірки опцій, щоб переконатися, що вони були передані правильно. Щоб валідувати типи опцій, викличте setAllowedTypes():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
// вкажіть один дозволений тип
$resolver->setAllowedTypes('host', 'string');
// вкажіть декілька дозволених типів
$resolver->setAllowedTypes('port', array('null', 'int'));
// рекурсивно перевірте всі обʼєкти у масиві на тип
$resolver->setAllowedTypes('dates', 'DateTime[]');
$resolver->setAllowedTypes('ports', 'int[]');
}
}
Ви можете передати будь-який тип, для якого функція is_<type>()
визначена в PHP.
Ви можете також передати повне імʼя класу або інтерфейсу (яке перевіряється, використовуючи
instanceof
). Крім того, ви можете валідувати всі обʼєкти в масиві рекурсивно, додавши
до типу суфікс []
.
Якщо ви зараз передасте невалідну опцію, буде викликано InvalidOptionsException:
1 2 3 4 5 6
$mailer = new Mailer([
'host' => 25,
]);
// InvalidOptionsException: Опція "host" зі значенням "25" повинна
// мати тип "string"
У підкласах ви можете використати addAllowedTypes(), щоб додати додаткові дозволені типи не видаляючи ті, що вже встановлені.
Валідація значення
Деякі опції можуть викристовувати лише один зі списків визначених значень. Наприклад,
уявіть, що клас Mailer
має опцію transport
, яка може бути одним з sendmail
,
mail
та smtp
. Використайте метод
setAllowedValues(), щоб
переконатися, що передана опція містить одне з цих значень:
1 2 3 4 5 6 7 8 9 10 11 12
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefault('transport', 'sendmail');
$resolver->setAllowedValues('transport', ['sendmail', 'mail', 'smtp']);
}
}
Якщо ви передасте невалідний транспоорт, буде викликано InvalidOptionsException:
1 2 3 4 5 6
$mailer = new Mailer([
'transport' => 'send-mail',
]);
// InvalidOptionsException: Опція "transport" має значення
// "send-mail", але повинна бути одним з "sendmail", "mail", "smtp"
Для опцій зі складнішими схемами валідації, передайте завершувач, який повертає
true
для прийнятних значень, і false
- для невалідних:
1 2 3 4
// ...
$resolver->setAllowedValues('transport', function (string $value): bool {
// повернути true або false
});
Tip
Ви навіть можете використати компонент Валидатор, щоб валідувати введення, використовуючи метод createIsValidCallable():
1 2 3 4 5 6 7 8
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Validation;
// ...
$resolver->setAllowedValues('transport', Validation::createIsValidCallable(
new Length(['min' => 10 ])
));
У підкласах ви можете використати addAllowedValues(), щоб додати додаткові дозволені значення, не стираючи вже встановлені.
Нормалізація опцій
Інколи, значення опцій потрібно нормалізувати перед тим, як викорисовувати. Наприклад,
уявіть, що host
повинен завжди починатися з http://
. Щоб зробити це, ви можете
написати нормалізатори. Нормалізатори виконуються після валідації опції. Ви можете
сконфігурувати нормалізатор, викликавши
setNormalizer():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use Symfony\Component\OptionsResolver\Options;
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setNormalizer('host', function (Options $options, string $value): string {
if ('http://' !== substr($value, 0, 7)) {
$value = 'http://'.$value;
}
return $value;
});
}
}
Нормалізатор отримує справжнє $value
і поовертає нормалізовану форму. Ви бачите,
що завершувач також використовує параметр $options
. Це корисно, якщо вам потрібно
використати інші опції під час нормалізації:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setNormalizer('host', function (Options $options, string $value): string {
if ('http://' !== substr($value, 0, 7) && 'https://' !== substr($value, 0, 8)) {
if ('ssl' === $options['encryption']) {
$value = 'https://'.$value;
} else {
$value = 'http://'.$value;
}
}
return $value;
});
}
}
Щоб нормалізувати нове дозволене значення у субкласах, які нормалізуються у батьківських
класах, використайте addNormalizer().
Таким чином, аргумент $value
буде отримувати раніше нормалізоване значення, або ж ви
можете додати до початку нового нормалізатора, передавши true
в якості третього агументу.
Значення за замовчуванням, залежні від іншої опції
Уявіть, що ви хочете встановити значення за замовчуванням для опції port
,
засноване на шифруванні, вибраному користувачем класу Mailer
. Точніше, ви
хочете встановити порт 465
, якщо використовується SSL, і 25
- в інших випадках.
Ви можете реалізувати цю функцію, передавши завершувач в якості значення за
замовчуванням опції port
. Завершувач отримує опцію в якості аргументу.
Засновуючись на цих опціях, ви можете повернути бажане значення за замовчуванням:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use Symfony\Component\OptionsResolver\Options;
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefault('encryption', null);
$resolver->setDefault('port', function (Options $options): int {
if ('ssl' === $options['encryption']) {
return 465;
}
return 25;
});
}
}
Caution
Аргумент викликаного повинен бути типізований як Options
. Інакше
саме викликане розглядається як значення опції за замовчуванням.
Note
Завершувач виконується лише якщо опція port
не встановлена
користувачем або перезаписана у підкласі.
Доступ до раніше встановленого значення за замовчуванням пожна отримати, додавши до завершувача другий аргумент:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefaults([
'encryption' => null,
'host' => 'example.org',
]);
}
}
class GoogleMailer extends Mailer
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setDefault('host', function (Options $options, string $previousValue): string {
if ('ssl' === $options['encryption']) {
return 'secure.example.org';
}
// Взяти значення за замовчуванням, сконфігуроване у базовому класі
return $previousValue;
});
}
}
Як можна побачити у прикладі, ця функція найкорисніша, якщо ви хочете повторно використати значення за замовчуванням, встановлені у батьківських класах або підкласах.
Опції без значень за замовчуванням
У деяких випадках, корично визначати опцію, не встановлюючи значення за замовчуванням. Це корисно, якщо вам потрібно знати, чи дійсно користувач встановив опцію. Наприклад, якщо ви встановите значення за замовчуванням для опції, неможливо дізнатися, чи передав користувач значення, чи воно просто є значенням за замовчуванням:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefault('port', 25);
}
// ...
public function sendMail(string $from, string $to): void
{
// Це значення за замовчуванням або ініціатор класу дійсно встановив
// порт 25?
if (25 === $this->options['port']) {
// ...
}
}
}
Ви можете використати setDefined(), щоб визначити опцію, не встановлюючи значення за замовчуванням. Тоді опція буде додана у дозволені опції лише якщо вона дійсно була передана resolve():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefined('port');
}
// ...
public function sendMail(string $from, string $to): void
{
if (array_key_exists('port', $this->options)) {
echo 'Set!';
} else {
echo 'Not Set!';
}
}
}
$mailer = new Mailer();
$mailer->sendMail($from, $to);
// => Не встановлено!
$mailer = new Mailer([
'port' => 25,
]);
$mailer->sendMail($from, $to);
// => Встановлено!
Ви також можете передати масив імен опцій, якщо ви хочете визначати декілька опцій за один раз:
1 2 3 4 5 6 7 8 9 10
// ...
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->setDefined(['port', 'encryption']);
}
}
Методи isDefined() і getDefinedOptions() дозволяють вам дізнатися, які опції визначені:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
class GoogleMailer extends Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
if ($resolver->isDefined('host')) {
// Було викликано одне з наступних:
// $resolver->setDefault('host', ...);
// $resolver->setRequired('host');
// $resolver->setDefined('host');
}
$definedOptions = $resolver->getDefinedOptions();
}
}
Вкладені опції
Уявіть, що у вас є опція під назвою spool
, яка має дві підопції - type
і path
.
Замість того, щоб визначати її як простий масив значень, ви можете передати замикання в
якості значення за амовчуванням опції spool
з аргументом
OptionsResolver. Засновуючись на цьому екземплярі,
ви можете визначити опції під spool
та його бажане значення за замовчуванням:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('spool', function (OptionsResolver $spoolResolver): void {
$spoolResolver->setDefaults([
'type' => 'file',
'path' => '/path/to/spool',
]);
$spoolResolver->setAllowedValues('type', ['file', 'memory']);
$spoolResolver->setAllowedTypes('path', 'string');
});
}
public function sendMail(string $from, string $to): void
{
if ('memory' === $this->options['spool']['type']) {
// ...
}
}
}
$mailer = new Mailer([
'spool' => [
'type' => 'memory',
],
]);
Вкладені опції також підтримують обовʼязкові опції, валідацію (типа, значення) та
нормалізацію їх значень. Якщо значення за замовчуванням вкладеної опції залежить від
іншої опції, визначеної на батьківському рівні, додайте другий аргумент Options
до її замикання, щоб отримати до них доступ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('sandbox', false);
$resolver->setDefault('spool', function (OptionsResolver $spoolResolver, Options $parent): void {
$spoolResolver->setDefaults([
'type' => $parent['sandbox'] ? 'memory' : 'file',
// ...
]);
});
}
}
Caution
Аргументи замикання повинні мати підказки OptionsResolver
та Options
,
відповідно. Інашке, саме замикання буде вважатися значенням за замовчуванням.
Таким же чином, батьківські опції можуть отримати доступ до вкладених опцій, як до нормальних масивів:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class Mailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('spool', function (OptionsResolver $spoolResolver): void {
$spoolResolver->setDefaults([
'type' => 'file',
// ...
]);
});
$resolver->setDefault('profiling', function (Options $options): void {
return 'file' === $options['spool']['type'];
});
}
}
Note
Те, що опція визначена як вкладена, означає, що ви повинні передати масив значень, щоб розвʼязати їх під час прогону.
Прототипи опцій
Бувають ситуації, коли вам потрібно буде розвʼязаи та валідувати набір опцій,
які можуть повторюватися багато разів в іншій опції. Давайте уявимо опцію
connections
, яка прийматиме масив зʼєднань бази даних з host
, database
,user
і password
в кожній.
Найкращий спосіб реалізувати це - визначити опцію connections
в якості прототипу:
1 2 3 4 5 6
$resolver->setDefault('connections', function (OptionsResolver $connResolver): void {
$connResolver
->setPrototype(true)
->setRequired(['host', 'database'])
->setDefaults(['user' => 'root', 'password' => null]);
});
У відповідності з визначенням прототипу у прикладі вище, можливо мати багато масивів зʼєднання на кшталт наступного:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$resolver->resolve([
'connections' => [
'default' => [
'host' => '127.0.0.1',
'database' => 'symfony',
],
'test' => [
'host' => '127.0.0.1',
'database' => 'symfony_test',
'user' => 'test',
'password' => 'test',
],
// ...
],
]);
Ключі масиву (default
, test
, та ін.) цього прототипу опції не підлягають
валідації і можуть бути будь-яким довільним значенням, що допомагає диференціювати
зʼєднання.
Note
Прототип опції може бути визначений лише всередині вкладеної опції, та під час її розвʼязання від буде очікувати масив масивів.
Застарівання опції
Як тільки опція застаріла або ви вирішили її більше не підтримувати, ви можете відмітити її застарілою, використовуючи метод setDeprecated():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$resolver
->setDefined(['hostname', 'host'])
// це виведе наступне загальне повідомлення про застарівання:
// Починаючи з acme/пакета 1.2: Опція "hostname" застаріла.
->setDeprecated('hostname', 'acme/package', '1.2')
// Ви можете також передати ваше власне повідомлення про застарівання (доступний заповнювач %name%)
->setDeprecated(
'hostname',
'acme/package',
'1.2',
'Опція "hostname" застаріла, натомість використовуйте "host".'
)
;
Note
Повідомлення про застарівання буде запущене лише якщо опція десь використовується або її значення надане користувачем, або опція оцінюється в рамках замикань лінивих опцій та нормалізаторів.
Note
При використанні опції, відміченої застаролою вами у власній бібліотеці, ви
можете передати false
в якості другого аргументу методу
offsetGet(), щоб не
викликати попередження про застарівання.
Note
Всі повідомлення про застарівання відображаються у логах профілювальника у вкладці "Застарівання".
Замість передачі повідомлення ви можете також передати замикання, яке поверне рядок (повідомлення про застарівання) або пустий рядок для ігнорування застарівання. Це замикання зручне для застарівання лише деяких типів або значень опції:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$resolver
->setDefault('encryption', null)
->setDefault('port', null)
->setAllowedTypes('port', ['null', 'int'])
->setDeprecated('port', 'acme/package', '1.2', function (Options $options, ?int $value): string {
if (null === $value) {
повернення 'Passing "null" опції "port" застаріло, натомість передайте ціле число.';
}
// застарівання також може залежати від іншої опції
if ('ssl' === $options['encryption'] && 456 !== $value) {
повернення 'Passing a different port than "456" коли опція "encryption" встановлена як "ssl" застаріло.';
}
return '';
})
;
Note
Застарівання, засноване на значенні, запускається лише тоді, коли опція надається користувачем.
Це замикання отримує в якості другого аргумента значення опції після її валідації та перед її нормалізацією, коли опція розвʼязується.
Ігнорування невизначених опцій
За замовчуванням, всі опції розвʼязуються та валідуються, що призводить до UndefinedOptionsException, якщо передано невідому опцію. Ви можете ігнорувати невизначені опції за допомогою методу ignoreUndefined():
1 2 3 4 5 6 7 8 9 10 11
// ...
$resolver
->setDefined(['hostname'])
->setIgnoreUndefined(true)
;
// опція "version" буде проігнорована
$resolver->resolve([
'hostname' => 'acme/package',
'version' => '1.2.3'
]);
Створення ланцюжку конфігурацій опції
У багатьох випадках вам може знадобитися визначити багато конфігурацій для кожної
опції. Наприклад, уявіть, що клас InvoiceMailer
має обовʼязкову опцію host
та опцію transport
, яка може бути sendmail
, mail
або smtp
. Ви можете
покращити читаність кода, уникнувши дублювання імені опції для кожної конфігурації,
використовуючи метод define():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// ...
class InvoiceMailer
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
// ...
$resolver->define('host')
->required()
->default('smtp.example.org')
->allowedTypes('string')
->info('The IP address or hostname');
$resolver->define('transport')
->required()
->default('transport')
->allowedValues('sendmail', 'mail', 'smtp');
}
}
Налаштування продуктивності
З поточною реалізацією, метод configureOptions()
викликатиметься для кожного екземпляра
класу Mailer
. В залежності від обсягу конфігурації опції та кількості створених екземплярів,
це може створювати додаткове навантаження на ваш додаток. Якщо це навантаження стане проблемою,
ви можете змінити ваш код, щоб конфігурація робилась лише один раз для одного класу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// ...
class Mailer
{
private static array $resolversByClass = [];
protected array $options;
public function __construct(array $options = [])
{
// Який це тип Mailer: Mailer, GoogleMailer, ... ?
$class = get_class($this);
// Чи була виконана configureOptions() до цього класу?
if (!isset(self::$resolversByClass[$class])) {
self::$resolversByClass[$class] = new OptionsResolver();
$this->configureOptions(self::$resolversByClass[$class]);
}
$this->options = self::$resolversByClass[$class]->resolve($options);
}
public function configureOptions(OptionsResolver $resolver): void
{
// ...
}
}
Тепер екземпляр OptionsResolver
буде створений один раз для одного класу і далі використаний повторно. Майте на
увазі, що це може призвести до пробілів в памʼяті у довгострокових додатках, якщо
опції за замовчуванням містять посилання на обʼєкти або графіки обʼєктів. Якщо це
такий випадок, реалізуйте метод clearOptionsConfig()
та періодично викликайте його:
1 2 3 4 5 6 7 8 9 10 11 12
// ...
class Mailer
{
private static $resolversByClass = [];
public static function clearOptionsConfig()
{
self::$resolversByClass = [];
}
// ...
}
Ось і все! Тепер у вас є всі інструменти та знання, необхідні для легкої обробки опції у вашому коді.
Отримайте більше інсайтів
Використайте OptionsResolverIntrospector
, щоб дослідити визначення опцій всередині
екземпляру OptionsResolver
:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
use Symfony\Component\OptionsResolver\OptionsResolver;
$resolver = new OptionsResolver();
$resolver->setDefaults([
'host' => 'smtp.example.org',
'port' => 25,
]);
$introspector = new OptionsResolverIntrospector($resolver);
$introspector->getDefault('host'); // Retrieves "smtp.example.org"