Repository navigation
04 Architecture
Для тех, кто хочет понять код, доработать его или написать свой драйвер/канал.
- Без фреймворков и runtime-зависимостей. Ни composer-пакетов, ни CDN: только PHP 8.2+ и PDO. Ядро — ~20 файлов, которые можно прочитать за вечер. Это осознанно: приложение управляет воротами дома и должно заводиться на любом хостинге с PHP и переживать 3 часа ночи без black-box магии.
- Прогрессивное улучшение. Каждая страница работает без JavaScript (обычные формы), JS добавляет диалоги, AJAX, палитру команд, графики, QR.
-
Деньги — целыми копейками. Суммы хранятся и считаются в минимальных
единицах (
Money::parse/toFloat/split), расщепление без потерь, округление документируемое. - Всё пишется в журнал. Открывания, аудит действий, входы, устройства, уведомления — отдельные каналы логов с request-id.
-
Один контейнер — одна точка сборки.
src/bootstrap.phpсвязывает сервисы; все точки входа (web, CLI, тесты) используют его.
├── bin/bas-ip # консольный вход (Kernel + 71 команда)
├── config/
│ ├── config.php # все значения по умолчанию (документированы)
│ └── routes.php # единая таблица маршрутов с middleware-опциями
├── database/
│ ├── migrations/ # 5 файлов схемы (переносимы между СУБД)
│ └── seeds/demo.php # демо-данные (24 жителя, машины, счета, события)
├── public/ # ДОКУМЕНТ-РУТ
│ ├── index.php # front controller
│ ├── router.php # для php -S
│ ├── bas-ip.php, bas_ip.php, get_last_image.php # legacy v1 (форвардеры)
│ └── assets/ # собранные css/js/шрифты/иконки (локально)
├── resources/
│ ├── css/ # исходники: app.css (вход), tokens (shadcn),
│ │ # components (примитивы), fonts (woff2)
│ ├── views/ # PHP-шаблоны: layouts, admin, cabinet, guest,
│ │ # auth, help, errors, components
│ └── help/help.php # встроенная справка (36 разделов, данные)
├── src/BasIp/
│ ├── Core/ # App, Container, Router, Request, Response,
│ │ # Session, Csrf, Crypto, Secrets, Logger,
│ │ # RateLimiter, Cache, Clock, Config, View,
│ │ # Lang, HttpClient(+Response), Events
│ ├── Db/ # Database (PDO+диалекты), Query builder,
│ │ # Schema/Blueprint, Migrator, Expression
│ ├── Auth/ # Guard, User, AuthService, Permissions,
│ │ # ApiPrincipal, Totp
│ ├── Http/ # Controller, RoutingException,
│ │ ├── Controllers/ # Admin\*, Api\*, Auth, Cabinet, Guest,
│ │ │ # Panel, Dashboard, Help, Install, System
│ │ └── Middleware/ # TrustProxies, SecurityHeaders, Maintenance,
│ │ # IpAllowlist, StartSession, VerifyCsrf,
│ │ # RateLimit, Authenticate, Authorize
│ ├── Domain/
│ │ ├── Model/ # Entity, Resident, Contact, Car, Charge,
│ │ │ # Invoice, Payment, GuestPass, GateEvent, Device
│ │ └── Repository/ # ResidentRepository (гидратация, batch-relations)
│ ├── Services/ # Resident, Billing, Gate, GuestPass, Notify-
│ │ # смежные, Import/Export, Backup, Settings,
│ │ # Statistics, Audit, ApiToken, Announcement,
│ │ # User, Data
│ ├── Notify/ # NotificationService, QueueWorker, Message,
│ │ └── Channel/ # TemplateRenderer, SmtpClient + log/mail/
│ │ # telegram/sms/whatsapp/webhook
│ ├── Device/ # DeviceManager, GateDriver (интерфейс),
│ │ └── Driver/ # BasIpDriver, HttpRelayDriver, DryRunDriver
│ ├── Update/ # UpdateService, Release
│ ├── Console/ # Kernel, Command, Input, Output + commands/
│ ├── Support/ # Arr, Str, Money, Phone, Plate, Validator,
│ │ # Paginator, Markdown, Ip, Fs, Env
│ ├── autoload.php # PSR-4 автозагрузчик (без composer)
│ └── bootstrap.php # сборка контейнера
├── storage/ # runtime (вне git): logs, app/{backups,exports,
│ # queue,tokens,cache,updates}, framework/{sessions,views}
├── tests/ # Feature + Unit + Support (TestApp/TestCase),
│ # run.php — собственный раннер
├── tools/ # smoke.php, migrate-smoke.php, build-*.mjs
└── wiki/ # эта документация
public/index.php
└─ Config::boot(root) # config.php ← .bas-ip.config.local.php ← .env
└─ App::boot(bootstrap.php) # контейнер: все биндинги ленивые
└─ App::run()
├─ Request::capture()
└─ App::handle(Request)
├─ сброс request-scoped состояния (Guard::reset, auth.*/route)
├─ Session::start + ageFlash, access-логгер с request-id
├─ Router::dispatch(method, path) # 404/405 → RoutingException
├─ MiddlewarePipeline (луковица):
│ TrustProxies → SecurityHeaders → MaintenanceMode →
│ IpAllowlist → StartSession → VerifyCsrf → RateLimit →
│ Authenticate → Authorize → [контроллер]
├─ callAction(handler) # контроллер из контейнера
└─ Response
└─ finish(): debug-заголовки, session touch, логгер close
└─ Response::emit()
Middleware читают опции маршрута из config/routes.php:
auth (true — сессия, 'api' — Bearer-токен), roles, permission,
permissions (ALL), any (ANY), scopes (ANY-OF для API-токенов),
throttle (имя пресета или 'N,секунд'), csrf_except, name.
Ошибки рендерит App::renderThrowable: для HTML — шаблоны errors/*
(400/401/403/404/405/419/422/429/500/503), для JSON — стабильная форма
{ok:false, error, status, request_id} (+ стектрейс только при debug).
Базовые security-заголовки добавляются и на error-пути.
Core\Container — ~150 строк: bind/factory/set/get/extend/forget,
ленивое разрешение с автовайрингом конструкторов по типам. Синглтоны:
Config, Database, Session, Guard, сервисы. Request-scoped ключи
(auth.user, api.principal, cspNonce, route) сбрасываются в начале
каждого handle() — ядро ре-ентерабельно (тесты, очереди, демоны).
-
Database— обёртка PDO: ленивое соединение,reload()(смена конфига на лету — нужно установщику), транзакции с вложенностью (savepoints), статистика запросов, диалектные хелперы (isMySql/isPgsql,quoteIdentifier, upsert-синтаксис). -
Query— билдер: select/join/where-группы/orderBy/limit/offset,insertGetId,insertBatch,value,pluck, пагинация; только prepared statements.Expression/SubQueryдля сырых фрагментов. -
Schema/Blueprint— переносимые миграции: типы колонок маппятся в диалект (MySQL/PostgreSQL/SQLite), индексы, внешние ключи с cascade. -
Migrator— журналmigrations, up/down,--step, fresh/rollback/status.
Схема (5 миграций): residents, contacts, cars, users, sessions_meta,
login_attempts, charges, invoices, payments, billing_history,
gate_events, guest_passes, devices, access_schedules, announcements,
api_tokens, outbox, notification_batches, audit_log, settings,
app_state, backups, import_jobs, export_jobs.
Модели — лёгкие сущности (fromRow/toArray, доменные методы:
Resident::shortName(), Invoice::isOverdue(), User::can() через
Permissions). Логика — в сервисах, контроллеры тонкие:
| Сервис | Зона ответственности |
|---|---|
ResidentService |
CRUD жителей, контакты, машины, флаги, поиск, история |
BillingService |
preview/charge/recalc/invoices/payments(FIFO)/balances/debtors/reminders |
GateService |
открытие/закрытие, журнал событий, снимки, статистика, cooldown |
GuestPassService |
выпуск/использование/отзыв пропусков, коды, подписи, PIN |
ImportExportService |
инспекция, маппинг, upsert-импорт, экспорт CSV/JSON |
BackupService |
дампы БД (mysqldump/pg_dump/sqlite), zip, ротация, восстановление |
NotificationService + QueueWorker
|
resolve получателей, очередь (outbox), ретраи, батчи |
AuditService |
журнал действий с актёром, субъектом и meta |
SettingsService |
key-value поверх таблицы settings (приоритет над конфигом) |
ApiTokenService |
токены с областями, хэширование, срок, отзыв |
UpdateService |
проверка GitHub, скачивание, checksum, установка, снапшоты, откат |
StatisticsService |
сводки для дашбордов |
Интерфейс GateDriver: open/close/status/reboot/snapshot/sip… +
supports(capability). Реализации:
-
BasIpDriver— Camdroid/api/v0: логин → токен устройства (TTL, авто-рефреш, хранение в БД/файле/кэше), открытие замка по каналу, снимки камеры, SIP, перезагрузка; таймауты/ретраи из конфига. -
HttpRelayDriver— произвольный HTTP-контроллер: URL/метод/тело настраиваются, для DIY-реле и сторонних шлюзов. -
DryRunDriver— эмулятор: всё «открывается», пишет события; для разработки, демо и тестов.
DeviceManager маршрутизирует между несколькими устройствами (device.gates),
кеширует статусы, пишет канал device в лог.
Message (immutable fluent) → NotificationService::resolve() (каналы →
контакты жителя, fallback на денормализованные email/phone) → таблица
outbox → QueueWorker (claim с lease, ретраи с экспоненциальной задержкой,
статусы, батчи) → Channel-ы. TemplateRenderer — подстановки с
модификаторами и удалением «повисших» строк-подписей. SMTP-клиент собственный
(SmtpClient: STARTTLS/AUTH/plain).
Core\View — PHP-шаблоны с layout-ами, экранированием e(), компонентами
(include('components/…')), хелперами: url(), asset() (cache-busting по
mtime), icon() (SVG-спрайт Lucide), csrf(), flashPayload(), setting(),
Markdown-рендер для справки. Компиляция не нужна; кэш видов опционален.
-
Tailwind CSS 3 (JIT, локальный CLI) + дизайн-токены shadcn/ui
(HSL-переменные light/dark в
tokens.css) + hand-ported shadcn-примитивы (components.css: btn, card, input, dialog, sheet, dropdown, tabs, toast, badge, table…). -
theme.js (инлайннится в
<head>до отрисовки): порядок выбора темы — сохранённый выбор пользователя → localStoragebasip:theme→prefers-color-scheme→ light; переключатель в шапке, системная тема отслеживается live. - app.js/admin.js/panel.js/guest.js — vanilla ES2020: диалоги, тосты, AJAX-формы, командная палитра (Ctrl+K), поллинг статусов, графики, QR (vendored qrcode-generator с фиксом transpose-бага), countdown, copy-to-clipboard, confirmation-диалоги.
-
Сборка (
npm run build):tools/build-js.mjs(проверка синтаксиса + вендоринг QR с патчем),tools/build-icons.mjs(сканируетicon('…')по всему коду → спрайт из lucide-static + манифест; падает на неизвестном имени), tailwind CLI →public/assets/css/app.css.tools/vendor-fonts.mjs— woff2 из @fontsource (Inter, JetBrains Mono; latin+cyrillic) со сверкой сfonts.css.
Собственный раннер tests/run.php (без PHPUnit): классы в tests/Unit и
tests/Feature, TestCase с ассертами, TestApp — поднимает приложение на
временной SQLite (или MySQL/PostgreSQL через BASIP_TEST_DB), мигрирует,
даёт фикстуры (makeAdmin/makeResident) и диспетчер запросов через полный
стек (TestApp::request). Покрытие: деньги (до копейки), права, CSRF,
заголовки, гостевые пропуска, импорт/экспорт, TOTP, обновления, support-утилиты.
tools/smoke.php — end-to-end по всем значимым маршрутам (68 проверок)
на любой из трёх СУБД.
- Точка входа:
public/index.php→src/bootstrap.php→Core/App.php. - Маршруты и права:
config/routes.php(одна таблица — вся карта приложения). - Деньги:
Services/BillingService.php+Support/Money.php+tests/Feature/BillingTest.php. - Открытие ворот:
Services/GateService.php+Device/Driver/BasIpDriver.php. - Безопасность:
Http/Middleware/*+Auth/*.
Дальше: 05 · Гид администратора