← Вернуться к курсу

Чистый код в компонентах Битрикс: Магия class.php

Урок №2. Анатомия class.php. Собираем идеальный каркас и управляем жизненным циклом

Текст 15 мин
Введение

На прошлом уроке мы разобрали, почему старый процедурный подход (component.php) — это путь в никуда, и как «лапша» из кода убивает поддержку проекта. Мы увидели, что даже переход на классы не спасает, если не менять само мышление.

Сегодня мы переходим от теории к практике. Мы возьмем наш реальный легаси-компонент и пройдем путь его трансформации в современный класс.

Наш пациент: Легаси-компонент (component.php)

ℹ️ Информация Исходники компонента можно скачать по ссылке.

Это типичный код списка новостей, который можно встретить в 90% проектов.

        // lesson-01-legacy/component.php

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

global $USER, $APPLICATION;

$arParams["IBLOCK_ID"] = intval($arParams["IBLOCK_ID"]);
$arParams["NEWS_COUNT"] = $arParams["NEWS_COUNT"] ? intval($arParams["NEWS_COUNT"]) : 10;
$arParams["CACHE_TIME"] = $arParams["CACHE_TIME"] ? intval($arParams["CACHE_TIME"]) : 3600;

CModule::IncludeModule("iblock");

$obCache = new CPHPCache();
$cacheTime = $arParams["CACHE_TIME"];
$cacheId = serialize($arParams);
$cachePath = "/" . SITE_ID . "/news_list/";

if ($obCache->InitCache($cacheTime, $cacheId, $cachePath)) {
    $vars = $obCache->GetVars();
    $arResult = $vars["arResult"];
} else {
    $obCache->StartDataCache();
    $arResult = array();
    $arResult["ITEMS"] = array();

    $rsElements = CIBlockElement::GetList(
        array("ACTIVE_FROM" => "DESC", "ID" => "DESC"),
        array("IBLOCK_ID" => $arParams["IBLOCK_ID"], "ACTIVE" => "Y"),
        false,
        array("nTopCount" => $arParams["NEWS_COUNT"]),
        array("ID", "NAME", "PREVIEW_TEXT", "PREVIEW_PICTURE", "DETAIL_PAGE_URL", "ACTIVE_FROM", "CREATED_BY")
    );

    while ($obElement = $rsElements->GetNextElement()) {
        $arItem = $obElement->GetFields();
        // ... форматирование, N+1 запросы авторов ...
        $arResult["ITEMS"][] = $arItem;
    }

    $obCache->EndDataCache(array("arResult" => $arResult));
}

$APPLICATION->SetTitle("Новости компании");
$this->IncludeComponentTemplate();

    

Шаг 1: Миграция «в лоб» — переносим код в class.php

Первый логичный шаг при рефакторинге — это перенести старый процедурный код в метод executeComponent. Это промежуточная стадия, которая позволяет запустить компонент на «новом движке», но при этом сохранить старую логику.

Что нужно изменить минимально, чтобы это заработало:

  1. Контекст: Все переменные, которые раньше были «просто переменными» ($arResult, $arParams), теперь становятся свойствами объекта через $this-> ($arResult -> $this->arResult, $arParams -> $this->arParams).
  2. Глобальные объекты: Вместо global $APPLICATION внутри класса лучше использовать \Bitrix\Main\Application (на этом этапе можно оставить и global $APPLICATION).
  3. Вызов шаблона: Функция IncludeComponentTemplate() превращается в метод $this->includeComponentTemplate().
  4. Кэширование: Вместо ручного создания объекта new CPHPCache() и вызова InitCache/StartDataCache, мы переходим на встроенный метод $this->startResultCache(). Это избавляет нас от 10+ строк лишнего кода и ручного управления путями кэша.

Главное изменение: Упрощение кэширования

В component.php мы вручную создавали объект CPHPCache, инициализировали его, проверяли InitCache и вручную сохраняли переменные через EndDataCache. В классе всё это делает один метод $this->startResultCache().

Было (component.php) Стало (class.php)
$obCache = new CPHPCache(); (Не нужно, встроено в родительский класс)
$cacheId = serialize($arParams); (Генерируется автоматически на основе $this->arParams)
if ($obCache->InitCache($time, $id, $path)) if ($this->startResultCache())
$obCache->EndDataCache(["arResult" => $arResult]); (Происходит автоматически при вызове includeComponentTemplate)
⚠️ Важно
$this->startResultCache() автоматически использует время кэширования из $this->arParams["CACHE_TIME"]. Если метод возвращает true — кэша нет, нужно выполнять код. Если false — Битрикс сам достанет $this->arResult из кэша и сразу подключит шаблон.

Почему мы не останавливаемся на этом этапе?

Хотя компонент заработал, мы получили «ООП-макароны». Весь код (100+ строк) по-прежнему лежит в одной куче внутри executeComponent. Это затрудняет дебаг и не дает нам использовать преимущества жизненного цикла Битрикса.

💻 Посмотреть код этого этапа class.php (Шаг 1)

Шаг 2: Используем жизненный цикл (Lifecycle)

Битрикс предоставляет нам встроенные механизмы, которые позволяют разнести логику по этапам.

Метод onPrepareComponentParams: Первый рубеж обороны

Это самый важный метод. Он вызывается до кэширования.

  • Его роль: Привести входящие параметры $arParams к нужному типу.
  • Архитектурный смысл: Гарантировать, что вся остальная логика класса будет работать с валидными данными.

Метод executeComponent: Дирижер, а не оркестр

Этот метод — точка входа. Его задача — управлять последовательностью действий, а не выполнять их все самому.

  1. Переносим валидацию параметров в onPrepareComponentParams.
  2. Выделяем проверку модулей в checkModules.

Результат: Чистый каркас (Boilerplate)

Вот как выглядит наш компонент после первого этапа правильного рефакторинга (код сокращен для наглядности).

        // lesson-02-class-basics/step-2-lifecycle/class.php

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader;

class NewsListComponent extends CBitrixComponent
{
    /**
     * Валидация параметров - теперь в отдельном методе!
     */
    public function onPrepareComponentParams($params): array
    {
        $params["IBLOCK_ID"] = (int)($params["IBLOCK_ID"] ?? 0);
        $params["NEWS_COUNT"] = (int)($params["NEWS_COUNT"] ?? 10);
        $params["CACHE_TIME"] = (int)($params["CACHE_TIME"] ?? 3600);

        return $params;
    }

    /**
     * Проверка подключения модулей
     */
    protected function checkModules(): void
    {
        if (!Loader::includeModule("iblock")) {
            throw new \Bitrix\Main\SystemException("Модуль iblock не установлен");
        }
    }

    public function executeComponent()
    {
        try {
            $this->checkModules();

            if ($this->startResultCache()) {
                // ... логика выборки данных (CIBlockElement::GetList) ...
                
                $this->includeComponentTemplate();
            }

        } catch (\Exception $e) {
            $this->abortResultCache();
            ShowError($e->getMessage());
        }

        // SEO и заголовки
        $GLOBALS["APPLICATION"]->SetTitle("Новости компании");
    }
}

    
💻 Финальный код этого урока Шаг 2

Что мы получили:

  1. Читаемость: executeComponent стал понятнее за счёт разбиения на отдельные методы.
  2. Надежность: Параметры всегда валидны благодаря onPrepareComponentParams.
  3. Безопасность: Ошибки модулей обрабатываются через try/catch.
🛠 Практическая работа

Миграция в class.php

  1. Возьмите легаси-код из репозитория.
  2. Создайте новый компонент на базе классов.
  3. Перенесите логику последовательно: сначала параметры, потом проверку модулей, затем саму выборку.
  4. Убедитесь, что вы используете $this->startResultCache() вместо старого CPHPCache.
  5. Сравните полученный результат с финальной версией

Навигация

Уроки курса

1
Архитектурный кризис компонентов. Почему процедурный подход убивает ваш проект?
2
Анатомия class.php. Собираем идеальный каркас и управляем жизненным циклом
3
Параметры и Кэширование. Строим надежный фундамент и не «травим» кэш (Скоро)
4
Декомпозиция и Чистота. Выносим бизнес-логику в методы (Скоро)
5
Наследование и Трейты. Масштабируем архитектуру без боли и копипаста (Скоро)
6
Финальный Бой. Рефакторинг реального компонента: от «лапши» к архитектуре (Скоро)

Ваш прогресс

Войдите в аккаунт, чтобы отслеживать свой прогресс

Войти
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.

AI Домовой История

0 / 100

Привет! Я помогу с вопросами по 1С-Битрикс.

Спрашивай про D7, ORM, компоненты или события.

Требуется авторизация

Войдите или зарегистрируйтесь, чтобы задавать вопросы AI-ассистенту.

Войти