Хуки в PHP — расширение функций и контроллеров ядра

Общая информация

Многие функции и методы CS-Cart имеют специальные хуки.

Хуки позволяют модифицировать и расширять возможности платформы с помощью модуля.

С помощью хука можно:

  • Изменять входящие параметры функции
  • Дополнять логику работы функции
  • Влиять на результат работы функции
  • Выполнять собственную функцию при выполнении стандартной функции

Хуки расположены в функциях и методах ядра CS-Cart.

Общий принцип использования и работы с хуками:

  1. Определились с необходимыми изменениями стандартной функциональности
  2. Нашли подходящий хук, рядом с местом необходимых изменений, вам обязательно встретятся хуки в процессе п. 1
  3. Подключились к хуку своим модулем и внесли нужные изменения.

Доступно очень много хуков:

База хуков ядра (на английском).

Как выглядит и как использовать хук?

Хуки в PHP выглядят так:

fn_set_hook('hook_name', $param1, $param2, $param3, $param4);

Чтобы подключиться к хуку, вам необходимо:

  1. Инициализировать подключение к хуку.

    В своём модуле откройте или создайте файл app/addons/[id_модуля]/init.php.

    В данный файл добавьте функцию:

    <?php
    
    if (!defined('BOOTSTRAP')) { die('Access denied'); }
    
    fn_register_hooks(
        'hook_name'
    );
    

    Если используте несколько хуков, передавайте названия хуков через запятую:

    fn_register_hooks(
        'hook_name',
        'hook_name_2',
        'hook_name_3',
    );
    
  2. Откройте или создайте файл app/addons/[id_модуля]/func.php.

    Создайте функцию, которая будет выполняться в хуке.

    Функция должна иметь название вида: fn_[id_модуля]_[название_хука]($[параметры_хука_через_запятую])

    В функции будут доступны все параметры передаваемые в хук.

    Чтобы функция могла влиять на параметры (изменять снаружи), их необходимо передавать как ссылки (&$param)

    // Создаём функцию, которая подключится к хуку.
    function fn_[id_модуля]_hook_name(&$param1, &$param2, &$param3, &$param4) {
    
        // Изменяем нужны параметр
        $param1 = 'новое значение';
    
    }
    

Живой пример

Предположим, что нам нужно добавить какую то новую информацию о товаре, если товара нет на складе.

Функция fn_get_product_data() получает информацию о товаре для карточки товара (и много ещё где используется). Данная функция находится в файле app/functions/fn.catalog.php .

Она имеет 3 хука:

// В начале функции, все входящие параметры доступны.
fn_set_hook('get_product_data_pre', $product_id, $auth, $lang_code, $field_list, $get_add_pairs, $get_main_pair, $get_taxes, $get_qty_discounts, $preview, $features, $skip_company_condition);

// В середине функции, перед SQL запросом, чтобы можно было расширить SQL запрос.
fn_set_hook('get_product_data', $product_id, $field_list, $join, $auth, $lang_code, $condition);

// В конце, доступен результат работы функции — массив с данными о товара $product_data
fn_set_hook('get_product_data_post', $product_data, $auth, $preview, $lang_code);

Подключимся к последнему хуку и добавим нужную нам информацию с помощью модуля “Мои изменения”:

  1. Создадим файл app/addons/my_changes/init.php, чтобы инициализировать подключение к хуку.

    Добавим в него код:

    <?php
    
    if (!defined('BOOTSTRAP')) { die('Access denied'); }
    
    fn_register_hooks(
        'get_product_data_post'
    );
    
  2. Создадим функцию для подключения к хуку.

    Создадим файл app/addons/my_changes/func.php

    Добавим новую функцию, которая сработает в хуке:

    <?php
    
    if (!defined('BOOTSTRAP')) { die('Access denied'); }
    
    // Создаём функцию, которая подключится к хуку.
    function fn_my_changes_get_product_data_post(&$product_data, $auth, $preview, $lang_code) {
    
        if ($product_data <= 0) {
    
            $product_data['new_info'] = 'Bam Bam Bigelow';
    
        }
    
    }
    
  3. Включим модуль “Мои изменения”.

Всё.

Важно

Как проверить что оно работает?

Используйте функцию fn_print_r($product_data); до хука, после хука или внутри хука. Она распечатает на экран содержимое массива.

Как я могу использовать именно этот пример?

Создайте новую вкладку с SMARTY блоком для карточки товара. В данном SMARTY блоке вы можете использовать информацию из массива {$product_data}, в том числе вашу новую информацию, например, для каких либо условий.

Разграничение редакций

Функционал хуков ядра необходим для правильной реализации архитектуры различных редакций. Сейчас редакций две: ULTIMATE (CS-Cart) и MULTIVENDOR.

Хуки, которыми будут расширяться редакции, необходимы, чтобы избежать услвоий по типу:

if (PRODUCT_EDITION == 'MVE') {
    do_something

Также хуки позволят при сборке релизов исключать лишний код. Например при сборке ULTIMATE-редакции нам не нужны функции, которые относятся к Multi-Vendor. Подключаемые файлы располагаются там же где и остальные функции. Сейчас редакций две, поэтому файла только два:

app
 └── functions
      ├── fn.multivendor.php
      └── fn.ultimate.php

Пример: допустим, у нас в ядре есть функция fn_place_order, в которой регистрируется хук:

fn_set_hook('place_order', $cart, $allow);

Нам нужно расширить данную функцию в Multi-Vendor. Таким образом, в файле /app/functions/fn.multivendor.php создаем функцию:

function fn_mve_place_order(...) {

    ..
    do_somtehing
    ..

    //
    fn_set_hook('mve_pre_place_order', $cart, $allow);
}

Код для определенной редакции

Для того, чтобы писать код, который должнен появиться только в определенной редакции, нужно использовать конструкцию {if "EDITION"|fn_allowed_for}{/if}, где EDITION заменяется на ULTIMATE или MULTIVENDOR:

{if "ULTIMATE,MULTIVENDOR"|fn_allowed_for}
   Some code
{/if}

Или же использовать отрицание:

{if !"MULTIVENDOR"|fn_allowed_for}
   Some code
{/if}

Первая часть кода означает, что этот код появится в перечисленных редакциях, вторая же часть — что код НЕ появится в этих редакциях

Хуки в PHP скриптах

Версионные теги так же используют фукнцию fn_allowed_for:

if (fn_allowed_for('ULTIMATE')) {
... some code ...
}