bitrix-routing
RoutingConfigurator, /local/routes, PublicPageController, site-guard, urlrewrite migration. Use for public/API URLs.
Install
npx skills add https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-routing
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install bxmaximum-bitrix-framework-skills@llmmart
git clone https://github.com/bxmaximum/bitrix-framework-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole bxmaximum/bitrix-framework-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Routing in Bitrix
Baseline: main 23.0+. Features newer than baseline are marked Since.
Progressive disclosure: open only the rule files that match the task. Do not read every rules/*.md.
How to use
- Identify the layer the task touches.
- Open the matching
rules/*.mdbelow. - Prefer framework-native Bitrix patterns over custom abstractions.
Choose a rule file
When to read rules/setup.md
Read rules/setup.md (Enable routing and module wiring) when the task involves:
- Enabling New Routing
When to read rules/routes-handlers.md
Read rules/routes-handlers.md (Routes, handlers, params, groups) when the task involves:
- Basic
web.php - Supported Methods
- Handlers
- Route Parameters
- Names and URL Generation
- Groups (fluent API)
- Delivering view / component
When to read rules/matching-legacy.md
Read rules/matching-legacy.md (Matching, PublicPageController, site-guard) when the task involves:
- Matching and safety
- PublicPageController (legacy bridge)
- Site-guard (multisite)
- Migration from
urlrewrite.php - Checklist
Checklist
- Opened only the rule file(s) needed for this task.
- Followed DI /
/local// security canons fromAGENTS.md.
Files (bitrix-framework-skills)
-
rules
-
matching-legacy.md 4.3 KB
# Matching, PublicPageController, site-guard ## Matching and safety - All files from `/local/routes/` and `/bitrix/routes/` merge into one `Router`. Matching is **first-match-wins** within the compiled set; order **inside one file** matters. Do not rely on order **across** files — each file must be self-correct. - Legacy `$arUrlRewrite` from `urlrewrite.php` is checked **before** modern routes. A conflicting rewrite means the modern route never runs — remove the legacy rule when migrating. - Compiled route regex is cached (`CompileCache` by file `mtime`). After edits that “don’t apply”, clear routing cache on the stand. - **Do not** use routing `->middleware()` for auth, CSRF, or authorization. Middleware runs early (before normal prolog/controller filters). Protect Engine controllers via ActionFilter/attributes (`bitrix-controllers`); protect `PublicPageController` targets inside the included PHP. ## PublicPageController (legacy bridge) Use `Bitrix\Main\Routing\Controllers\PublicPageController` only to include an existing public PHP page. Do not put new business logic behind it — new features go to Engine controllers. ```php use Bitrix\Main\Routing\Controllers\PublicPageController; use Bitrix\Main\Routing\RoutingConfigurator; return function (RoutingConfigurator $routes): void { $siteDir = '/'; // leading slash — path under document root $sitePrefix = ltrim($siteDir, '/'); $routes ->prefix($sitePrefix . 'docs') ->group(function (RoutingConfigurator $routes) use ($siteDir) { $routes->get('item/{id}/', new PublicPageController($siteDir . 'docs/item.php')) ->where('id', '[0-9]+') ->default('download', '0'); $routes->any('{any}', new PublicPageController($siteDir . 'docs/index.php')) ->where('any', '.*'); }); }; ``` Caveats: - `PublicPageController` `include`s the file and ends the request (`die()`). Engine prolog/filters do **not** wrap it — the page must validate input, rights, CSRF itself. - Route parameters are copied into `$_GET` / `$_REQUEST`. Use `->default()` for flags the page reads from query. - `$siteDir` (with leading `/`) for file paths; `$sitePrefix` (no leading `/`) for `prefix()`. ## Site-guard (multisite) If routes belong to one site only, guard at the start of the closure and `return` without registering when the current request site does not match. Without a guard, those routes register for every site. ```php use Bitrix\Main\SiteTable; return function (RoutingConfigurator $routes): void { // Global/service routes (all sites) — register before guard // $routes->any('.well-known/{any}', …); $request = \Bitrix\Main\Context::getCurrent()->getRequest(); $site = SiteTable::getByDomain($request->getHttpHost(), $request->getRequestUri())->fetch(); if (!$site || ($site['LID'] ?? '') !== 's1') { return; } // site-specific routes… }; ``` Site-guard is not authorization — still check rights in the handler/controller. ## Migration from `urlrewrite.php` 1. For **new** routes, use `/local/routes/web.php` — `urlrewrite.php` is no longer needed. 2. Old `urlrewrite.php` can be left for legacy component SEF. 3. Migration rule: entry ```php ['CONDITION' => '#^/catalog/section/(\d+)/?$#', 'RULE' => 'SECTION_ID=$1', 'PATH' => '/catalog/section.php'] ``` is replaced by ```php $routes->get('/catalog/section/{id}', [Catalog::class, 'sectionAction'])->where('id', '\d+'); ``` 4. Clear `urlrewrite` cache when migrating: `CUrlRewriter::ReIndexAll()`. ## Checklist - [ ] Web server forwards to `routing_index.php`. - [ ] Global `routing.config` lists basenames; user files under `/local/routes/`. - [ ] Module route files are `require`d from `/local/routes/web.php` (not module `.settings.php` `routing`). - [ ] No conflicting `urlrewrite.php` rule for the same path. - [ ] Groups use fluent `->prefix()->name()->group(fn)`; narrow routes before catch-all. - [ ] State-changing routes use explicit verbs, not `any()`. - [ ] Parameters constrained with `->where(...)`; names unique for `router()->route()`. - [ ] Auth/CSRF via controller filters — not routing middleware. - [ ] `PublicPageController` only for legacy includes; page handles security itself. - [ ] Multisite: site-guard when routes are site-specific. -
routes-handlers.md 4.1 KB
# Routes, handlers, params, groups ## Basic `web.php` ```php <?php declare(strict_types=1); use Bitrix\Main\Routing\RoutingConfigurator; use Vendor\Module\Infrastructure\Controller\Post; return function (RoutingConfigurator $routes): void { $routes->get('/api/posts', [Post::class, 'listAction'])->name('post.list'); $routes->get('/api/posts/{id}', [Post::class, 'getAction']) ->where('id', '\d+') ->name('post.get'); $routes->post('/api/posts', [Post::class, 'createAction'])->name('post.create'); $routes->put('/api/posts/{id}', [Post::class, 'updateAction'])->where('id', '\d+'); $routes->delete('/api/posts/{id}', [Post::class, 'deleteAction'])->where('id', '\d+'); }; ``` ## Supported Methods - `get`, `post`, `put`, `patch`, `delete`, `head`, `options` — for specific HTTP methods. - `any($uri, $handler)` — for any method. - `match(['GET', 'POST'], $uri, $handler)` — explicit list of methods. - `get` routes also accept `HEAD` automatically. ## Handlers Accepted: - `[Controller::class, 'actionName']` — Engine controller; name **without** or with `Action` suffix (`view` / `viewAction` — routing strips `Action` if present). Prefer short form `view`. - Callable/closure — AutoWire can inject route params, `Route`, `HttpRequest`. Keep closures thin; grow into a controller when CSRF/auth/stable contract appears. - Action class string only if it implements `Bitrix\Main\Engine\Contract\RoutableAction`. - `PublicPageController` — include a legacy PHP page (see below). ```php $routes->get('/health', function () { return new \Bitrix\Main\HttpResponse('ok'); }); ``` Closure return: `HttpResponse`, string, array (converted to JSON), `null`. Prefer explicit HTTP verbs (`get`/`post`/…) over `any()` for state-changing operations. ## Route Parameters ```php $routes->get('/posts/{slug}', [Post::class, 'bySlugAction']); $routes->get('/users/{id}/posts/{postId?}', [Post::class, 'userPostsAction']); ``` `{param?}` is optional (requires a `default`): ```php $routes->get('/posts/{page?}', [Post::class, 'listAction'])->default('page', 1); ``` Regex on parameter: ```php $routes->get('/posts/{id}', [Post::class, 'getAction']) ->where('id', '[0-9]+'); $routes->get('/{section}/{slug}', $handler) ->where(['section' => '[a-z]+', 'slug' => '[a-z0-9\-]+']); ``` ## Names and URL Generation ```php $routes->get('/posts/{id}', [Post::class, 'getAction']) ->where('id', '\d+') ->name('post.get'); ``` ```php $url = (string)\Bitrix\Main\Application::getInstance() ->getRouter() ->route('post.get', ['id' => 42]); // /posts/42 ``` ## Groups (fluent API) `group()` accepts **only a closure**. Apply `prefix` / `name` via the fluent chain: ```php $routes ->prefix('api') ->name('api.') ->group(function (RoutingConfigurator $routes) { $routes->get('/posts', [Post::class, 'listAction'])->name('post.list'); // URL: /api/posts, name: api.post.list $routes ->prefix('admin') ->name('admin.') ->group(function (RoutingConfigurator $routes) { $routes->get('/stats', [Admin::class, 'statsAction'])->name('stats'); // URL: /api/admin/stats, name: api.admin.stats }); }); ``` Also available on the configurator/options: `where` (group defaults), `domain`. > Do **not** use Laravel-style `group(['prefix' => '/api'], fn () => …)` — that is not the Bitrix API. ### Prefix and trailing slash - `->prefix()` takes a path **without** a leading slash (`api`, or `ltrim($siteDir, '/')`). - For fixed leaf paths without a dynamic segment, prefer a trailing slash in the URI (`…/settings/`) or a catch-all `{any}` — otherwise the web server may treat the path as a static file and 404 before `routing_index.php`. - Inside a group: register **narrow** routes first, then catch-all `{any}` with `->where('any', '.*')`. ## Delivering view / component ```php $routes->get('/about', fn () => \Bitrix\Main\Engine\Response\Component::createByComponentName( 'bitrix:main.include', '.default', ['PATH' => '/about.inc.php'] )); ``` Or return an array/object — the engine serializes via `Engine\Response\Converter`. -
setup.md 1.6 KB
# Enable routing and module wiring ## Enabling New Routing ### Web server Route non-existent files to `routing_index.php`: **Apache** (`.htaccess`): ```apache RewriteCond %{REQUEST_FILENAME} !/bitrix/routing_index.php$ RewriteRule ^(.*)$ /bitrix/routing_index.php [L] ``` **Nginx**: ```nginx try_files $uri $uri/ /bitrix/routing_index.php; ``` ### Global `.settings.php` only In `/local/.settings.php` (or `/bitrix/.settings.php`): ```php 'routing' => [ 'value' => [ 'config' => ['web.php'], // basename only — searched in /local/routes/ and /bitrix/routes/ ], 'readonly' => true, ], ``` How the kernel loads files (`Application::initializeRouter`): 1. For each name in `routing.config`, look for `/local/routes/<name>` **and** `/bitrix/routes/<name>` (both may be included). 2. Then append system `/bitrix/routes/web_bitrix.php` if present. 3. Each file must `return` a `callable(RoutingConfigurator $routes): void`. > **User routes belong only in `/local/routes/`.** `/bitrix/routes/` is reserved for the system. ### Module routes — require pattern A `routing` section in **module** `.settings.php` is **not** read by the router. Keep module files under `/local/modules/<id>/routes/` and include them from `/local/routes/web.php`: ```php <?php declare(strict_types=1); use Bitrix\Main\Routing\RoutingConfigurator; return function (RoutingConfigurator $routes): void { $moduleRoutes = $_SERVER['DOCUMENT_ROOT'] . '/local/modules/vendor.module/routes/web.php'; if (is_file($moduleRoutes)) { (require $moduleRoutes)($routes); } // project-level routes… }; ```
-
-
SKILL.md 1.4 KB
--- name: bitrix-routing description: RoutingConfigurator, /local/routes, PublicPageController, site-guard, urlrewrite migration. Use for public/API URLs. --- # Routing in Bitrix Baseline: **main 23.0+**. Features newer than baseline are marked **Since**. Progressive disclosure: open **only** the rule files that match the task. Do not read every `rules/*.md`. ## How to use 1. Identify the layer the task touches. 2. Open the matching `rules/*.md` below. 3. Prefer framework-native Bitrix patterns over custom abstractions. ## Choose a rule file ### When to read `rules/setup.md` Read `rules/setup.md` (`Enable routing and module wiring`) when the task involves: - Enabling New Routing ### When to read `rules/routes-handlers.md` Read `rules/routes-handlers.md` (`Routes, handlers, params, groups`) when the task involves: - Basic `web.php` - Supported Methods - Handlers - Route Parameters - Names and URL Generation - Groups (fluent API) - Delivering view / component ### When to read `rules/matching-legacy.md` Read `rules/matching-legacy.md` (`Matching, PublicPageController, site-guard`) when the task involves: - Matching and safety - PublicPageController (legacy bridge) - Site-guard (multisite) - Migration from `urlrewrite.php` - Checklist ## Checklist - [ ] Opened only the rule file(s) needed for this task. - [ ] Followed DI / `/local/` / security canons from `AGENTS.md`.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.