agentleFS
AnyOrgNameAnyOrgName/php-skills/skills/bitrix/component-engine/SKILL.md

component-engine

Skill4 starsChanged 4 months agoMITView on GitHub

Build 1C-Bitrix components with the CBitrixComponent engine — the executeComponent lifecycle, includeComponentTemplate, component parameters, result caching inside components, the arResult/arParams contract, and the split of responsibilities between component and template. Use when the user needs to pass data from a component to its template via `$arResult`, read `$arParams`, or decide what belongs in `component.php` versus `template.php`; the user is building or modifying a Bitrix component and asks about the `CBitrixComponent` class, the `executeComponent` lifecycle, or `component.php`; пользователь настраивает кеширование результата внутри компонента Битрикс — `startResultCache` / `endResultCache` / `AbortResultCache` — и спрашивает, что включать в ключ кеша и как его сбрасывать. Keywords: bitrix, component, cbitrixcomponent, template, arresult, arparams, cache, lifecycle, d7, legacy, битрикс, компонент.

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 7 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not use this skill for pure kernel bootstrap, event, or ORM query work that does not render through a component or its template.

    line 83
  2. Very specific:

    Do not use this skill on a non-Bitrix PHP app.

    line 85
  3. Distinctive:

    Verify: $arParams is typed and defaulted before executeComponent runs.

    line 138
  4. Distinctive:

    Verify: The cache key includes every varying input and empty results are aborted.

    line 143
  5. Very specific:

    After filling $arResult, call $this->includeComponentTemplate().

    line 144
  6. Distinctive:

    Verify: No markup is echoed from component.php and template output is escaped.

    line 146
  7. Distinctive:

    Verify: The template exits when accessed outside the component context.

    line 163
  8. Distinctive:

    Verify: Every dynamic value is escaped and no data access happens in the template.

    line 166
  9. Very specific:

    Put per-request work that must run even on a cache hit (hit counters, view logging) in component_epilog.php, which runs outside the cached …

    line 167
  10. Distinctive:

    Verify: Per-request side effects run on cache hits via the epilog, not the cached body.

    line 169
  11. Very specific:

    Call $this->AbortResultCache() when the component hits an error or produces no meaningful data (missing element, empty list you do not want …

    line 173

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- generated: do not edit — source: knowledge/atoms/bitrix/component-engine/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->

Bitrix Component Engine

When to use

  • The user needs to pass data from a component to its template via $arResult, read $arParams, or decide what belongs in component.php versus template.php.
  • The user is building or modifying a Bitrix component and asks about the CBitrixComponent class, the executeComponent lifecycle, or component.php.
  • Пользователь настраивает кеширование результата внутри компонента Битрикс — startResultCache / endResultCache / AbortResultCache — и спрашивает, что включать в ключ кеша и как его сбрасывать.

When NOT to use

  • Do not use this skill for pure kernel bootstrap, event, or ORM query work that does not render through a component or its template.
  • Do not use this skill on a non-Bitrix PHP app; CBitrixComponent, includeComponent, and the component/template layout exist only in 1C-Bitrix.

Core guidance

  • Include every varying input in the result cache key — When wrapping work in startResultCache(), pass a cache id built from every input that changes the output (relevant $arParams, language, site, user group) so different inputs get different cache entries.
  • Escape arResult values when emitting them in the template — In template.php, escape $arResult/$arParams values for their output context (htmlspecialcharsbx() for HTML, CUtil::JSEscape() for JS) rather than echoing them raw.
  • Keep data logic in the component, markup in the template — Put data fetching, validation, and business logic in component.php (the CBitrixComponent subclass) and populate $arResult; keep template.php to presentation that reads $arResult/$arParams and emits markup.

Decision Engine

Walk this section before choosing an implementation path.

Is the code you are writing data/logic, or is it presentation markup?

├── Data or logic: Is the data expensive to build and safe to reuse across requests?

│   ├── Expensive and reusable: → cached — wrap the build in startResultCache with a key of all varying inputs, fill arResult, and AbortResultCache on empty/error.

│   └── Per-user or trivial: → component — build arResult directly in executeComponent without result cache (or cache only the shared part) for per-user or cheap data.

└── Presentation markup: → template — read arResult/arParams in template.php and emit escaped markup; do not query or mutate data there.

Full engine: references/decision-engine.md — Decision Tree, Priority Matrix, Decision Table.

Workflows

Workflow: Build Cached Component

Progress:
- [ ] In `component.php`, declare
`class MyComponent extends \CBitrixComponent` and normalize inputs in
`onPrepareComponentParams(array $arParams): array`
- [ ] In `executeComponent()`, wrap the build with
`if ($this->startResultCache(false, [$this->arParams['ID'], LANGUAGE_ID])) {
..
- [ ] After filling `$arResult`, call `$this->includeComponentTemplate();` and keep all
markup in `template.php`, where values are escaped on output
  1. In component.php, declare class MyComponent extends \CBitrixComponent and normalize inputs in onPrepareComponentParams(array $arParams): array.
    • Verify: $arParams is typed and defaulted before executeComponent runs.
  2. In executeComponent(), wrap the build with if ($this->startResultCache(false, [$this->arParams['ID'], LANGUAGE_ID])) { ... $this->arResult = ...; if (empty($this->arResult)) $this->AbortResultCache(); }.
    • Verify: The cache key includes every varying input and empty results are aborted.
  3. After filling $arResult, call $this->includeComponentTemplate(); and keep all markup in template.php, where values are escaped on output.
    • Verify: No markup is echoed from component.php and template output is escaped.

Workflow: Write Template

Progress:
- [ ] Begin `template.php` with the standard guard
`if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();` so the
template is never executed directly
- [ ] Iterate `$arResult['ITEMS']` and output each value escaped, e.g
- [ ] Put per-request work that must run even on a cache hit (hit counters, view
logging) in `component_epilog.php`, which runs outside the cached template body
  1. Begin template.php with the standard guard if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die(); so the template is never executed directly.
    • Verify: The template exits when accessed outside the component context.
  2. Iterate $arResult['ITEMS'] and output each value escaped, e.g. <?= htmlspecialcharsbx($item['NAME']) ?>, without querying or mutating data.
    • Verify: Every dynamic value is escaped and no data access happens in the template.
  3. Put per-request work that must run even on a cache hit (hit counters, view logging) in component_epilog.php, which runs outside the cached template body.
    • Verify: Per-request side effects run on cache hits via the epilog, not the cached body.

Best practices

  • Call $this->AbortResultCache() when the component hits an error or produces no meaningful data (missing element, empty list you do not want to persist) instead of letting endResultCache() store the failure
  • Fill $this->arResult fully in executeComponent(), then render with $this->includeComponentTemplate(); do not echo markup directly from component.php
  • Normalize and default incoming parameters in onPrepareComponentParams() — cast types, apply defaults, and trim — so executeComponent() and the template can trust $arParams

Anti-patterns

Anti-patternWhy it failsDo instead
Caching personalized output under a shared keyA shared key serves one user's personalized render to others, a privacy and correctness breach.Build personalized data uncached, or include the user group / identity in the<br>cache id so entries never cross users.
Echoing markup directly from component.phpDirect output bypasses template selection, the component's caching wrapper, and epilog wiring.Fill $arResult and call $this->includeComponentTemplate(); put all markup in<br>template.php.
Business logic living in the templateLogic in the template breaks caching, blocks template reuse, and mixes concerns that are hard to test.Move the logic into component.php and expose only the prepared data through<br>$arResult; leave presentation in the template.

See references/anti-patterns.md.

Debugging

  1. Cache key that omits a varying input — The first request's rendered result is replayed for every other input value until the entry expires. Fix: Build the cache id from all varying inputs — the relevant $arParams, plus language/site/user-group when they affect output — so each combination caches separately.
  2. Caching an empty or errored result — A cached failure turns a transient error into a persistent one for all subsequent visitors. Fix: Call $this->AbortResultCache() before returning when the build is empty or errored so the failure is never persisted.
  3. Querying data from inside the template — The cached template body is not re-executed on a hit, so template-side queries produce stale or missing data. Fix: Move all data access into component.php/$arResult; if per-request work must run on every hit, use component_epilog.php.
  4. Echoing arResult values without escaping — Passing data through the component does not sanitize it; the template is the output boundary that must escape. Fix: Escape for the output context — htmlspecialcharsbx() for HTML, CUtil::JSEscape() for JavaScript — on every dynamic value in the template.

Performance notes

  • Querying data from inside the template — Running a query or calling the ORM/CIBlockElement inside template.php couples data access to markup and, when the template is cached, the query stops running on cache hits so the data silently goes stale.
  • Include every varying input in the result cache key — When wrapping work in startResultCache(), pass a cache id built from every input that changes the output (relevant $arParams, language, site, user group) so different inputs get different cache entries.

Production checklist

  • The component calls AbortResultCache() on error or empty data so a failed render is never persisted and served to other visitors.
  • Every startResultCache() key is built from all inputs that change the output (relevant $arParams, language, site, user group where applicable).
  • Every dynamic value emitted in template.php is escaped for its output context (htmlspecialcharsbx for HTML, CUtil::JSEscape for JS).
  • Incoming parameters are cast, defaulted, and trimmed in onPrepareComponentParams() so the rest of the lifecycle can trust $arParams.
  • All data fetching and business logic live in component.php/$arResult, and template.php only presents already-prepared data.

Version notes

  • Class-based components supersede procedural component.php — On modern cores, components subclass CBitrixComponent with an executeComponent() method; very old components used a procedural component.php script with no class, which still runs for backward compatibility.
  • PHP support tracks the Bitrix core version — Modern Bitrix cores run components on PHP 7.4 through 8.2; the component engine and stock components were not certified past 8.2 at the time of writing, so avoid relying on PHP 8.3+ syntax in component and template code.

Security

  • The template is the output-escaping boundary — Treat all $arResult/$arParams values as untrusted at output time: escape each for its context in the template (htmlspecialcharsbx for HTML, CUtil::JSEscape for JS), and never build a cache key or SQL from unvalidated request-derived parameters.

Testing

  • Activation and decision behavior are verified in eval/scenarios/bitrix/component-engine.yaml.

Additional resources

  • references/anti-patterns.md
  • references/core.md
  • references/decision-engine.md
  • references/examples.md
  • references/pitfalls.md
  • references/recipes.md
  • references/version-differences.md

Knowledge graph

Prerequisites: bitrix/kernel-main-module
Related: bitrix/cache-layers

<!-- graph-fragment: graph/fragments/bitrix/component-engine.yaml -->

Skill contract

Produces: component.rendered
Requires: bitrix.kernel-bootstrapped
Risk: medium · Confidence: 0.9

<!-- contract-fragment: contracts/fragments/bitrix/component-engine.yaml -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.