Closed sergey-s-betke closed 10 years ago
Напрашивается описание опций так же через компоненты плагина. Возможно - через некий агрегатор компонентов - опций.
Доступ к опциям - через методы \WPF\Plugin\Base
. При этом доступны должны быть только те опции, которые определены в компонентах, описывающих опции.
То есть плагин должен будет содержать коллекцию опций, которую он будет собирать путём анализа своих компонентов на предмет реализации интерфейса опции.
Для всех компонентов тянуть в базовые интерфейсы и классы методы удаления, деактивации и активации смысла нет. Вполне (благо при этих операциях производительность не столь важна) можно определять необходимые компоненты путём проверки, реализуют ли они необходимый интерфейс (что-нибудь типа IActivatable
, IInstallable
, IUpdatable
и так далее).
К каждой опции можно привязать свой класс, тогда при запросе опции можно его десериализовать из значения опции и вернуть уже объект. В некоторых случаях такой подход может показаться удобным.
Но нужно предложить и базовые классы, которые в результате отдают просто целое, вещественное числа, строку, дату, и так далее.
С удалением всё более менее понятно. Мы можем зарегистрировать register_uninstall_hook
. Правда - необходимо использовать статические методы, динамические методы использовать нельзя.
Но регистрацию опций придётся выполнять при первой активации. Наш Фреймворк должен анализировать необходимость выполнения "инсталляции" или обновления.
Обязательное правило - новые версии плагина должны уметь обновлять / удалять версии всех предыдущих версий своих.
Пока вижу только следующие решения:
load_data
)plugins_loaded
и только при is_admin()
сравнивать действительную версию плагина (опять - из load_data()
) с данными в свойстве. И по результатам анализа инициировать процедуру обновления. На этом этапе в достаточно вызывать некую процедуру обновления, в которую передавать версию, с которой осуществляем обновление.
По результатам обновления желательно генерировать admin notice со ссылкой на страницу настроек плагина.
Итак, для каждого объекта опций мы должны определить функцию обновления! В базовом классе будем проверять наличие опции, добавлять её при отсутствии, проводить санитизацию значения и сохранять значение после санитизации.
Если требуется более сложная логика обновления опции - необходимо переопределять класс.
Не хотелось бы тянуть в плагин код с логикой обновлений, если и обновлять то будет нечего - то есть если нет опций, базы данных и прочего.
Итого, опции предлагаю оформлять не как компоненты. Как компоненту предлагаю оформить коллекцию опций. И в этой компоненте уже и реализовать логику обновлений.
Но саму компоненту (видимо - отдельную), отвечающую за механизм обновлений, необходимо регистрировать неявно.
Сначала необходимо решить более простую задачу: версионность и механизм обновлений необходимо либо изначально в код плагина включать (в код базового класса), либо же опционально - но автоматически при наличии компонентов, требующих этого механизма.
Как вариант решения:
id
bind
включать в плагин дополнительные компоненты, от которых они сами зависят. И проверять наличие необходимых компонентов по id
. В таком варианте код поддержки описанного механизма мы бы грузили только при подключении компонентов, его требующих.Такое решение пока кажется в достаточно степени работоспособным, хотя и в достаточной степени некрасивым. Но реализовывать, видимо, буду всё-таки его.
Прямую манипуляцию компонентами допускать не стоит, но через методы, с инкапсуляцией, в достаточной степени безопасно...
С одной стороны повесить на компоненты задачи по контролю зависимостей заманчиво. Но в таком случае мы ограничим действительный класс компонента зависимости тем, что знает зависимая компонента. А она должна знать только интерфейс, по сути, не более.
В итоге, предложение следующее:
WP_DEBUG
, и только в случае его установки выполнять дальнейшие действияbind
проверять наличие среди компонентов плагина компонентов с необходимым нам интерфейсом. И да - эту задачу мы повесим на компоненты.Теперь для инсталлятора необходим алгоритм определения вызова обработчиков install
, uninstall
. Да и update
добавить надо.
Проще всего с uninstall
. Есть такой hook. Но для него нужен статический метод. Необходимо выяснить очередность вызова uninstall hook, необходимо, чтобы загрузчик admin side уже отработал, потому как наш uninstall hook только там и определяется. И потребуется задействовать singleton для этих целей.
Сложнее - с install
, ещё сложнее - с update
.
install
будем вызывать следующим образом: при activate
проверяем наличие некоего свойства. Имя свойства должно определяться с использованием $plugin->get_namespace()
. Если свойства нет - значит ещё только инсталлируем наш плагин. Вызываем install
, и только после этого - activate
.
В это опцию писать предлагаю версию плагина. И по admin_init
читаем это свойство, сравниваем версии плагина, и если актуальная версия плагина изменилась в большую сторону - вызываем update
, после чего обновляем опцию на актуальную версию.
Операция register_uninstall_hook
крайне дорогая, как выясняется. И ясно - почему требует статического метода - она сохраняет информацию о callback в базу данных!
Посему использовать эту функции нужно только однажды - при установке (install
), но никак не при каждой загрузке плагина или при его активации!
Есть радостные новости! Изучение кода wordpress показало, что при деинсталляции вызывается действие do_action( 'uninstall_' . $plugin_file )
.
Но вызывается это действие только в том случае, если зарегистрирован деинсталлятор через register_uninstall_hook
.
Посему решение следующее:
uninstall_<plugin_file>
.uninstall_<plugin_file>
, уже не статическийОписанную выше логику с установкой, удалением и обновлением реализовал и протестировал на примере опций - всё работает, включая удаление опций из базы при удалении плагина.
Корректное условие на возможности пользователя по установке и удалению плагинов:
if ( ( ! is_multisite() && is_blog_admin() && current_user_can( 'install_plugins' ) ) || ( is_network_admin() && current_user_can( 'manage_network_plugins' ) && current_user_can( 'install_plugins' ) ) )
Целесообразно добавить по аналогии с uninstall_<plugin_basename>
action также и install_<plugin_basename>
, update_<plugin_basename>
. Сделаем это.
Реализовал и протестировал.
Опции должны быть описаны отдельно от их валидаторов и интерфейса. Просто потому, что валидаторы и интерфейс нужны только в admin console. А сами опции нужны плагину всегда.
Не стоит мудрить с сериализацией опций в одно значение в
wp_options
, пусть каждая опция будет отдельным значением, это нормально.Кроме того, необходимо по умолчанию свойства устанавливать не
autoload
.Итак, описывать опции логично в главном файле модуля.