| 1 | # PHP — данные и персистентность (PDO и обход без ORM) |
| 2 | |
| 3 | **Назначение:** фундамент **работы с БД из PHP** без привязки к Laravel/Eloquent: PDO, подготовленные запросы, транзакции, типичные ошибки. Мир **`software.php`**. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ### 1. Почему PDO как дефолт |
| 8 | |
| 9 | - **Fact:** **PDO** — унифицированный слой с драйверами (`mysql`, `pgsql`, `sqlite`, …); единый стиль prepared statements; исключения в режиме `ERRMODE_EXCEPTION`. |
| 10 | - **Heuristic:** новый код без тяжёлого ORM — PDO + тонкий репозиторий; mysqli допустим, но два API в одном проекте — только с обоснованием. |
| 11 | |
| 12 | --- |
| 13 | |
| 14 | ### 2. Подключение и DSN |
| 15 | |
| 16 | - **Fact:** DSN строка зависит от драйвера; учётные данные не хардкодить — env/config вне VCS. |
| 17 | - **Heuristic:** для MySQL 8 — явно charset `utf8mb4` в DSN; timezone — задавать на соединении или в сервере согласованно. |
| 18 | |
| 19 | --- |
| 20 | |
| 21 | ### 3. Подготовленные запросы и плейсхолдеры |
| 22 | |
| 23 | - **Fact:** именованные и позиционные плейсхолдеры; для `IN (...)` — собирать плейсхолдеры динамически или использовать альтернативы (временные таблицы) — не конкатенировать сырые значения. |
| 24 | - **Heuristic:** никогда не интерполировать пользовательские данные в SQL; идентификаторы таблиц/колонок — allowlist, не параметры prepared statement. |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ### 4. Эмуляция prepared statements |
| 29 | |
| 30 | - **Fact:** PDO может эмулировать prepares на клиенте (`ATTR_EMULATE_PREPARES`); для MySQL влияет на повторное использование планов и экранирование. |
| 31 | - **Heuristic:** для прод-MySQL часто отключают эмуляцию (`false`), если нет причин иначе — проверить поведение лимитов/типов. |
| 32 | |
| 33 | --- |
| 34 | |
| 35 | ### 5. Транзакции и изоляция |
| 36 | |
| 37 | - **Fact:** `beginTransaction` / `commit` / `rollBack`; уровень изоляции задаётся SQL `SET TRANSACTION` (зависит от СУБД). |
| 38 | - **Heuristic:** бизнес-операция «всё или ничего» — одна транзакция на use-case; не держать транзакции открытыми на время HTTP long poll. |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ### 6. Deadlock и retry |
| 43 | |
| 44 | - **Fact:** при deadlock СУБД откатывает одну транзакцию — приложение должно уметь **retry** идемпотентной операции. |
| 45 | - **Heuristic:** логировать код ошибки; ограничить число повторов с backoff. |
| 46 | |
| 47 | --- |
| 48 | |
| 49 | ### 7. Потоковое чтение и память |
| 50 | |
| 51 | - **Fact:** `fetch` в цикле vs `fetchAll` — последний раздувает память на больших выборках. |
| 52 | - **Heuristic:** отчёты и миграции данных — итерация с лимитом/ключом или курсоры (если поддерживаются). |
| 53 | |
| 54 | --- |
| 55 | |
| 56 | ### 8. N+1 на уровне ручных запросов |
| 57 | |
| 58 | - **Fact:** без ORM N+1 всё равно возможен: цикл по списку ID с запросом внутри. |
| 59 | - **Heuristic:** собрать ID → один запрос `WHERE id IN (...)` + карта в памяти; или JOIN с осознанием дублирования строк. |
| 60 | |
| 61 | --- |
| 62 | |
| 63 | ### 9. Миграции схемы без фреймворка |
| 64 | |
| 65 | - **Fact:** инструменты: **Phinx**, **Doctrine Migrations**, самописные SQL с таблицей версий. |
| 66 | - **Heuristic:** миграции идемпотентны в рамках версии; никогда не править уже применённую миграцию в истории — новая миграция. |
| 67 | |
| 68 | --- |
| 69 | |
| 70 | ### 10. SQLite и продакшен |
| 71 | |
| 72 | - **Fact:** SQLite удобен для тестов/edge; в высоконагруженном concurrent write — ограничения. |
| 73 | - **Heuristic:** явно документировать, если прод на SQLite; иначе считать dev-only по умолчанию для web-scale записи. |
| 74 | |
| 75 | --- |
| 76 | |
| 77 | ## Registry card (template-knowledge-card-v1) |
| 78 | |
| 79 | ### Provenance |
| 80 | - source_refs: `https://www.php.net/manual/en/book.pdo.php`; KB 2026-03-01. |
| 81 | - created_at: 2026-03-01 |
| 82 | - updated_at: 2026-03-01 |
| 83 | |
| 84 | ### Metadata |
| 85 | - card_id: KC-2026-03-01-PHP-DB-L1 |
| 86 | - world: software.php |
| 87 | - layer: world |
| 88 | - tags: php; pdo; sql; transactions; migrations |
| 89 | - status: active |
| 90 | |
| 91 | ### Epistemic Linkage |
| 92 | - epistemic_basis: fact + inference |
| 93 | - confidence: high |
| 94 | - transfer_boundary: тюнинг конкретной СУБД — вендорная документация |
| 95 | |
| 96 | ### Core Unit |
| 97 | - context: сырой SQL из PHP, легаси скрипты, микросервис без ORM |
| 98 | - signal: SQL injection, утечки соединений, deadlock |
| 99 | - action: prepared statements + транзакции + профилирование запросов |
| 100 | - lesson: PDO не отменяет дисциплину SQL |
| 101 | |
| 102 | ### Operationalization |
| 103 | - first_adoption_task: ERRMODE_EXCEPTION + единый слой репозитория |
| 104 | - validation_check: статический поиск конкатенации в SQL |
| 105 | - success_criterion: нет сырого пользовательского ввода в SQL |
| 106 | |
| 107 | ### Lifecycle |
| 108 | - supersedes: — |
| 109 | - superseded_by: — |
| 110 | - deprecation_reason: — |
| 111 | |