Claude Skill

bitrix-routing

RoutingConfigurator, /local/routes, PublicPageController, site-guard, urlrewrite migration. Use for public/API URLs.

LLM Mart · 0 points · 12 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download bxmaximum-bitrix-framework-skills-skills_bitrix-routing-66c40e0.zip · 5 KB
Part of bxmaximum/bitrix-framework-skills — 38 skills

Install

skills CLI npx skills add https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-routing
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install bxmaximum-bitrix-framework-skills@llmmart
Git 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

  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.
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.

No comments yet.

Reviews (0)

No reviews yet.

Related