Исследование механизма плагинов ctools 7.x-1.0. Типы плагинов

Главные вкладки

Аватар пользователя Hinikato Hinikato 28 июня 2012 в 7:34

В исследовании я буду использовать пакет \Myak\Debug и функцию d(), код которой я привожу в конце статьи.

Коротко о плагинах

В этой статье мы будем рассматривать механизм типов плагинов модуля ctools. Но, чтобы понять что такое тип плагина, нужно вкратце рассмотреть что такое плагины.

Плагины используются когда нужно дать возможность расширять какую-то функциональность позже. В ctools плагины вызываются с помощью функции ctools_get_plugins(). Эта функция возвращает массивы-описывающие плагины. Используя эти мета-массивы, разработчик может вызывать плагины. Каждый плагин должен принадлежать определенному модулю и определенному типу. Функция ctools_get_plugin() первым параметром принимает имя модуля, а вторым тип плагина. Пример использования этой функции:
<?php
function page_manager_get_tasks() {
ctools_include('plugins');
return ctools_get_plugins('page_manager', 'tasks');
}
// ...
/**
* Delegated implementation of hook_menu().
*/
function page_manager_menu() {
// ...
$tasks = page_manager_get_tasks();

// Provide menu items for each task.
foreach ($tasks as $task_id => $task) {
// ...
// Allow the task to add its own menu items.
if ($function = ctools_plugin_get_function($task, 'hook menu')) {
$function($items, $task);
}
// ...
}

return $items;
}
?>
Это пример того, как можно использовать плагины в hook_menu(): мы получаем список всех плагинов типа 'tasks' (2-ой параметр функции ctools_get_plugins()). Затем в цикле foreach получаем функцию плагина (ctools_plugin_get_function()) и вызываем её, передав массив $items, который плагины смогут изменить по своему усмотрению.

Каждый плагин, как я уже сказал выше, должен принадлежать какому-либо типу. Поэтому при создании плагина, нужно либо найти подходящий тип среди уже существующих, либо создать новый тип плагина. Идеологически тип плагина можно сравнить с классом, а сам плагин с его экземпляром - объектом, однако ctools использует не классы, а массивы для описания типов и плагинов.

Как работает механизм типа плагинов?

В файле ctools/includes/plugins.inc объявлена функция ctools_plugin_get_plugin_type_info(). Эта функция вызывает hook_ctools_plugin_type(). Любой модуль может реализовать функцию hook_ctools_plugin_type(), которая должна возвращать массив, описывающий тип плагина.

Вот как можно вызвать ctools_plugin_get_plugin_type_info():
<?php
ctools_include('plugins');
d()->varExport(ctools_plugin_get_plugin_type_info());
?>
В результате выводится такой массив. Ключи - это названия модулей, где объявлен тип плагина, а элементы - массивы, описывающие сам тип. Функция вызывает hook_ctools_plugin_type() кеширует результат, так что последующее обращение будет использовать кеш, если не использован параметр функции $flush.

Я не нашел функции, возвращающие некоторые отдельные части массива, поэтому рассмотрим как это можно сделать:
1. Узнать какие модули описывают типы плагинов:

ctools_include('plugins');
d(array_keys(ctools_plugin_get_plugin_type_info()));

Результат:

array(2) {
  [0] => string(6) "ctools"
  [1] => string(12) "page_manager"
}

2. Теперь, когда мы знаем названия модулей, можно узнать какие типы плагинов реализует модуль:
<?php
ctools_include('plugins');
$info = ctools_plugin_get_plugin_type_info();
d(array_keys($info['page_manager']));
?>
Результат:

array(3) {
  [0] => string(5) "tasks"
  [1] => string(13) "task_handlers"
  [2] => string(12) "page_wizards"
}

3. Зная имя модуля и имя типа плагина можно получить информацию по конкретном типу:
<?php
ctools_include('plugins');
$info = ctools_plugin_get_plugin_type_info();
d($info['page_manager']['tasks']);
?>
Результат:

array(13) {
  ["module"] => string(12) "page_manager"
  ["type"] => string(5) "tasks"
  ["cache"] => bool(false)
  ["cache table"] => string(5) "cache"
  ["classes"] => array(0) {
  }
  ["use hooks"] => bool(false)
  ["defaults"] => array(0) {
  }
  ["process"] => string(0) ""
  ["alterable"] => bool(true)
  ["extension"] => string(3) "inc"
  ["info file"] => bool(false)
  ["hook"] => string(18) "page_manager_tasks"
  ["load themes"] => bool(false)
}

Для этого вызова есть shortcut - функция ctools_plugin_get_info():
<?php
// Делает тоже самое что и пример выше:
ctools_include('plugins');
d(ctools_plugin_get_info('page_manager', 'tasks'));
?>

Посмотрим какие элементы нужно определить, чтобы описать тип, для примера возьмем тип плагина tasks, модуля page_manager:

array(13) {
  ["module"] => string(12) "page_manager"
  ["type"] => string(5) "tasks"
  ["cache"] => bool(false)
  ["cache table"] => string(5) "cache"
  ["classes"] => array(0) {
  }
  ["use hooks"] => bool(false)
  ["defaults"] => array(0) {
  }
  ["process"] => string(0) ""
  ["alterable"] => bool(true)
  ["extension"] => string(3) "inc"
  ["info file"] => bool(false)
  ["hook"] => string(18) "page_manager_tasks"
  ["load themes"] => bool(false)
}

Назначение многих элементов описано в файле help/plugins-creating.html, который можно найти в папке модуля ctools. Почти все элементы, если они не определены будут прописаны автоматически:
<?php
$plugin_type_info += array(
'module' => $module,
'type' => $plugin_type_name,
'cache' => FALSE,
'cache table' => 'cache',
'classes' => array(),
'use hooks' => FALSE,
'defaults' => array(),
'process' => '',
'alterable' => TRUE,
'extension' => 'inc',
'info file' => FALSE,
'hook' => $module . '_' . $plugin_type_name,
'load themes' => FALSE,
);
?>
Рассмотрим назначение этих элементов:
- 'cache' (bool) - если установлено в TRUE, ctools_get_plugins() будет каждый раз подключать .inc файл с определением плагина, иначе определение плагина будет кешироваться в таблице 'cache table' (см. следующий пункт).
- 'cache table' (string) - задает таблицу, где будет храниться определения плагина (см. предыдущий пункт).
- 'classes' (array) - название ключей в объявлении плагина, которые могут опредялять имя класса, например, если здесь прописано array('handler'), то ctools будет искать в объявлении плагина элемент 'handler', который может выглядеть, например, так:
<?php
$plugin = array(
// As this is the base class plugin, it shouldn't declare any menu items.
'has menu' => FALSE,
'handler' => array(
'class' => 'ctools_export_ui',
),
);
?>
- 'use hooks' (bool) - если TRUE, плагины этого типа будут подключаться с помощью функции ctools_plugin_load_hooks(), иначе с помощью функции ctools_plugin_load_includes(). Имя хука задается с помощью элемента 'hook'.
- 'defaults' (array) - элементы, объявленные в 'defaults' будут добавлены во все определения плагинов.
- 'process' (callback) - функция которая будет вызвана, если 'alterable' === TRUE. Пример:
<?php
'process' => array(
'function' => 'ctools_export_ui_process',
'file' => 'export-ui.inc',
'path' => drupal_get_path('module', 'ctools') . '/includes',
),
?>
- 'alterable' (bool) - если TRUE, тип плагина и все определения плагинов этого типа могут быть изменены, см. _ctools_process_data().
- 'extension' (string) - расширение файла с плагином.
- 'info file' (bool) - если TRUE, определение плагина будет искаться в файле c расширением .info, вместо .inc. Это может быть полезно, если плагин не содержит кода.
- 'hook' (string) - если 'use hooks' FALSE, плагин будет подключаться с помощью функции ctools_plugin_load_includes(), которая вызывает ctools_plugin_get_directories(). Эта функция вызывает hook_ctools_plugin_directory(), которая должна возвращать директорию (строку) с плагинами. Каждый плагин может быть описан, либо с помомью массива $plugin, либо с помощью функции $module_$file_name_{$info['hook']}.
- 'load themes' (bool) - если TRUE, тогда темы тоже могут определять плагины этого типа.

Теперь когда мы примерно знаем с помощью каких элементов можно описывать типы плагинов, осталось найти больше примеров. Для этого можно сделать поиск по строке '_ctools_plugin_type(' в папках модулей.

Надеюсь, все это может помочь в реализации своих типов плагинов или использовании уже существующих Wink

Полезное по теме

  1. Список модулей, определяющих hook_ctools_plugin_type().
  2. Функция d():
    <?php
    function d() {
    $args = func_get_args();
    $class = '\Myak\Debug\Debugger';
    if (count($args) > 0) {
    call_user_func(array($class, 'skipCaller'), __FILE__, __LINE__ + 1);
    return call_user_func_array(array($class, 'dump'), $args);
    }
    return singleton($class);
    }
    ?>

Комментарии