# Coding Conventions CLAUDE.md documents the conventions; actual code matches ~95%. Specific patterns: ## Naming - **Files:** `class.{ClassName}.php` (consistent across 40+ classes). - **Namespaces:** `\controls`, `\factory`, `\services`, `\view`. Root-namespace utilities live at `autoload/class.{Name}.php` (e.g. `\S`, `\Tpl`, `\DbModel`, `\Cache`, `\Chunk`, `\Cron`, `\Excel`, `\Html`). - Mapping enforced by `spl_autoload_register` in [index.php](index.php) lines 3-12. ## Methods - Heavy reliance on **static methods** in controllers, factories, services. ~99% of public API. - Static modifier ordering inconsistent (`static public` vs `public static`). ## Database access - Always via global `$mdb` (Medoo). Never `new` a connection. - Initialized once in [index.php](index.php) lines 24-31. - Raw SQL only for complex joins via `$mdb->query(':sql', [':param' => $value])->fetch()`. - **Exception:** [api.php](api.php) and [cron.php](cron.php) use RedBeanPHP (`\R::`). See [concerns.md](concerns.md) for the inconsistency. ## Reading params ```php $id = (int) \S::get('client_id'); ``` `\S::get($name)` reads POST first, then GET. Defined in [autoload/class.S.php](autoload/class.S.php) lines 198-217. **Violations found** (use `$_POST` / `$_GET` directly): - [autoload/controls/class.Api.php](autoload/controls/class.Api.php) line 47 — API token fallback - [autoload/controls/class.Products.php](autoload/controls/class.Products.php) — DataTables `$_POST['order'][0]['dir']`/`['name']` ## Response patterns ```php // AJAX echo json_encode([...]); exit; // Page return \Tpl::view('module/template', ['var' => $value]); ``` 100% consistent across controllers. ## Flash messages ```php \S::alert('Nazwa klienta jest wymagana.'); header('Location: /clients'); exit; ``` `\S::alert()` writes to `$_SESSION['alert']`; layout reads it. ## Currency formatting `\S::number_display($value)` → `"1 234,56 zł"` (Polish locale: space thousands separator, comma decimal). Defined at [autoload/class.S.php](autoload/class.S.php) lines 51-54. ## Localization - All UI strings in **Polish**. - Timezone fixed to `Europe/Warsaw` (`date_default_timezone_set` in [index.php](index.php) line 14, [ajax.php](ajax.php) line 13). - Polish slug helper: `\S::seo($val)`. ASCII transliteration: `\S::noPL($val)`. ## Code style - 2-space indent, no tabs. - K&R braces (open on same line). - No `declare(strict_types=1);` anywhere. - No type hints on signatures. - Explicit casting (`(int)`, `(string)`, `(float)`) used liberally for normalization. ## Comments - Density low (~5%). No `@param`/`@return` docblocks in production code. - Only [autoload/class.Chunk.php](autoload/class.Chunk.php) (vendored) has docblocks. - Section headers occasionally in Polish (e.g. `// --- Autoryzacja ---`). ## Error handling - Try/catch present in newer modules ([class.Clients.php](autoload/controls/class.Clients.php) lines 58-75, [class.Cron.php](autoload/controls/class.Cron.php)). - Catches generic `\Throwable`; no custom exception classes. - Errors persisted as DB settings (e.g. `google_ads_last_error`). - No central logger; ad-hoc `error_log` and DB `logs` table. - Global `error_reporting` suppresses `E_NOTICE | E_STRICT | E_WARNING` (set in [ajax.php](ajax.php) line 1). ## Templates - Data injected via `\Tpl::view('module/file', ['k' => $v])`. - Inside templates: `k ?>` (magic `__get`). - Output buffering wraps each `include`. - User overrides: `templates_user/` is searched before `templates/`. ## Adding a new module 1. Create `autoload/controls/class.{Module}.php` (namespace `\controls`). 2. Create `autoload/factory/class.{Module}.php` (namespace `\factory`). 3. Optionally create `autoload/view/class.{Module}.php` (namespace `\view`). 4. Create `templates/{module}/main_view.php` and other actions. 5. Add to `$route_aliases` in [index.php](index.php) if clean URL needed. 6. Add sidebar link in [templates/site/layout-logged.php](templates/site/layout-logged.php).