1 (edited by PunBB 2019.12.17 19:21)

Topic: Developer Helper - помощник разработчикам по созданию расширений

Developer_helper предназначен помогать разработчикам создавать расширения для PunBB.
Он позволяет программировать с использованием ООП и MVC.
По-началу этот подход может показаться сложным, неудобным и даже может вызывать приступы бешенства,
у тех кто не знаком с MVC и не привык к ООП.
Тем не менее со временем становится понятен принцип работы этого помощника и становится
возможным уход от процедурного программирования, особенно там где уместно ООП.

Скачать расширение Developer Helper

1

2

Re: Developer Helper - помощник разработчикам по созданию расширений

1. Загрузка хелпера

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

2. Глобальные переменные   
Хелпер предоставляет прямой доступ к большинству глобальных переменных ядра форума
посредством одноимённых свойств статического класса App. Например, для получения глобальной переменной
$forum_user в своей функции, вместо её предварительного объявления
globals $forum_user;
можно обратится к ней через свойство класса App:
App::$forum_user

Вот список переменных ядра, доступных через этот класс:
$lang_common, $forum_db, $forum_user, $forum_page, $forum_config,
$forum_hooks, $forum_url, $forum_flash, $forum_loader, $base_url;

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

Класс имеет 2 метода, устанавливающее и считывающее произвольное значение:
// установить значение
Registry::set('variable', $value);

// получить значение
echo Registry::get('variable');

3

Re: Developer Helper - помощник разработчикам по созданию расширений

3. Автоматическая загрузка классов

Хелпер умеет автоматически загружать классы.
Для корректной загрузки класс должен следовать простым правилам.

3.1 Класс должен распологаться в одной из папок расширения: controller, model, view или module
3.2 Класс должен называться: Имя_расширения_Имя_папки_Имя_класса
3.3 Файл класса должен называться так-же как последняя часть в названии класса.

Например у нас есть расширение My_reputation, мы хотим создать контроллер,
файл контроллера класса мы решили назвать reputation.php, тогда имя класса будет:
My_reputation_Controller_Reputation, этот файл должен быть в папке controller.

Если предустановленного набора имен папок недостаточно,
то можно добавить имя своей произвольной папки:
App::add_autoload_folder('my_folder');

Если требуется свой механизм автозагрузки, то его можно зарегистрировать так:
App::register_autoloader('new_autoloader');

4

Re: Developer Helper - помощник разработчикам по созданию расширений

4. Языковые файлы и доступ к языковым переменным

Получить доступ к языковым переменным ядра можно только к определенным в $lang_common
через соответсвующее имя свойства класса Аpp, например:
App::$lang_common   
Другие языковые определения ядра доступны только традиционным способом.

Доступ к языковым переменным расширения осуществляется через App::$lang.
Загрузка языкового файла отличается от традиционной и основана на пространствах имен:
App::load_language('my_reputation.reputation');

Здесь my_reputation - это имя расширения, а "reputation" - имя загружаемого файла.

Эта инструкция будет искать фалй по следующему пути:
my_reputation/lang/(English)/reputation.php

В App::load_language('my_reputation.reputation'); мы не указываем язык явно, хелпер
выбирает его исходя во-первых из настроек пользователя, во-вторых исходя из наличия такого
языка в расширении. Поэтому наличие папки English обязательно, т.к. она используется по-умолчанию
при отсутствии папки с именем языка установленного в настройках пользоватея.
В противном случае приложение завершит работу с ошибкой.

Структура языкового файла тоже нестандартно для PunBB. У расширения, использующего
хелпер, языковой файл должен выглядеть следующим образом:

return array(
  'Reputation'    => 'Reputation',
  'Disabled'      => 'Reputation System is currently disabled',
  ...
);

Доступ к переменным: App::$lang['Disabled'];

5

Re: Developer Helper - помощник разработчикам по созданию расширений

5. Базовый класс Base.php

Базовый класс реализует функциональность "магических" функций php.
Несмотря на то, что их использование влечет за собой некоторую утерю "прозрачности",
тем не менее, такие функции очень полезны, поскольку упрощают процесс разработки.
Класс реализует методы __set, __get, __isset и __unset.
Таким образом в своем классе, унаследованном от Base, можно создавать и использовать
переменные, заранее не определенные и доступные из других частей программного кода.
Например в процессе выполнения программы возникла необходимость сохранить некоторое значение
в своем классе My_class в переменной my_variable, тогда
мы просто используем её: $My_class->my_variable = 'my_value';

Кроме этого класс Base реализует интерфейс SplSubject, благодоря этому всем его потомкам
доступен паттерн Observer в виде традиционных методов:
attach(SplObserver $observer)
detach(SplObserver $observer)
notify()

Вообще, "магических" функций php и интерфейсы SplSubject и SplObserver являются
основами php и шаблонного программирования,
поэтому использование этой функциональности зависит от вашего уровня владения вопросом.

Остается добавить, что базовый контроллер наследуется от base.php

6

Re: Developer Helper - помощник разработчикам по созданию расширений

6. Добавление своих пунктов в навигацию в профиле

App::add_profile_menu($item);

Где $item - это массив:
section - название страницы, на которой пункт будет активным.
    Другими словами, это значение будет    присвоено константе FORUM_PAGE.
    Например, на странице "настройки" профиля этот параметр равен 'profile-settings'
   

href   - сформированный тег a, например: <a href="http://myforum.url">my forum</a>
name    - имя расширения
number   - порядковый номер пункта меню
path   - относительный путь в файловой системе к расширению (необязательно) 
url      - url расширения (необязательно)

7

Re: Developer Helper - помощник разработчикам по созданию расширений

7. Добавление своих пунктов меню и подменю в админ-центре

Функция практически идентична предыдущей, за исключением

8. Добавление произвольного кода для хуков во время выполнения (хакерская штучка)

Добавить обработчик любого хука просто, главное, чтобы он был добавлен до вызова хука:

App::inject_hook('hook_name', array(
  'name'
  'path'
  'url'
  'code'
));

name - это обычно id расширения,
path - относительный путь в файловой системе к расширению, необязательный параметр
url - путь к расширению - необязательный параметр
code - исполняемый код, аналогичный тому, который обычно содержится в манифесте

Остается добавить, что функции добавления пунктов меню в профиле и админ-центре являются обертками
для функции inject_hook

8

Re: Developer Helper - помощник разработчикам по созданию расширений

9. Облегченное формирование пажинации

Если вы используете Pagination, то код достаточно громоздкий. Поскольку это частоиспользуемая операция,
то намного удобней сформировать навигацию по страницам так:

App::paginate($total_count, $items_per_page, App::$forum_url['topic'], array('param'=> $value);

10. Аякс запросы и ответы json

Если вы используете Аякс, то при отправке запросов к серверу будет послан заголовок XMLHttpRequest
По крайней мере такие библиотеки как jQuery это делают.
Хелпер определит такой запрос и установит флаг is_ajax в состояние TRUE;
В любой момент времени проверить его состояние можно обратившись к этой переменной:
if (App::$is_ajax)

Кроме того может быть полезной функция send_json($params).
После обработки запроса, если вы используете JSON, и вам необходимо отправить JSON данные клиенту,
вы можете вызвать send_json($params), передав в качестве аргумента массив параметров.
Функция является оберткой к стандартной json_encode, вот ее код:

  public static function send_json($params)
  {
    header('Content-type: text/html; charset=utf-8');
    echo json_encode($params);
    die;
  } 

В результате ваши данные будут отправлены клиенту и будет осуществлено завершение выполнения программы.

9

Re: Developer Helper - помощник разработчикам по созданию расширений

11. Роутинг

11.1 Введение
Роутинг предоставляет удобный механизм вызова расширений.
Сразу пример:

misc.php?r=my_reputation/reputation/view/uid/2

параметр r содержит информацию для роутинга. а именно - расширение/контроллер/экшн/параметры
в данном примере это значит:
расширение - my_reputation
контроллер - reputation
метод контроллера (или action) - view
параметры - uid=2

Хелпер, отслеживает вызовы misc.php и обнаружив параметр роутинга r производит его разбор
выделяет расширение, контроллер, метод, параметры (если они есть) и осуществялет
последовательно инициализацию класса контроллера, инициализацию переменных класса, переданных
в параметрах, а затем производит вызов метода класса.
В нашем примере у контроллера появится свойство uid, которому будет присвоено значение 2:
echo $my_reputation->uid;

Дальше хелпер осуществляет рендеринг вывода и зщавершает работу.

Для осуществления роутинга в своем расширении необходимо придерживаться правил именования
описанных в п.3 "Автоматическая загрузка классов".

С точки зрения разработчика расширений, использование роутинга позволяет отказаться от
самостоятельного поиска места перехвата вызова, чтобы осуществить передачу управления
своему скрипту и программирования манифеста.
Кроме этого из строки запроса всегда видно куда конкретно ведёт запрос, это всегда:
расширение/контроллер/метод . Ну и кроме того, такое соглашение об именовании гарантирует, что
строка запроса GET вашего расширения не вступит в конфликт с другим расширением, т.к. имена расширений
уникальны.

Итак, если мы хотим получить управление в свои руки при переходе по нашей ссылке,
то мы должны сделать 2 вещи: создать ссылку в соответствии с соглашением и создать контроллер.
При переходе по такой ссылке хелпер вызовет наш контроллер автоматически.

11.2 Виды Роутинга

PunBB имеет особенность, которая наложила отпечаток на Роутинг.
Особенность обусловлена дополнительными меню, которые присутствут и отличаются друг от друга
в админ-центре, в профиле, и отсутствуют в остальных частях форума.
Таким образом получилось 3 вида роутинга и соответствующие строки запроса:
1) Профиль: profile.php?r=
2) Админ-центр: admin/settings.php?section=route&r=
3) Всё остальное: misc.php?r=

Это не очень красиво и на вид избыточно, но таков PunBB и такое разделение позволяет
получить управление в нужном контексте.

10

Re: Developer Helper - помощник разработчикам по созданию расширений

12. View и вывод

Хелпер использует View для отображения информации. Контроллер должен подготовить данные
и инициализировать необходимые представления. После завершения работы контроллера
хелпер в случае если View инициализирован осуществит рендеринг, вызвав View::$instance->render();
При этом особенность такова, что рендеринг затрагивает только <!-- forum_main --> в шаблоне,
остальные части обслуживает ядро, вот так выглядит код хелпера:

    if (View::$instance)
    {
      require FORUM_ROOT.'header.php';
      ob_start();
      echo  View::$instance->render();
      $tpl_temp = forum_trim(ob_get_contents());
      $tpl_main = str_replace('<!-- forum_main -->', $tpl_temp, $tpl_main);
      ob_end_clean();
      require FORUM_ROOT.'footer.php';
    }  

View представляет собой View из фреймворка Kohana с минимальными изменениями, обеспечивающими
функционирование в среде PunBB.

Инициализация View:

View::$instance = View::factory($this->view.'view_name', array ('param' => $value));

Здесь первый параметр - относительный путь к файлу представления, второй - массив именованных параметров.
Т.к. контроллер при инициализации устанавливает значение $this->view как путь к каталогу  view расширения
то нам остается только добавить имя конкретного файла без '.php':
$this->view.'view_name'
сам файл представления, в нашем случае это view_name.php, может выглядеть примерно так:
<p><?php echo $param ?></p>
<div><?php echo $additional ?></div>

После инициализации View мы можем устанвливать отдельные части представления как другие представления :

View::$instance->additional = View::factory($this->view.'additional', array ('param2' => $value))

;

11 (edited by PunBB 2017.04.02 13:28)

Re: Developer Helper - помощник разработчикам по созданию расширений

13. Breadcrumbs

Хелпер инициализирует Breadcrumbs, добавляя начальное значение - главная страница.
В зависимости от типа роутинга - профиль или админ-центр он так же добавит значение "профиль пользоватоля"
или "Администриование". Вам остается дополнить крошки исходя из своих соображений, например так:

App::$forum_page['crumbs'][] = App::$lang['Comment header'

];


14. Безопасность

Передача параметров в запросе может оказать влияние на программу и привести к ошибкам и
проблемам с безопасностью. Например в коде может использоваться параметр uid, при этом ожидается,
что он передан пользователем и должен иметь тип int. Этот параметр используется в SQL запросе.
Во избежание подмены параметра на строковой, следует проверить его  тип,
но хелпер позволяет применить к нему фильтр, гарантирующий,
что значение переданное пользователем будет нужного типа, либо ход программы будет остановлен.
Для этого в конструкторе контроллера вызываем:
$this->set_filter(array('uid' => 'int',    'pid' => 'int',    'rid' => 'int'));
Здесь мы ожидаем параметры uid, pid и rid и все типа integer.
Предустановленные фильтры:

'int'     =>  'is_numeric',
'bool'     =>  'is_bool',
'float'    =>  'is_float',
'string'  =>  'is_string',
'array'    =>  'is_array',
'object'  =>  'is_object'

Они определены в базовом контроллере.

Если вы используете csrf_token в запросе, то можно его проверить:

$this->_check_csrf_token($generated_token)

15. HTML helper