Bitrix Modules
Published by bxmaximum in bitrix-framework-skills
What this skill does
Covers creation and maintenance of a custom Bitrix module in /local/modules/<vendor>.<module>/ — CModule class, install/index.php, DoInstall and DoUninstall, install/version.php with $arModuleVersion, registration of events and agents during installation, module options (options.php), generation via make:module. Applied when creating a new module, refining installation/uninstallation, registering event handlers and publishing module options in the Admin Panel. Key terms — CModule, DoInstall, DoU
Add Bitrix Modules to your agent
Review the source and files first. When you are ready, copy the prompt instruction or use the CLI command supported by your environment.
Install with a prompt
Paste this into a compatible coding agent:
add this skill "bitrix-modules" from https://github.com/bxmaximum/bitrix-framework-skillsInstall with the CLI
Run this command in a controlled environment after reviewing the repository:
npx skills add https://github.com/bxmaximum/bitrix-framework-skills --skill bitrix-modulesSkill instructions
Bitrix Modules
Identifier and Namespace
- Identifier:
<vendor>.<module>(lowercase, no_, no digit at start). - Installer class:
<vendor>_<module>(dot →_). - Namespace:
\<Vendor>\<Module>\...(dot →\, CamelCase) for partner modules with a dot in the id. - One-word module id (no partner prefix), e.g.
mymodule: installer classmymodule, PSR-4 namespace\Bitrix\Mymodule(Loader usesBitrix\+ucfirst($moduleName)), not\Mymodule.
Quick Creation
php bitrix/bitrix.php make:module vendor.module
Since main 25.900. On older versions, scaffold files manually.
make:module creates a minimal skeleton only:
install/index.php,install/version.phpinstall/mysql/install.sql,install/mysql/uninstall.sql(empty stubs)default_option.phplang/ru/install/index.php
It does not create .settings.php, /lib/, routes, or controllers. Add those yourself or via further make:* / dev:module-skeleton.
Minimal Structure
/local/modules/vendor.module/
├── install/
│ ├── index.php
│ ├── version.php
│ └── mysql/ # optional SQL stubs from make:module
├── lang/ru/install/index.php
├── default_option.php
├── lib/ # PSR-4, Vendor\Module\... (add manually)
├── views/ # PHP views for renderView() in controllers
├── routes/ # Module route files — require from /local/routes/web.php
├── .settings.php # controllers, services, console (add manually)
└── include.php # optional, for registerNamespace/registerAutoLoadClasses
Module Routing
Routing is global-only. The kernel loads route files listed in global routing.config from /local/routes/ and /bitrix/routes/ only.
A routing section in the module's .settings.php is not auto-loaded. Connect module routes by require from /local/routes/web.php:
// /local/routes/web.php
return function (\Bitrix\Main\Routing\RoutingConfigurator $routes): void {
$moduleRoutes = $_SERVER['DOCUMENT_ROOT'] . '/local/modules/vendor.module/routes/web.php';
if (is_file($moduleRoutes))
{
(require $moduleRoutes)($routes);
}
};
install/version.php
<?php
$arModuleVersion = [
'VERSION' => '1.0.0',
'VERSION_DATE' => '2026-04-16 12:00:00',
];
install/index.php
Inherit from CModule, implement DoInstall/DoUninstall. Base template:
<?php
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ModuleManager;
use Bitrix\Main\EventManager;
Loc::loadMessages(__FILE__);
final class vendor_module extends CModule
{
public $MODULE_ID = 'vendor.module';
public $MODULE_VERSION;
public $MODULE_VERSION_DATE;
public $MODULE_NAME;
public $MODULE_DESCRIPTION;
public $PARTNER_NAME = 'Vendor';
public $PARTNER_URI = 'https://vendor.example.com';
public function __construct()
{
$arModuleVersion = [];
include __DIR__ . '/version.php';
$this->MODULE_VERSION = $arModuleVersion['VERSION'] ?? '';
$this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'] ?? '';
$this->MODULE_NAME = (string)Loc::getMessage('VENDOR_MODULE_NAME');
$this->MODULE_DESCRIPTION = (string)Loc::getMessage('VENDOR_MODULE_DESCRIPTION');
}
public function DoInstall(): void
{
global $USER, $APPLICATION;
if (!$USER->IsAdmin())
{
$APPLICATION->ThrowException('Access denied');
return;
}
ModuleManager::registerModule($this->MODULE_ID);
$this->installDb();
$this->installEvents();
$this->installAgents();
$this->installFiles();
}
public function DoUninstall(): void
{
global $USER;
if (!$USER->IsAdmin()) return;
$this->uninstallAgents();
$this->uninstallEvents();
$this->uninstallDb();
$this->uninstallFiles();
ModuleManager::unRegisterModule($this->MODULE_ID);
}
private function installDb(): void
{
// Table creation via ORM Entity:
// \Vendor\Module\Model\PostTable::getEntity()->createDbTable();
}
private function uninstallDb(): void
{
// Application::getConnection()->dropTable(PostTable::getTableName());
}
private function installEvents(): void
{
EventManager::getInstance()->registerEventHandler(
fromModule: 'main',
eventType: 'OnAfterUserAdd',
toModuleId: $this->MODULE_ID,
toClass: \Vendor\Module\Internals\Integration\Main\EventHandler\OnAfterUserAddHandler::class,
toMethod: 'handle',
);
}
private function uninstallEvents(): void
{
EventManager::getInstance()->unRegisterEventHandler(
fromModule: 'main',
eventType: 'OnAfterUserAdd',
toModuleId: $this->MODULE_ID,
toClass: \Vendor\Module\Internals\Integration\Main\EventHandler\OnAfterUserAddHandler::class,
toMethod: 'handle',
);
}
private function installAgents(): void
{
\CAgent::AddAgent(
\Vendor\Module\Cli\Agent\QueueAgent::class . '::run();',
$this->MODULE_ID,
'N',
300,
'',
'Y',
'',
100,
);
}
private function uninstallAgents(): void
{
\CAgent::RemoveModuleAgents($this->MODULE_ID);
}
private function installFiles(): void
{
CopyDirFiles(
__DIR__ . '/components',
$_SERVER['DOCUMENT_ROOT'] . '/local/components',
true,
true,
);
}
private function uninstallFiles(): void
{
DeleteDirFilesEx('/local/components/vendor');
}
}
Language Files
/local/modules/vendor.module/lang/ru/install/index.php (created by make:module):
<?php
$MESS['VENDOR_MODULE_NAME'] = 'Vendor Module';
$MESS['VENDOR_MODULE_DESCRIPTION'] = 'Module description';
Add lang/en/ (and other locales) as needed for multi-language admin UI.
Lang file paths must mirror the source file path relative to the module root: /install/index.php → /lang/<code>/install/index.php, /admin/my_page.php → /lang/<code>/admin/my_page.php. MODULE_NAME / MODULE_DESCRIPTION are read from these phrases in the installer constructor and shown in Admin → Settings → Product settings → Modules; if the lang file path or phrase codes don't match, the module appears there with an empty name/description.
DB Tables
Do not use raw SQL for table creation. Describe the entity in /lib/Model/PostTable.php and create the table via ORM:
\Bitrix\Main\Loader::includeModule('vendor.module');
\Vendor\Module\Model\PostTable::getEntity()->createDbTable();
For deletion:
\Bitrix\Main\Application::getConnection()->dropTable(PostTable::getTableName());
Module Options (options.php)
Module options (Option + default_option.php) are for permanent settings. For TTL runtime state vs cache vs Option, see skill bitrix-storage.
If you need a settings page in Admin Panel (Settings → Module Settings → Vendor Module):
<?php
/** @var CMain $APPLICATION */
/** @var string $mid */ // module id
use Bitrix\Main\Config\Option;
use Bitrix\Main\Localization\Loc;
$options = [
['api_key', Loc::getMessage('VENDOR_API_KEY'), '', ['text', 40]],
['debug_mode', Loc::getMessage('VENDOR_DEBUG'), 'N', ['checkbox', 'Y']],
];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && check_bitrix_sessid())
{
foreach ($options as $opt)
{
$val = $_POST[$opt[0]] ?? $opt[2];
Option::set($mid, $opt[0], $val);
}
}
// ... display via CAdminTabControl
PSR-4 Autoloading
Nothing needs to be registered manually in include.php if:
- Module is in
/local/modules/vendor.module/. - Classes are in
/lib/. - Namespace follows
\Vendor\Module\...(or\Bitrix\Mymodule\...for a one-word id).
Bitrix Loader handles this automatically when includeModule is called.
Checklist
- Module identifier follows
vendor.moduleformat (or one-word →\Bitrix\...namespace). - After
make:module,.settings.php/lib/added if needed (generator is minimal). - Module routes are
required from/local/routes/web.php— not expected from module.settings.phprouting. -
DoInstall/DoUninstallare implemented and idempotent. - Event handlers and agents are registered upon installation and removed upon uninstallation.
- DB tables are managed via ORM or
SqlHelper(DDL). - Language files exist where needed (
lang/ru/from generator; addlang/en/etc.). - Services and controllers are registered in
.settings.php. - No hardcoded strings in
index.php(useLoc). - Module is compatible with PSR-4.
- Files are copied to
/local/, not/bitrix/.
Files included
- SKILL.md

