- 13.05.15
- 23:30
- 1159
- 0
Одна из тем имеет сайдбары, оформление любого виджета или типа виджетов в которых задаётся на соотв. экране настройки. Интерфейс экрана должен содержать выпадающие списки виджетов и их типов.
Задача: составить списки виджетов и типов виджетов. При этом
- список всех зарегистрированных виджетов требуется в виде хеша
$id_base => $name
; - список всех размещённых на сайдбарах виджетов должен быть сгруппирован по сайдбарам:
123456<optgroup label="Правый сайдбар"><option value="calendar-2">Календарь (Календарь)</option><option value="text-2">Текст (Lorem ipsum dolor)</option><option value="tag_cloud-2">Облако меток</option><option value="categories-3">Рубрики</option></optgroup>"{$name} ({$title})"
, пустой заголовок не приводится; значения пунктов — идентификаторы виджетов.
Первая задача решается обработкой массива $wp_widget_factory->widgets
, содержащего экземпляры всех зарегистрированных классов виджетов:
1 2 |
global $wp_widget_factory; $list = wp_list_pluck($wp_widget_factory->widgets, 'name', 'id_base'); |
Обработка массива возложена на функцию wp_list_pluck($arr, $field_val, $field_key)
: здесь она выбирает из элементов массива — объектов — значения полей 'id_base'
и 'name'
, а затем помещает их в выходной массив как пару "ключ => значение"
. Массив с объектами следует первым аргументом, имя поля значений — вторым, имя поля ключей — третьим.
Причина такого вида списка в том, что при обработке вывода виджета имя его класса обработчикам хуков не передаётся, но передаётся либо идентификатор, из которого можно извлечь $id_base
, либо само $id_base
.
Вторая задача требует определить все размещённые в сайдбарах виджеты и для каждого получить настройки — в них хранится опциональный заголовок. Задача сводится к формированию вложенного хеша вида $sidebar_id => array($widget_id => $text)
.
Информация о виджетах находится в глобальном массиве $wp_registered_widgets
, ключами которого являются идентификаторы виджетов — имеют вид "{$id_base}-{$number}"
. Массив также содержит «потерянные», неактивные и «несуществующие» виджеты.
Последние отражают существование классов, не имеющих ни одного виджета. Отличаются номером -1 (минус один) и идентификатором "{$id_base}-1"
(номера реальных виджетов начинаются с 2). Регистрируются с пустыми настройками. Нужны для экрана «Внешний вид > Виджеты».
Номер виджета лежит в $wp_registered_widgets[$widget_id]['params'][0]['number']
, также может быть выделен из идентификатора выражением '/^.*-(\d+)$/'
.
Для сортировки по сайдбарам необходимы список зарегистрированных сайдбаров и списки соответствующих им виджетов. Информация о сайдбарах хранится в глобальном массиве $wp_registered_sidebars
вида $sidebar_id => array( /* параметры сайдбара*/ )
.
Хеш соответствия виджетов сайдбарам даётся вызовом wp_get_sidebars_widgets()
: в ключах находятся идентификаторы сайдбаров, в значениях — массивы идентификаторов виджетов. В результат также входят «потерянные» и неактивные виджеты:
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 |
Array ( [orphaned_widgets_1] => Array ( [0] => search-2 [1] => recent-posts-2 [2] => recent-comments-2 [3] => archives-2 [4] => categories-2 [5] => meta-2 ) [wp_inactive_widgets] => Array ( [0] => recent-posts-3 [1] => sample_widget-2 [2] => sample_widget-3 ) [right-sidebar] => Array ( [0] => calendar-2 [1] => text-2 [2] => recent-posts-2 [3] => tag_cloud-2 [4] => categories-3 ) ) |
Группы wp_inactive_widgets
и orphaned_widgets_*
сайдбарами не являются и в $wp_registered_sidebars
не содержатся, что удобно для фильтрации.
Формирование хеша $sidebar_id => array($widget_id => '')
— без текста для пунктов списка — выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
global $wp_widget_factory, $wp_registered_widgets, $wp_registered_sidebars; $list = array(); // целевой список foreach (wp_get_sidebars_widgets() as $sb_id => $a_wids) { if (!isset($wp_registered_sidebars[$sb_id])) continue; // группа виджетов не является сайдбаром if (empty($a_wids)) continue; // сайдбар не содержит виджетов $arr = array(); foreach ($a_wids as $w_id) { $text = ''; // (*) здесь надо сформировать строку $text = "{$name} ({$title})"; $arr[$w_id] = $text; } $list[$sb_id] = $arr; } |
Для формирования строки $text
нужны имя виджета и заголовок. Человекочитаемое имя находится в поле $name
класса виджета, заголовок — в настройках. Массив с настройками всех виджетов одного типа даётся методом get_settings()
их класса:
1 |
$all_settings = $wp_widget_factory->widgets['ClassName']->get_settings(); |
Результат имеет вид $number => $instance
, где $number
— номер виджета, $instance
— его настройки. Заголовок берётся из $instance['title']
.
Имя класса 'ClassName'
не известно. Для удобного его определения можно заранее построить хеш вида $id_base => 'ClassName'
:
1 2 3 4 |
$a_wids_names = array(); foreach ($wp_widget_factory->widgets as $k => $v) { $a_wids_names[$v->id_base] = $k; } |
Примечание: сам WordPress использует массив $wp_widget_factory->widgets
только при инициализации виджетов. При этом все необходимые им функции раскладываются по глобальным массивам, поэтому имена классов ему не требуются и средств для их определения по, например, идентификатору виджета не имеется.
При построении выпадающего списка значение $id_base
виджета определяется из его идентификатора:
1 |
$id_base = preg_replace('/-[0-9]+$/', '', $widget_id); |
Примечание: Это же самое действие выполняет функция _get_widget_id_base($widget_id)
, но она, как видно из первого знака подчёркивания, является «приватной» и к использованию не рекомендуется. Поэтому в неё следует разве что заглядывать от версии к версии на предмет к.-л. изменений в устройстве идентификатора виджета.
Формирование строк "{$name} ({$title})"
для элементов списка выглядит так:
1 2 3 4 5 6 7 8 9 10 11 |
$wid = $wp_registered_widgets[$w_id]; // данные виджета - хеш-массив $num = $wid['params'][0]['number']; // номер - для получения настроек $id_base = preg_replace('/-\d+$/', '', $w_id); $wclass = $a_wids_names[$id_base]; $wtype = $wp_widget_factory->widgets[$wclass]; // экземпляр класса виджета $settings = $wtype->get_settings(); // настройки всех виджетов данного типа $settings = $settings[$num]; // настройки данного виджета $text = $wid['name']; if (!empty($settings['title'])) $text .= ' (' . $settings['title'] . ')'; |
Выдать построенный массив в виде выпадающего списка <select>
можно, напр., так:
1 2 3 4 5 6 7 8 9 |
echo "<select>\n"; foreach ($list as $sb_id => $a_wids) { $sb_name = $wp_registered_sidebars[$sb_id]['name']; echo " <optgroup label=\"{$sb_name}\">\n"; foreach ($a_wids as $w_id => $name) echo " <option value=\"{$w_id}\">{$name}</option>\n"; echo " </optgroup>\n"; } echo "</select>\n"; |
Результат выглядит так (функция и выдача списка находятся в разных файлах, всё лишнее вырезано):
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
/* основной файл плагина */ add_action('plugins_loaded', array('MyPlugin', 'plugin')); class MyPlugin { public static $instance = null; public static function plugin () { if (self::$instance === null) self::$instance = new self(); return self::$instance; } public function __construct () { // ... } public function get_widgets_list () { global $wp_widget_factory, $wp_registered_widgets, $wp_registered_sidebars; $list = array(); // целевой список $sidebars_widgets = wp_get_sidebars_widgets(); $a_wids_names = array(); foreach ($wp_widget_factory->widgets as $k => $v) $a_wids_names[$v->id_base] = $k; foreach ($sidebars_widgets as $sb_id => $a_wids) { if (!isset($wp_registered_sidebars[$sb_id])) continue; if (empty($a_wids)) continue; $arr = array(); foreach ($a_wids as $w_id) { $wid = $wp_registered_widgets[$w_id]; $num = $wid['params'][0]['number']; $id_base = preg_replace('/-\d+$/', '', $w_id); $wclass = $a_wids_names[$id_base]; $wtype = $wp_widget_factory->widgets[$wclass]; $settings = $wtype->get_settings(); $settings = $settings[$num]; $s = $wid['name']; if (!empty($settings['title'])) $s .= ' (' . $settings['title'] . ')'; $arr[$w_id] = $s; } $list[$sb_id] = $arr; } return $list; } } /* файл с шаблоном экрана редактирования */ global $wp_registered_sidebars; echo "<select>\n"; foreach (MyPlugin::plugin()->get_widgets_list() as $sb_id => $a_wids) { $sb_name = $wp_registered_sidebars[$sb_id]['name']; echo " <optgroup label=\"{$sb_name}\">\n"; foreach ($a_wids as $w_id => $name) echo " <option value=\"{$w_id}\">{$name}</option>\n"; echo " </optgroup>\n"; } echo "</select>\n"; |
Так выглядят пара вариантов выдаваемого списка:
- Ресурс с четыремя однотипными сайдбарами в футере:
123456789101112131415<select><option selected="1" disabled="1" value="-1">-- виджет --</option><optgroup label="Область счётчиков в футере"><option value="gqct_widget-3">Счётчики</option></optgroup><optgroup label="Широкая область в футере"><option value="gqmenuwidget-5">Меню со столбцами (Направления)</option></optgroup><optgroup label="Средняя область в футере"><option value="recent-posts-2">Свежие записи</option></optgroup><optgroup label="Узкая область в футере"><option value="calendar-2">Календарь (Календарь)</option></optgroup></select> - Ресурс с правым сайдбаром и сайдбарами в футере:
12345678910111213141516<select><option selected="1" disabled="1" value="-1">-- виджет --</option><optgroup label="Правый сайдбар"><option value="calendar-2">Календарь</option><option value="text-2">Текст (Lorem ipsum dolor)</option><option value="gqns-recent-posts-2">Недавние посты GQ</option><option value="tag_cloud-2">Облако меток</option><option value="categories-3">Рубрики</option></optgroup><optgroup label="Футер, область счётчиков"><option value="gqct_widget-2">Счётчики</option></optgroup><optgroup label="Футер #1"><option value="nav_menu-2">Произвольное меню (Разделы)</option></optgroup></select>
P. S. Класс виджета из его идентификатора можно получить и другим способом: функционал $wp_registered_widgets[$id]['callback']
для всех «нормальных» виджетов конструируется как array($this, 'display_callback')
в методе _get_display_callback()
класса WP_Widget
. Поэтому для них такие варианты будут идентичны:
1 2 3 4 5 6 |
// вариант 1: копипаст сверху - для сравнения $id_base = preg_replace('/-\d+$/', '', $widget_id); $wclass = $a_wids_names[$id_base]; $wtype = $wp_widget_factory->widgets[$wclass]; // вариант 2: экземпляр класса берётся из функционала вида array($obj, 'method') $wtype = $wp_registered_widgets[$widget_id]['callback'][0]; |
Две проблемы связаны со вторым вариантом:
- технически возможны «ненормальные» виджеты, реализованные ручным использованием функции
wp_register_sidebar_widget($id, $name, $output_callback, $options)
, которая от существования класса никак не зависит. Такие виджеты приведут второй вариант в нерабочее состояние; - от значения
$wp_registered_widgets[$id]['callback']
требуется только способность его к выполнению — вызовis_callable(...)
должен возвращать истину. Поэтому ничего не мешает разработчикам изменить вид функционала на строку с именем функции, анонимную функцию, метод к.-л. другого класса, вызывающий метод класса виджета и т. д.
По этим соображениям первый вариант — более громоздкий и с дополнительным массивом — кажется более надёжным.