1 (29.04.2014 08:38 отредактировано hcs)

Тема: [Релиз] Помощник разработчика ( developer_helper )

Расширение предназначено для разработчиков. Всем остальным оно может понадобиться только если от него завист другое расширение.
Делалось как помощник для самого себя в первую очередь чтобы облегчить разработку сложного расширения, имеющего множество точек входа. Во вторую очередь, чтобы не делать впоследствии одно и то же.
О возможностях можно посмотреть в некоем подобии документации в следующем сообщении.
Огромное спасибо всем, кто принимал участие в появлении на свет этого расширения, в первую очередь KANekT который один из первых взял его на вооружение и помог пофиксить массу багов и Griff0n'у за активное участие в проектировании.

Текущая версия 1.0.0.28 для punbb 1.4.2
Скачать:
http://punbb.ru/downloads/v14/developer_helper.zip

Захочешь — найдешь время, не захочешь — найдешь причину.

Поделиться

2 (15.10.2012 22:20 отредактировано hcs)

Re: [Релиз] Помощник разработчика ( developer_helper )

Введение

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


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. Автоматическая загрузка классов

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

  • 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. Языковые файлы и доступ к языковым переменным

Получить доступ к языковым переменным ядра можно только к определенным в $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. Базовый класс 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. Добавление своих пунктов в навигацию в профиле

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. Добавление своих пунктов меню и подменю в админ-центре

Функция практически идентична предыдущей.
[ TODO: добавить описание ]


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

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

App::inject_hook('hook_name', array(
    'name'=> 'name',
    'path'=> 'path',
    'url'=> 'url',
    'code'=> 'code',
));
  • name - это обычно id расширения,

  • path - относительный путь в файловой системе к расширению, необязательный параметр

  • url - путь к расширению - необязательный параметр

  • code - исполняемый код, аналогичный тому, который обычно содержится в манифесте

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


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;
    } 

В результате ваши данные будут отправлены клиенту и будет осуществлено завершение выполнения программы.
Кроме того, если ваше расширение использует ajax и в процессе обработки запроса произойдет ошибка и ядро сформирует ответ функцией message(''), то помощник предотвратит отправку страницы и сформирует ответ в виде json:

[{
code :  -1,
message : 'message'
}]

А если ядро сформирует редирект, то будет отправлен json-ответ дополнительно сожержащий адрес перенаправления:

[{
code :  -2,
message : 'message',
destination_url: 'destination_url'
}]

Поэтому при работе с ajax будьте внимательны, при получении ответа с сервера, особенно если вы используете в качестве флага состояние название code.


11. Роутинг

11.1 Введение

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

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 и такое разделение позволяет
получить управление в нужном контексте.

11.3 Контроллер

Контроллер представляет собой класс, унаследованный от базового контроллера.
Он инициализируется хелпером и у него вызывается метод, определенный роутером в результате разбора строки запроса. Так выглядит типичный контроллер на примере из 11.1:

 
class Reputation_Controller_Reputation extends Controller
{
    public function __construct($ext_path)
    {
        parent::__construct($ext_path);
        App::load_language('reputation.reputation');
    }
    public function view()
    {
        echo 'Hello world';
    }
}


Класс назван в соответствии с п3.2: Имя_расширения_Имя_папки_Имя_класса,  унаследован от базового контроллера. В конструкторе осуществляется загрузка локализации, а так же реализован метод view, к которому осуществляется доступ.

 
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::$forum_override = true;

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));

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 в GET-запросе, то можно его проверить:

$this->_check_csrf_token($generated_token)


15. HTML helper
coming soon...

Захочешь — найдешь время, не захочешь — найдешь причину.

Поделиться

3 (11.12.2013 12:56 отредактировано Zevs)

Re: [Релиз] Помощник разработчика ( developer_helper )

почему при установке helpera может возникать такая ошибка ?

Fatal error: require(): Failed opening required '../style//.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/xxxxxxxxxx/forum/header.php on line 114

$forum_user['style'] перестает возвращать значение.  Все остальные расширения я отключил...

Нашел, откуда ноги растут - буду разбираться, почему. Отбой тревоги smile

Сайт Zevs

Поделиться