From edcaadc09c095e7c1c4592985149c2069ad37660 Mon Sep 17 00:00:00 2001 From: "v.razuvaev" Date: Thu, 16 Jul 2026 22:03:23 +0300 Subject: [PATCH 1/3] docs: add Russian README --- README.md | 2 ++ README.ru.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 README.ru.md diff --git a/README.md b/README.md index 0d9b774..cc82dce 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ [![PHP](https://img.shields.io/packagist/dependency-v/rasuvaeff/rector-datetime-immutable/php)](https://packagist.org/packages/rasuvaeff/rector-datetime-immutable) [![License](https://img.shields.io/packagist/l/rasuvaeff/rector-datetime-immutable.svg)](LICENSE.md) +[Русская версия](README.ru.md) + [Rector](https://getrector.com) rules that migrate mutable `DateTime` to `DateTimeImmutable` — and **auto-fix the lost mutations** the migration creates, the classic silent bug where `$date->modify('+1 day');` throws the diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..344f699 --- /dev/null +++ b/README.ru.md @@ -0,0 +1,55 @@ +# rasuvaeff/rector-datetime-immutable + +[English version](README.md) + +Rector rules и CLI для безопасной миграции mutable `DateTime` на +`DateTimeImmutable`, включая repair потерянных результатов mutator calls. + +## Требования + +PHP 8.3 - 8.5 и Rector 2.x; точные constraints приведены в `composer.json`. + +## Установка + +```bash +composer require --dev rasuvaeff/rector-datetime-immutable +``` + +## Использование + +```bash +vendor/bin/rector-datetime-immutable process src +``` + +Команда сначала запускает preflight boundaries, затем миграцию и confirmation +pass. Повторяйте command, пока Rector не перестанет менять files: lost mutation, +созданная migration, видна только на следующем run. `--dry-run` работает в +temporary workspace и не меняет исходники; `--format=human|github|json` задаёт +вывод для CI. + +`MutableDateTimeBoundaryRector` report-only находит native, vendor, inherited, +interface и abstract contracts с mutable `DateTime`. `DateTimeImmutableRector` +мигрирует constructions, concrete typehints, properties и docblock tags. +`LostDateTimeMutationRector` в `fix` mode добавляет assignment, а в `report` +mode оставляет marker. `--doctrine-columns` включает согласованную migration +attributes Doctrine columns. + +## Безопасность + +Rules намеренно пропускают uncertain dispatch, mutable contracts, open base +types и отмеченные `@mutable-datetime` declarations. Проверяйте diff и tests +целевого приложения до применения migration. + +## Примеры + +Подробный workflow и все option constants: [README.md](README.md). + +## Разработка + +```bash +docker run --rm -v "$PWD":/app -w /app composer:2 composer build +``` + +## Лицензия + +BSD-3-Clause. См. [LICENSE.md](LICENSE.md). From fc0f4e5e7838f137e52da5afadf871c030cfc4f6 Mon Sep 17 00:00:00 2001 From: "v.razuvaev" Date: Thu, 16 Jul 2026 22:12:00 +0300 Subject: [PATCH 2/3] docs: complete Russian README translation --- README.ru.md | 412 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 383 insertions(+), 29 deletions(-) diff --git a/README.ru.md b/README.ru.md index 344f699..4f5438c 100644 --- a/README.ru.md +++ b/README.ru.md @@ -1,55 +1,409 @@ # rasuvaeff/rector-datetime-immutable - +[![Stable Version](https://img.shields.io/packagist/v/rasuvaeff/rector-datetime-immutable.svg)](https://packagist.org/packages/rasuvaeff/rector-datetime-immutable) +[![Total Downloads](https://img.shields.io/packagist/dt/rasuvaeff/rector-datetime-immutable.svg)](https://packagist.org/packages/rasuvaeff/rector-datetime-immutable) +[![Build](https://img.shields.io/github/actions/workflow/status/rasuvaeff/rector-datetime-immutable/build.yml?branch=master)](https://github.com/rasuvaeff/rector-datetime-immutable/actions) +[![Static Analysis](https://img.shields.io/github/actions/workflow/status/rasuvaeff/rector-datetime-immutable/static-analysis.yml?branch=master)](https://github.com/rasuvaeff/rector-datetime-immutable/actions) +[![Psalm Level](https://img.shields.io/badge/Psalm-level%201-brightgreen.svg)](psalm.xml) +[![PHP](https://img.shields.io/packagist/dependency-v/rasuvaeff/rector-datetime-immutable/php)](https://packagist.org/packages/rasuvaeff/rector-datetime-immutable) +[![License](https://img.shields.io/packagist/l/rasuvaeff/rector-datetime-immutable.svg)](LICENSE.md) [English version](README.md) -Rector rules и CLI для безопасной миграции mutable `DateTime` на -`DateTimeImmutable`, включая repair потерянных результатов mutator calls. +[Rector](https://getrector.com) rules that migrate mutable `DateTime` to +`DateTimeImmutable` — и **автоматическое исправление потерянных мутаций**, создаваемых миграцией +, классическая тихая ошибка, при которой `$date->modify('+1 day');` выбрасывает новый экземпляр +: -## Требования +```php +// before — mutable construction, in-place mutation +$deadline = new \DateTime('2026-01-01'); +$deadline->modify('+1 month'); -PHP 8.3 - 8.5 и Rector 2.x; точные constraints приведены в `composer.json`. +// after (both rules) — immutable, and the mutation result is kept +$deadline = new \DateTimeImmutable('2026-01-01'); +$deadline = $deadline->modify('+1 month'); +``` +PHPStan (уровень 4) и Psalm *report* игнорировали результаты мутатора DateTimeImmutable +; этот пакет — это часть, которая **исправляет их массово** во время миграции +. + + > Используете помощника по программированию с искусственным интеллектом? [llms.txt](llms.txt) имеет компактную ссылку + > которую можно передать в качестве контекста. @@ЛИНИЯ@@ +## ТЛ;ДР +Два способа выполнить миграцию: + + | Путь | Как | + |---|---| + | **Оболочка CLI** (рекомендуется) | `vendor/bin/rector-datetime-immutable src` — граничная предполетная проверка, миграция к конвергенции и проход диагностики в одной команде; см. [Миграция одной командой](#one-command-migration) | + | **Руководство `rector.php`** | зарегистрировать правила самостоятельно; см. [Ручная настройка Rector](#manual-rector-setup) | + + **Предупреждение при ручной настройке:** один запуск Rector не может одновременно выполнить миграцию и восстановление — запускайте + `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений** (обычно дважды), + в противном случае потерянные мутации, созданные при первом проходе, останутся в коде. Обертка + сделает это за вас. @@ЛИНИЯ@@ +## Оглавление +- [Требования](#requirements) + - [Установка](#install) + - [Использование](#использование) + - [Миграция одной командой](#one-command-migration) + - [Предварительный просмотр](#dry-run-preview) + - [Выход CI](#ci-output) + - [Разрешение результатов предполетной проверки](#resolve-preflight-findings) + - [Совместная миграция столбцов Doctrine](#doctrine-columns-co-migration) + - [Ручная настройка Rector](#manual-rector-setup) + - [`MutableDateTimeBoundaryRector`](#mutabledatetimeboundaryrector) + - [`DateTimeImmutableRector`](#datetimeimmutablerector) + - [`LostDateTimeMutationRector`](#lostdatetimemutationrector) + - [Маркеры](#маркеры) + - [Безопасность](#security) + - [Примеры](#examples) + - [Разработка](#development) + - [Лицензия](#лицензия) -## Установка +## Требования +- PHP 8.3 - 8.5 для запуска правил + - `rector/rector` ^2.5 + - `webmozart/assert` ^1.11 || ^2.0 + - `proc_open` включен при использовании оболочки конвергенции — доступен в сборке PHP + по умолчанию, если хост не отключит его через `disable_functions` +## Установка ```bash composer require --dev rasuvaeff/rector-datetime-immutable ``` - ## Использование +### Миграция одной командой +Установленный двоичный файл Composer сначала запускает предварительную проверку изменяемой границы только для чтения, + многократно применяет миграцию по умолчанию до полного прохождения подтверждения, затем + запускает `LostDateTimeMutationRector` в `MODE_REPORT` без изменения файлов: ```bash -vendor/bin/rector-datetime-immutable process src +vendor/bin/rector-datetime-immutable src ``` +Команда редактирует выбранные пути. Сначала выполните или спрячьте несвязанную работу. + Типичный вывод: -Команда сначала запускает preflight boundaries, затем миграцию и confirmation -pass. Повторяйте command, пока Rector не перестанет менять files: lost mutation, -созданная migration, видна только на следующем run. `--dry-run` работает в -temporary workspace и не меняет исходники; `--format=human|github|json` задаёт -вывод для CI. +```text +Preflight: no mutable DateTime boundaries found. +Migration pass 1: 12 changed file(s). +Migration pass 2: 4 changed file(s). +Migration pass 3: 0 changed file(s). +Converged after 2 change-producing pass(es). +Diagnostic pass: no manual review cases found. +Summary: 14 file(s) changed across 2 change-producing pass(es); 0 manual review case(s). +``` +Если предварительная проверка обнаруживает собственный, унаследованный, абстрактный/интерфейсный или вызываемый поставщиком объект +, параметр которого принимает DateTime, но отклоняет DateTimeImmutable, или параметр метода +, который передает свойство, которое миграция сохраняет как изменяемое, он печатает записи + `file:line` плюс подсказку по разрешению для каждой категории поиска, завершает работу с кодом + `2` и не изменяет файлы. Тот же выход используется после конвергенции, когда отчет + о потерянной мутации обнаруживает случай, который не может быть назначен безопасно. + + | Выход | Значение | + |---|---| + | `0` | миграция совпала, и дел, выполняемых вручную, не осталось | + | `1` | Ошибка ректора/процесса/JSON | + | `2` | предварительная блокированная миграция или ручная проверка после миграции остается | + | `3` | миграция не сошлась в пределах пропуска | + | `64` | неверные аргументы оболочки | + + Полезные опции: -`MutableDateTimeBoundaryRector` report-only находит native, vendor, inherited, -interface и abstract contracts с mutable `DateTime`. `DateTimeImmutableRector` -мигрирует constructions, concrete typehints, properties и docblock tags. -`LostDateTimeMutationRector` в `fix` mode добавляет assignment, а в `report` -mode оставляет marker. `--doctrine-columns` включает согласованную migration -attributes Doctrine columns. +```bash +vendor/bin/rector-datetime-immutable --dry-run src # full preview, no writes +vendor/bin/rector-datetime-immutable --acknowledge-boundaries src +vendor/bin/rector-datetime-immutable --doctrine-columns src # co-migrate ORM columns +vendor/bin/rector-datetime-immutable --format=github src # or --format=json +vendor/bin/rector-datetime-immutable --max-passes=8 src tests +vendor/bin/rector-datetime-immutable --no-report src +vendor/bin/rector-datetime-immutable \ + --preflight-config=rector-preflight.php \ + --config=rector-migration.php \ + --report-config=rector-report.php \ + src +``` +Упакованные значения по умолчанию: `config/preflight.php`, `config/migration.php` и +`config/report.php`. Используйте пользовательские конфигурации для пропусков, специфичных для проекта, поэтапные параметры + или `ALLOW_SUBCLASS`. @@ЛИНИЯ@@ +### Предварительный просмотр пробного прогона +`--dry-run` копирует пути во временное рабочее пространство, запускает там весь поток + — предполетную проверку, конвергенцию, диагностический проход — печатает все потенциальные различия + с путями, сопоставленными с оригиналами, и не изменяет ни одного файла проекта. Коды выхода + сохраняют свое значение, поэтому предварительный просмотр также сообщает вам, чем закончится реальный запуск +. Объявления вне скопированных путей (классы поставщиков, родительские каталоги +, которые вы не передали) по-прежнему считываются из исходных файлов; прогон записи + остается авторитетным. @@ЛИНИЯ@@ +### выход CI +`--format=github` сохраняет результаты, полученные человеком, и дополнительно выдает + `::error file=…,line=…::…` аннотации рабочего процесса для предполетных блокировщиков и + `::warning …` для случаев проверки вручную, поэтому PR миграции отображает каждое обнаружение + в строке. + + `--format=json` подавляет повествование и печатает один машиночитаемый объект + на стандартный вывод: `status` (`clean`, `blocked`, `manual-review`, + `not-converged`, `acknowledged`), `exitCode`, `passes` для каждого прохода`, + `changedFiles` и Результаты `preflight`/`manualReview`/`acknowledged` как + `{файл, строка, сообщение, категория}`, где `category` — это одно из + `requires-datetime`, `feeds-mutable-property`, `lost-mutation`, `iagnostic`. + При `--dry-run` объект также содержит потенциальные `diffs`. @@ЛИНИЯ@@ +### Решение предполетных предполетных выводов +| Нахождение | Разрешение | + |---|---| + | `параметр $x передает изменяемое свойство $y` | отметьте включающий метод `@mutable-datetime` — его подпись и связанные аргументы места вызова остаются изменяемыми — выполните совместную миграцию столбцов ORM с помощью `--doctrine-columns` или сначала перенесите контракт хранения | + | `параметр $x требует DateTime` | перепишите вызов в API, безопасный для DateTimeImmutable, или просмотрите поток и подтвердите его | + + `@mutable-datetime` в методе **calling** не заглушает поиск + `requires DateTime`: маркер сохраняет собственный контракт этого метода, +, в то время как поиск указывает на вызываемый собственный/вендорный/унаследованный параметр. Сама миграция + сохраняет изменяемые значения, связанные с таким вызываемым объектом с помощью простых + назначений, поэтому после проверки потока подтвердите это: -## Безопасность +```bash +vendor/bin/rector-datetime-immutable --acknowledge-boundaries src +``` +При этом над каждым вызовом границы пишется самодокументируемый комментарий и повторно выполняется + предполетная проверка: -Rules намеренно пропускают uncertain dispatch, mutable contracts, open base -types и отмеченные `@mutable-datetime` declarations. Проверяйте diff и tests -целевого приложения до применения migration. +```php +// @mutable-datetime-boundary: parameter $object requires DateTime +date_modify($moment, '+1 hour'); +``` +Оператор, содержащий `@mutable-datetime-boundary`, пропускается во всех дальнейших предварительных проверках + — проверка живет в коде и выдерживает повторные запуски. Находки + типа `feeds mutable property` **никогда** не подтверждаются автоматически: если отключить + их, это позволит миграции нарушить назначение свойств во время выполнения, поэтому + они сохраняют свои собственные разрешения, указанные выше. + + Пропуск на уровне файла через пользовательскую предполетную конфигурацию остается доступным как + грубая альтернатива: -## Примеры +```php +// rector-preflight.php +withRules([ + MutableDateTimeBoundaryRector::class, + ]) + ->withSkip([ + MutableDateTimeBoundaryRector::class => [ + __DIR__ . '/src/Legacy/SdkAdapter.php', + ], + ]); +``` ```bash -docker run --rm -v "$PWD":/app -w /app composer:2 composer build +vendor/bin/rector-datetime-immutable --preflight-config=rector-preflight.php src ``` +После сходимости тот же выход `2` сообщает об утраченных мутациях, которые режим исправления не может безопасно назначить + — разрешите их, назначив результат мутатора самостоятельно + (`$date = $date->modify(...)`). @@ЛИНИЯ@@ +### Совместная миграция столбцов доктрины +По умолчанию элементы, сопоставленные с ORM, сохраняются. `--doctrine-columns` (опция + `DOCTRINE_COLUMNS` в обоих правилах) выбирает совместную миграцию столбцов с сопоставлением атрибутов +: свойство, его методы доступа и подключенные параметры конструктора + мигрируют вместе с сопоставлением, которое перемещается в собственный неизменяемый вариант DBAL + — та же схема базы данных, неизменяемая гидратация. @@ЛИНИЯ@@ +```php +#[ORM\Column(type: 'datetime')] // → type: 'datetime_immutable' +private \DateTime $expiresAt; // → private \DateTimeImmutable $expiresAt; -## Лицензия +#[ORM\Column(type: Types::DATETIME_MUTABLE)] // → Types::DATETIME_IMMUTABLE +#[ORM\Column] // no type: Doctrine infers it from the PHP type +``` +Покрытые сопоставления: `datetime`, `date`, `time`, `datetimetz` как строковые литералы + или соответствующие константы `Types::*_MUTABLE`, а также столбцы без аргумента `type` +. Строки пользовательских типов, выражения динамического типа, аргументы позиционного атрибута + и аннотации docblock `@ORM\Column` остаются сохраненными. Требуется + `doctrine/dbal` ≥ 2,6 (собственные типы `*_immutable`). Просмотрите код жизненного цикла +, в котором были изменены даты объекта — диагностический проход сообщает об этом как об утраченных мутациях +. @@ЛИНИЯ@@ +### Ручная настройка ректора +```php +// rector.php +withPaths([__DIR__ . '/src']) + ->withRules([ + DateTimeImmutableRector::class, + LostDateTimeMutationRector::class, + ]); +``` +Без оболочки запускайте `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений +** (обычно дважды): в течение одного прогона вывод типа по-прежнему видит типы + до миграции, поэтому потерянные мутации, созданные конструкционной миграцией +, становятся видимыми при *следующем* прогоне. + + Запустите MutableDateTimeBoundaryRector отдельно с помощью --dry-run перед ручной миграцией +. Не объединяйте его с правилами миграции: его комментарии являются диагностическими + маркерами различий, а не изменениями исходного кода, которые необходимо зафиксировать. @@ЛИНИЯ@@ +### `MutableDateTimeBoundaryRector` +Сообщает об аргументах, поступающих в стабильные вызываемые объекты, объявленный параметр + которых принимает DateTime, но отклоняет DateTimeImmutable. Стабильные вызываемые объекты — это встроенные + функции PHP, функции/методы поставщиков, интерфейсные или абстрактные методы, а также методы +, ограниченные предком или помеченные `@mutable-datetime`. Другие локальные + конкретные вызываемые объекты не сообщаются, поскольку их объявления мигрируют вместе + с их сайтами вызовов. + + Анализ поддерживает позиционные, именованные и переменные аргументы в функциях +, экземплярных/статических методах и конструкторах. Интерфейс командной строки запускает это правило как обязательный пробный прогон + перед изменением файлов. + + Правило также сообщает параметры метода, которые передают свойство, которое миграция + сохраняет как изменяемое — столбцы ORM, объявления `@mutable-datetime`, + унаследованные свойства (`$this->ormColumn = $param;`, включая ветки `??`/ternary +): миграция такого параметра гарантирует `TypeError` при назначении свойства +. Решите проблему, пометив метод `@mutable-datetime` (его подпись + и подключенные аргументы места вызова остаются изменяемыми), путем совместной миграции столбцов ORM + с `--doctrine-columns` или путем миграции самого контракта хранения +. + + Операторы, содержащие комментарий `@mutable-datetime-boundary`, пропускаются как + уже проверенные граничные вызовы. Опция `MODE` правила выбирает `report` + (по умолчанию — прикрепить diff-маркеры `@todo`) или `acknowledge` (запишите комментарий + `@mutable-datetime-boundary` над каждым вызовом границы; используется + CLI `--acknowledge-boundaries`). Результаты фида никогда не записываются в режиме подтверждения +. @@ЛИНИЯ@@ +### `DateTimeImmutableRector` +Переносит конструкцию DateTime и объявления конкретных типов в + DateTimeImmutable. + + | Вариант | По умолчанию | Что это позволяет | + |---|---|---| + | `КОНСТРУКТОРЫ` | `правда` | `new \DateTime(...)`, общие статические фабрики, включая `createFromTimestamp()`, и две процедурные `date_create*()` | + | `ПОДСКАЗКИ` | `правда` | `\DateTime` в именованных функциях, методах, замыканиях, стрелочных функциях и методах перечислений (включая типы, допускающие значение NULL, и типы объединения) | + | `НЕДВИЖИМОСТЬ` | `правда` | `\DateTime` в типизированных свойствах и расширенных параметрах конструктора | + | `ALLOW_SUBCLASS` | `ложь` | переписать `class X расширяет \DateTime` на `extends \DateTimeImmutable` (рискованно — разрывы мутаций на месте в нисходящем направлении; пара с `LostDateTimeMutationRector`) | + | `DOCTRINE_COLUMNS` | `ложь` | совместная миграция столбцов Doctrine с сопоставлением атрибутов вместе с их типом сопоставления (см. «Совместная миграция столбцов Doctrine») | + + Миграция также сохраняет целостность файла: + + - типы докблоков `@var`/`@param`/`@return` перенесенного объявления + (включая варианты тега `@psalm-`/`@phpstan-`) перезаписываются на + `DateTimeImmutable` — меняется только токен типа, описания остаются; + объявления только для докблоков без собственного типа никогда не перезаписываются; + — импорт `use DateTime;` (с псевдонимом или без него) удаляется, если в файле + больше ничего не ссылается на него — код, блок документации и комментарии ссылаются на все счетчики +, и сканер ошибается, сохраняя импорт. @@ЛИНИЯ@@ +```php +->withConfiguredRule(DateTimeImmutableRector::class, [ + DateTimeImmutableRector::CONSTRUCTORS => true, + DateTimeImmutableRector::TYPEHINTS => true, + DateTimeImmutableRector::PROPERTIES => true, + DateTimeImmutableRector::ALLOW_SUBCLASS => false, +]) +``` +Явное отключение одной категории поддерживается для поэтапной миграции, но промежуточный этап + может оказаться невыполнимым до тех пор, пока не будут перенесены связанные конструкции и объявления типа +. Запускайте статический анализ и тесты после каждого этапа. + + Никогда не трогал: + + | Дело | Почему | + |---|---| + | `класс X расширяет \DateTime` (без `ALLOW_SUBCLASS`) | переписывание родителя нарушает локальную мутацию подкласса | + | Сигнатуры и свойства, объявленные предком/интерфейсом/признаком | реализации должны сохранять унаследованные контракты | + | Интерфейсы, особенности, абстрактные классы | их подписи являются договорами на внедрение | + | `#[Column]` / `@ORM\Column` сопоставленные элементы | ORM определяет конкретный класс для каждого отображаемого типа | + | Все, чей блок документации содержит `@mutable-datetime` | явный маркер отказа | + | `\DateTime::createFromImmutable(...)` | не имеет аналога DateTimeImmutable; содержащий возвращаемый тип также остается изменяемым | + | Конструирование внутри анонимных/абстрактных/типовых областей, а также значения по умолчанию, прямое присвоение свойств и возвраты с сохранением изменяемых контрактов | предотвращает внедрение неизменяемого значения в пропущенное объявление, не блокируя несвязанные миграции в том же классе/методе | + | Значения, связанные простым присвоением со стабильным вызываемым объектом, доступным только для DateTime, таким как date_modify() или API поставщика | связанные параметры, свойства, возвраты и конструкция остаются неизменными | + | Объединения, уже содержащие `\DateTimeImmutable`, в том числе внутри пересечения DNF | перезапись может привести к созданию дублирующего или избыточного типа | + | Типы возвращаемых значений, `return` которых напрямую возвращает сохраненное изменяемое свойство, в т.ч. `??`/тройные ветви | значение времени выполнения остается `DateTime`; перенесенное объявление будет гарантированно `TypeError` | + | Типы докблоков в объявлениях без перенесенного собственного типа | контракт, содержащий только докблок, не содержит доказательств времени выполнения; теги в перенесенных объявлениях синхронизируются автоматически | + | `new $class()`, типы пересечений | не является статически доказуемым | @@ЛИНИЯ@@ +### `LostDateTimeMutationRector` +Находит вызовы мутаторов на уровне оператора для объекта DateTimeImmutable, возвращаемое значение + которого отбрасывается: modify, add, sub, setDate, setTime, + setISODate, setTimezone, setTimestamp, setMicro Second. + + | Режим | Поведение | + |---|---| + | `MODE_FIX` (по умолчанию) | перезаписывает `$d->modify(...);` на `$d = $d->modify(...);` для непосредственно инициализированных точных встроенных переменных и конечных подклассов/объединений; никогда не присваивает `$this` | + | `MODE_REPORT` | вместо этого прикрепляет комментарий-маркер `// @todo Lost DateTimeImmutable Mutation…`; запустить с `--dry-run`, чтобы вывести из строя CI, оставив код нетронутым | @@ЛИНИЯ@@ +```php +->withConfiguredRule(LostDateTimeMutationRector::class, [ + LostDateTimeMutationRector::MODE => LostDateTimeMutationRector::MODE_REPORT, +]) +``` +Пропускаются в обоих режимах: используемые результаты, изменяемые приемники, не-подтипы (включая оболочки + PHPStan `@mixin`) и статически видимые переопределения мутаторов. Режим исправления + также пропускает `$this`, приемники свойств/вызовов, открытые объявленные типы, такие как параметр + `DateTimeImmutable`, и локальные значения, заполненные открытым типом возвращаемого значения: подкласс времени выполнения + может переопределить мутатор и законно мутировать на месте. Локальное + становится точным только после безусловного присвоения верхнего уровня из прямой встроенной конструкции +, общей статической фабрики, процедурной + `date_create_immutable*()` фабрики, `клона` точного значения или другого + проверенного точного выражения. Простой псевдоним (`$b = $a;`) намеренно не обеспечивает + точности: в изменяемой программе перед миграцией оба имени использовали + один мутировавший объект, поэтому назначение только получателя могло незаметно отличаться от устаревшего поведения + — о таких утверждениях сообщается. + Присвоения, вложенные в условные выражения, циклы, ветки переключения/попробования/сопоставления и сокращенные выражения +, никогда не обеспечивают точности и не делают недействительным открытое + консервативное доказательство. Поэтому присвоение и потерянная мутация, содержащиеся в + одной и той же условной ветви, могут намеренно оставаться неизменными. Финальные подклассы + и объединения финальных подклассов можно безопасно исправить. Режим отчета может диагностически отмечать + как открытый подтип, поскольку он не изменяет программу. Вызовы Nullsafe + (`$d?->modify(...)`) выходят за рамки. + + `MODE_REPORT` перекрывается с PHPStan уровня 4 («вызов на отдельной строке не имеет эффекта +») — используйте его только в том случае, если ваш конвейер запускает Rector без статического анализатора. @@ЛИНИЯ@@ +### Маркеры +Добавьте `@mutable-datetime` в блок документации, чтобы сохранить изменяемое объявление для цели +: + +```php +/** + * @mutable-datetime — third-party SDK mutates this in place + */ +private \DateTime $sdkClock; +``` +Добавьте `@mutable-datetime-boundary` в качестве комментария к оператору вызова, чтобы отметить проверенный граничный вызов + — тогда предварительная проверка его пропускает. `--acknowledge-boundaries` + пишет для вас эти комментарии: + +```php +// @mutable-datetime-boundary: parameter $object requires DateTime +date_modify($moment, '+1 hour'); +``` +## Безопасность +Это миграция, меняющая контракт. Значения по умолчанию переносят конструкцию и + конкретные локальные объявления вместе. Типизированные собственные/поставочные/наследуемые вызываемые границы +, унаследованные свойства/сигнатуры, сопоставления ORM и динамические имена + охраняются. Динамические вызовы, магическая диспетчеризация, отражение и потоки нетипизированных внешних данных + не могут быть подтверждены правилом «от источника к источнику». Просматривайте разницу и запускайте полную сборку проекта + после каждого прохода, особенно при использовании поэтапных параметров или + `ALLOW_SUBCLASS`, которые намеренно изменяют поведение во время выполнения подклассов `DateTime` +. @@ЛИНИЯ@@ +## Примеры +Запускаемые сценарии находятся в [`examples/`](examples/README.md). @@ЛИНИЯ@@ +## Разработка +```bash +make install # composer install (Docker, no local PHP needed) +make build # validate + normalize + require-checker + cs + psalm + tests +make test # testo (unit + e2e fixtures) +make mutation # infection, minMsi=100 — gates the Internal/ decision core; + # the rule shells run inside rector subprocesses and are + # covered by the e2e fixture suites instead +``` +Тестирование мутаций по своей конструкции ограничено `src/Internal/`: ядро ​​принятия решений + (каталог мутаторов, карта фабрики, средства перезаписи типов/докблоков, детектор столбца Doctrine +, средство сопоставления маркеров) работает в процессе и ограничивается значением `minMsi = 100`. Классы общедоступных правил + и CLI выполняются внутри подпроцессов Rector, которые + Infection не может наблюдать — они покрываются наборами фикстур e2e вместо + (выходные данные с байтовым сравнением, `php -l` для каждого преобразованного файла, исполняемые фикстуры + во время выполнения). Таким образом, номера заражений удостоверяют ядро ​​`Internal/`, а не + оценку мутаций всего пакета; обоснование см. в [AGENTS.md](AGENTS.md). @@ЛИНИЯ@@ +## Лицензия +[BSD-3-пункт](LICENSE.md) From 03a245027332797bef60e9d2cc60c375f279d887 Mon Sep 17 00:00:00 2001 From: "v.razuvaev" Date: Thu, 16 Jul 2026 22:12:16 +0300 Subject: [PATCH 3/3] docs: normalize Russian README --- README.ru.md | 458 +++++++++++++++++++++++++-------------------------- 1 file changed, 229 insertions(+), 229 deletions(-) diff --git a/README.ru.md b/README.ru.md index 4f5438c..5b75fe7 100644 --- a/README.ru.md +++ b/README.ru.md @@ -9,9 +9,9 @@ [English version](README.md) [Rector](https://getrector.com) rules that migrate mutable `DateTime` to -`DateTimeImmutable` — и **автоматическое исправление потерянных мутаций**, создаваемых миграцией -, классическая тихая ошибка, при которой `$date->modify('+1 day');` выбрасывает новый экземпляр -: +`DateTimeImmutable` — и **автоматическое исправление потерянных мутаций**, создаваемых миграцией +, классическая тихая ошибка, при которой `$date->modify('+1 day');` выбрасывает новый экземпляр +: ```php // before — mutable construction, in-place mutation @@ -22,49 +22,49 @@ $deadline->modify('+1 month'); $deadline = new \DateTimeImmutable('2026-01-01'); $deadline = $deadline->modify('+1 month'); ``` -PHPStan (уровень 4) и Psalm *report* игнорировали результаты мутатора DateTimeImmutable -; этот пакет — это часть, которая **исправляет их массово** во время миграции -. - - > Используете помощника по программированию с искусственным интеллектом? [llms.txt](llms.txt) имеет компактную ссылку +PHPStan (уровень 4) и Psalm *report* игнорировали результаты мутатора DateTimeImmutable +; этот пакет — это часть, которая **исправляет их массово** во время миграции +. + + > Используете помощника по программированию с искусственным интеллектом? [llms.txt](llms.txt) имеет компактную ссылку > которую можно передать в качестве контекста. @@ЛИНИЯ@@ ## ТЛ;ДР -Два способа выполнить миграцию: - - | Путь | Как | - |---|---| - | **Оболочка CLI** (рекомендуется) | `vendor/bin/rector-datetime-immutable src` — граничная предполетная проверка, миграция к конвергенции и проход диагностики в одной команде; см. [Миграция одной командой](#one-command-migration) | - | **Руководство `rector.php`** | зарегистрировать правила самостоятельно; см. [Ручная настройка Rector](#manual-rector-setup) | - - **Предупреждение при ручной настройке:** один запуск Rector не может одновременно выполнить миграцию и восстановление — запускайте - `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений** (обычно дважды), - в противном случае потерянные мутации, созданные при первом проходе, останутся в коде. Обертка +Два способа выполнить миграцию: + + | Путь | Как | + |---|---| + | **Оболочка CLI** (рекомендуется) | `vendor/bin/rector-datetime-immutable src` — граничная предполетная проверка, миграция к конвергенции и проход диагностики в одной команде; см. [Миграция одной командой](#one-command-migration) | + | **Руководство `rector.php`** | зарегистрировать правила самостоятельно; см. [Ручная настройка Rector](#manual-rector-setup) | + + **Предупреждение при ручной настройке:** один запуск Rector не может одновременно выполнить миграцию и восстановление — запускайте + `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений** (обычно дважды), + в противном случае потерянные мутации, созданные при первом проходе, останутся в коде. Обертка сделает это за вас. @@ЛИНИЯ@@ ## Оглавление -- [Требования](#requirements) - - [Установка](#install) - - [Использование](#использование) - - [Миграция одной командой](#one-command-migration) - - [Предварительный просмотр](#dry-run-preview) - - [Выход CI](#ci-output) - - [Разрешение результатов предполетной проверки](#resolve-preflight-findings) - - [Совместная миграция столбцов Doctrine](#doctrine-columns-co-migration) - - [Ручная настройка Rector](#manual-rector-setup) - - [`MutableDateTimeBoundaryRector`](#mutabledatetimeboundaryrector) - - [`DateTimeImmutableRector`](#datetimeimmutablerector) - - [`LostDateTimeMutationRector`](#lostdatetimemutationrector) - - [Маркеры](#маркеры) - - [Безопасность](#security) - - [Примеры](#examples) - - [Разработка](#development) - - [Лицензия](#лицензия) +- [Требования](#requirements) + - [Установка](#install) + - [Использование](#использование) + - [Миграция одной командой](#one-command-migration) + - [Предварительный просмотр](#dry-run-preview) + - [Выход CI](#ci-output) + - [Разрешение результатов предполетной проверки](#resolve-preflight-findings) + - [Совместная миграция столбцов Doctrine](#doctrine-columns-co-migration) + - [Ручная настройка Rector](#manual-rector-setup) + - [`MutableDateTimeBoundaryRector`](#mutabledatetimeboundaryrector) + - [`DateTimeImmutableRector`](#datetimeimmutablerector) + - [`LostDateTimeMutationRector`](#lostdatetimemutationrector) + - [Маркеры](#маркеры) + - [Безопасность](#security) + - [Примеры](#examples) + - [Разработка](#development) + - [Лицензия](#лицензия) ## Требования -- PHP 8.3 - 8.5 для запуска правил - - `rector/rector` ^2.5 - - `webmozart/assert` ^1.11 || ^2.0 - - `proc_open` включен при использовании оболочки конвергенции — доступен в сборке PHP - по умолчанию, если хост не отключит его через `disable_functions` +- PHP 8.3 - 8.5 для запуска правил + - `rector/rector` ^2.5 + - `webmozart/assert` ^1.11 || ^2.0 + - `proc_open` включен при использовании оболочки конвергенции — доступен в сборке PHP + по умолчанию, если хост не отключит его через `disable_functions` ## Установка ```bash @@ -72,15 +72,15 @@ composer require --dev rasuvaeff/rector-datetime-immutable ``` ## Использование ### Миграция одной командой -Установленный двоичный файл Composer сначала запускает предварительную проверку изменяемой границы только для чтения, - многократно применяет миграцию по умолчанию до полного прохождения подтверждения, затем - запускает `LostDateTimeMutationRector` в `MODE_REPORT` без изменения файлов: +Установленный двоичный файл Composer сначала запускает предварительную проверку изменяемой границы только для чтения, + многократно применяет миграцию по умолчанию до полного прохождения подтверждения, затем + запускает `LostDateTimeMutationRector` в `MODE_REPORT` без изменения файлов: ```bash vendor/bin/rector-datetime-immutable src ``` -Команда редактирует выбранные пути. Сначала выполните или спрячьте несвязанную работу. - Типичный вывод: +Команда редактирует выбранные пути. Сначала выполните или спрячьте несвязанную работу. + Типичный вывод: ```text Preflight: no mutable DateTime boundaries found. @@ -91,22 +91,22 @@ Converged after 2 change-producing pass(es). Diagnostic pass: no manual review cases found. Summary: 14 file(s) changed across 2 change-producing pass(es); 0 manual review case(s). ``` -Если предварительная проверка обнаруживает собственный, унаследованный, абстрактный/интерфейсный или вызываемый поставщиком объект -, параметр которого принимает DateTime, но отклоняет DateTimeImmutable, или параметр метода -, который передает свойство, которое миграция сохраняет как изменяемое, он печатает записи - `file:line` плюс подсказку по разрешению для каждой категории поиска, завершает работу с кодом - `2` и не изменяет файлы. Тот же выход используется после конвергенции, когда отчет - о потерянной мутации обнаруживает случай, который не может быть назначен безопасно. - - | Выход | Значение | - |---|---| - | `0` | миграция совпала, и дел, выполняемых вручную, не осталось | - | `1` | Ошибка ректора/процесса/JSON | - | `2` | предварительная блокированная миграция или ручная проверка после миграции остается | - | `3` | миграция не сошлась в пределах пропуска | - | `64` | неверные аргументы оболочки | - - Полезные опции: +Если предварительная проверка обнаруживает собственный, унаследованный, абстрактный/интерфейсный или вызываемый поставщиком объект +, параметр которого принимает DateTime, но отклоняет DateTimeImmutable, или параметр метода +, который передает свойство, которое миграция сохраняет как изменяемое, он печатает записи + `file:line` плюс подсказку по разрешению для каждой категории поиска, завершает работу с кодом + `2` и не изменяет файлы. Тот же выход используется после конвергенции, когда отчет + о потерянной мутации обнаруживает случай, который не может быть назначен безопасно. + + | Выход | Значение | + |---|---| + | `0` | миграция совпала, и дел, выполняемых вручную, не осталось | + | `1` | Ошибка ректора/процесса/JSON | + | `2` | предварительная блокированная миграция или ручная проверка после миграции остается | + | `3` | миграция не сошлась в пределах пропуска | + | `64` | неверные аргументы оболочки | + + Полезные опции: ```bash vendor/bin/rector-datetime-immutable --dry-run src # full preview, no writes @@ -121,60 +121,60 @@ vendor/bin/rector-datetime-immutable \ --report-config=rector-report.php \ src ``` -Упакованные значения по умолчанию: `config/preflight.php`, `config/migration.php` и -`config/report.php`. Используйте пользовательские конфигурации для пропусков, специфичных для проекта, поэтапные параметры +Упакованные значения по умолчанию: `config/preflight.php`, `config/migration.php` и +`config/report.php`. Используйте пользовательские конфигурации для пропусков, специфичных для проекта, поэтапные параметры или `ALLOW_SUBCLASS`. @@ЛИНИЯ@@ ### Предварительный просмотр пробного прогона -`--dry-run` копирует пути во временное рабочее пространство, запускает там весь поток - — предполетную проверку, конвергенцию, диагностический проход — печатает все потенциальные различия - с путями, сопоставленными с оригиналами, и не изменяет ни одного файла проекта. Коды выхода - сохраняют свое значение, поэтому предварительный просмотр также сообщает вам, чем закончится реальный запуск -. Объявления вне скопированных путей (классы поставщиков, родительские каталоги -, которые вы не передали) по-прежнему считываются из исходных файлов; прогон записи +`--dry-run` копирует пути во временное рабочее пространство, запускает там весь поток + — предполетную проверку, конвергенцию, диагностический проход — печатает все потенциальные различия + с путями, сопоставленными с оригиналами, и не изменяет ни одного файла проекта. Коды выхода + сохраняют свое значение, поэтому предварительный просмотр также сообщает вам, чем закончится реальный запуск +. Объявления вне скопированных путей (классы поставщиков, родительские каталоги +, которые вы не передали) по-прежнему считываются из исходных файлов; прогон записи остается авторитетным. @@ЛИНИЯ@@ ### выход CI -`--format=github` сохраняет результаты, полученные человеком, и дополнительно выдает - `::error file=…,line=…::…` аннотации рабочего процесса для предполетных блокировщиков и - `::warning …` для случаев проверки вручную, поэтому PR миграции отображает каждое обнаружение - в строке. - - `--format=json` подавляет повествование и печатает один машиночитаемый объект - на стандартный вывод: `status` (`clean`, `blocked`, `manual-review`, - `not-converged`, `acknowledged`), `exitCode`, `passes` для каждого прохода`, - `changedFiles` и Результаты `preflight`/`manualReview`/`acknowledged` как - `{файл, строка, сообщение, категория}`, где `category` — это одно из - `requires-datetime`, `feeds-mutable-property`, `lost-mutation`, `iagnostic`. +`--format=github` сохраняет результаты, полученные человеком, и дополнительно выдает + `::error file=…,line=…::…` аннотации рабочего процесса для предполетных блокировщиков и + `::warning …` для случаев проверки вручную, поэтому PR миграции отображает каждое обнаружение + в строке. + + `--format=json` подавляет повествование и печатает один машиночитаемый объект + на стандартный вывод: `status` (`clean`, `blocked`, `manual-review`, + `not-converged`, `acknowledged`), `exitCode`, `passes` для каждого прохода`, + `changedFiles` и Результаты `preflight`/`manualReview`/`acknowledged` как + `{файл, строка, сообщение, категория}`, где `category` — это одно из + `requires-datetime`, `feeds-mutable-property`, `lost-mutation`, `iagnostic`. При `--dry-run` объект также содержит потенциальные `diffs`. @@ЛИНИЯ@@ ### Решение предполетных предполетных выводов -| Нахождение | Разрешение | - |---|---| - | `параметр $x передает изменяемое свойство $y` | отметьте включающий метод `@mutable-datetime` — его подпись и связанные аргументы места вызова остаются изменяемыми — выполните совместную миграцию столбцов ORM с помощью `--doctrine-columns` или сначала перенесите контракт хранения | - | `параметр $x требует DateTime` | перепишите вызов в API, безопасный для DateTimeImmutable, или просмотрите поток и подтвердите его | - - `@mutable-datetime` в методе **calling** не заглушает поиск - `requires DateTime`: маркер сохраняет собственный контракт этого метода, -, в то время как поиск указывает на вызываемый собственный/вендорный/унаследованный параметр. Сама миграция - сохраняет изменяемые значения, связанные с таким вызываемым объектом с помощью простых - назначений, поэтому после проверки потока подтвердите это: +| Нахождение | Разрешение | + |---|---| + | `параметр $x передает изменяемое свойство $y` | отметьте включающий метод `@mutable-datetime` — его подпись и связанные аргументы места вызова остаются изменяемыми — выполните совместную миграцию столбцов ORM с помощью `--doctrine-columns` или сначала перенесите контракт хранения | + | `параметр $x требует DateTime` | перепишите вызов в API, безопасный для DateTimeImmutable, или просмотрите поток и подтвердите его | + + `@mutable-datetime` в методе **calling** не заглушает поиск + `requires DateTime`: маркер сохраняет собственный контракт этого метода, +, в то время как поиск указывает на вызываемый собственный/вендорный/унаследованный параметр. Сама миграция + сохраняет изменяемые значения, связанные с таким вызываемым объектом с помощью простых + назначений, поэтому после проверки потока подтвердите это: ```bash vendor/bin/rector-datetime-immutable --acknowledge-boundaries src ``` -При этом над каждым вызовом границы пишется самодокументируемый комментарий и повторно выполняется - предполетная проверка: +При этом над каждым вызовом границы пишется самодокументируемый комментарий и повторно выполняется + предполетная проверка: ```php // @mutable-datetime-boundary: parameter $object requires DateTime date_modify($moment, '+1 hour'); ``` -Оператор, содержащий `@mutable-datetime-boundary`, пропускается во всех дальнейших предварительных проверках - — проверка живет в коде и выдерживает повторные запуски. Находки - типа `feeds mutable property` **никогда** не подтверждаются автоматически: если отключить - их, это позволит миграции нарушить назначение свойств во время выполнения, поэтому - они сохраняют свои собственные разрешения, указанные выше. - - Пропуск на уровне файла через пользовательскую предполетную конфигурацию остается доступным как - грубая альтернатива: +Оператор, содержащий `@mutable-datetime-boundary`, пропускается во всех дальнейших предварительных проверках + — проверка живет в коде и выдерживает повторные запуски. Находки + типа `feeds mutable property` **никогда** не подтверждаются автоматически: если отключить + их, это позволит миграции нарушить назначение свойств во время выполнения, поэтому + они сохраняют свои собственные разрешения, указанные выше. + + Пропуск на уровне файла через пользовательскую предполетную конфигурацию остается доступным как + грубая альтернатива: ```php // rector-preflight.php @@ -198,14 +198,14 @@ return RectorConfig::configure() ```bash vendor/bin/rector-datetime-immutable --preflight-config=rector-preflight.php src ``` -После сходимости тот же выход `2` сообщает об утраченных мутациях, которые режим исправления не может безопасно назначить - — разрешите их, назначив результат мутатора самостоятельно +После сходимости тот же выход `2` сообщает об утраченных мутациях, которые режим исправления не может безопасно назначить + — разрешите их, назначив результат мутатора самостоятельно (`$date = $date->modify(...)`). @@ЛИНИЯ@@ ### Совместная миграция столбцов доктрины -По умолчанию элементы, сопоставленные с ORM, сохраняются. `--doctrine-columns` (опция - `DOCTRINE_COLUMNS` в обоих правилах) выбирает совместную миграцию столбцов с сопоставлением атрибутов -: свойство, его методы доступа и подключенные параметры конструктора - мигрируют вместе с сопоставлением, которое перемещается в собственный неизменяемый вариант DBAL +По умолчанию элементы, сопоставленные с ORM, сохраняются. `--doctrine-columns` (опция + `DOCTRINE_COLUMNS` в обоих правилах) выбирает совместную миграцию столбцов с сопоставлением атрибутов +: свойство, его методы доступа и подключенные параметры конструктора + мигрируют вместе с сопоставлением, которое перемещается в собственный неизменяемый вариант DBAL — та же схема базы данных, неизменяемая гидратация. @@ЛИНИЯ@@ ```php #[ORM\Column(type: 'datetime')] // → type: 'datetime_immutable' @@ -214,12 +214,12 @@ private \DateTime $expiresAt; // → private \DateTimeImmutable $ #[ORM\Column(type: Types::DATETIME_MUTABLE)] // → Types::DATETIME_IMMUTABLE #[ORM\Column] // no type: Doctrine infers it from the PHP type ``` -Покрытые сопоставления: `datetime`, `date`, `time`, `datetimetz` как строковые литералы - или соответствующие константы `Types::*_MUTABLE`, а также столбцы без аргумента `type` -. Строки пользовательских типов, выражения динамического типа, аргументы позиционного атрибута - и аннотации docblock `@ORM\Column` остаются сохраненными. Требуется - `doctrine/dbal` ≥ 2,6 (собственные типы `*_immutable`). Просмотрите код жизненного цикла -, в котором были изменены даты объекта — диагностический проход сообщает об этом как об утраченных мутациях +Покрытые сопоставления: `datetime`, `date`, `time`, `datetimetz` как строковые литералы + или соответствующие константы `Types::*_MUTABLE`, а также столбцы без аргумента `type` +. Строки пользовательских типов, выражения динамического типа, аргументы позиционного атрибута + и аннотации docblock `@ORM\Column` остаются сохраненными. Требуется + `doctrine/dbal` ≥ 2,6 (собственные типы `*_immutable`). Просмотрите код жизненного цикла +, в котором были изменены даты объекта — диагностический проход сообщает об этом как об утраченных мутациях . @@ЛИНИЯ@@ ### Ручная настройка ректора ```php @@ -239,61 +239,61 @@ return RectorConfig::configure() LostDateTimeMutationRector::class, ]); ``` -Без оболочки запускайте `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений -** (обычно дважды): в течение одного прогона вывод типа по-прежнему видит типы - до миграции, поэтому потерянные мутации, созданные конструкционной миграцией -, становятся видимыми при *следующем* прогоне. - - Запустите MutableDateTimeBoundaryRector отдельно с помощью --dry-run перед ручной миграцией -. Не объединяйте его с правилами миграции: его комментарии являются диагностическими +Без оболочки запускайте `vendor/bin/rectorprocess` **пока он не сообщит об отсутствии изменений +** (обычно дважды): в течение одного прогона вывод типа по-прежнему видит типы + до миграции, поэтому потерянные мутации, созданные конструкционной миграцией +, становятся видимыми при *следующем* прогоне. + + Запустите MutableDateTimeBoundaryRector отдельно с помощью --dry-run перед ручной миграцией +. Не объединяйте его с правилами миграции: его комментарии являются диагностическими маркерами различий, а не изменениями исходного кода, которые необходимо зафиксировать. @@ЛИНИЯ@@ ### `MutableDateTimeBoundaryRector` -Сообщает об аргументах, поступающих в стабильные вызываемые объекты, объявленный параметр - которых принимает DateTime, но отклоняет DateTimeImmutable. Стабильные вызываемые объекты — это встроенные - функции PHP, функции/методы поставщиков, интерфейсные или абстрактные методы, а также методы -, ограниченные предком или помеченные `@mutable-datetime`. Другие локальные - конкретные вызываемые объекты не сообщаются, поскольку их объявления мигрируют вместе - с их сайтами вызовов. - - Анализ поддерживает позиционные, именованные и переменные аргументы в функциях -, экземплярных/статических методах и конструкторах. Интерфейс командной строки запускает это правило как обязательный пробный прогон - перед изменением файлов. - - Правило также сообщает параметры метода, которые передают свойство, которое миграция - сохраняет как изменяемое — столбцы ORM, объявления `@mutable-datetime`, - унаследованные свойства (`$this->ormColumn = $param;`, включая ветки `??`/ternary -): миграция такого параметра гарантирует `TypeError` при назначении свойства -. Решите проблему, пометив метод `@mutable-datetime` (его подпись - и подключенные аргументы места вызова остаются изменяемыми), путем совместной миграции столбцов ORM - с `--doctrine-columns` или путем миграции самого контракта хранения -. - - Операторы, содержащие комментарий `@mutable-datetime-boundary`, пропускаются как - уже проверенные граничные вызовы. Опция `MODE` правила выбирает `report` - (по умолчанию — прикрепить diff-маркеры `@todo`) или `acknowledge` (запишите комментарий - `@mutable-datetime-boundary` над каждым вызовом границы; используется - CLI `--acknowledge-boundaries`). Результаты фида никогда не записываются в режиме подтверждения +Сообщает об аргументах, поступающих в стабильные вызываемые объекты, объявленный параметр + которых принимает DateTime, но отклоняет DateTimeImmutable. Стабильные вызываемые объекты — это встроенные + функции PHP, функции/методы поставщиков, интерфейсные или абстрактные методы, а также методы +, ограниченные предком или помеченные `@mutable-datetime`. Другие локальные + конкретные вызываемые объекты не сообщаются, поскольку их объявления мигрируют вместе + с их сайтами вызовов. + + Анализ поддерживает позиционные, именованные и переменные аргументы в функциях +, экземплярных/статических методах и конструкторах. Интерфейс командной строки запускает это правило как обязательный пробный прогон + перед изменением файлов. + + Правило также сообщает параметры метода, которые передают свойство, которое миграция + сохраняет как изменяемое — столбцы ORM, объявления `@mutable-datetime`, + унаследованные свойства (`$this->ormColumn = $param;`, включая ветки `??`/ternary +): миграция такого параметра гарантирует `TypeError` при назначении свойства +. Решите проблему, пометив метод `@mutable-datetime` (его подпись + и подключенные аргументы места вызова остаются изменяемыми), путем совместной миграции столбцов ORM + с `--doctrine-columns` или путем миграции самого контракта хранения +. + + Операторы, содержащие комментарий `@mutable-datetime-boundary`, пропускаются как + уже проверенные граничные вызовы. Опция `MODE` правила выбирает `report` + (по умолчанию — прикрепить diff-маркеры `@todo`) или `acknowledge` (запишите комментарий + `@mutable-datetime-boundary` над каждым вызовом границы; используется + CLI `--acknowledge-boundaries`). Результаты фида никогда не записываются в режиме подтверждения . @@ЛИНИЯ@@ ### `DateTimeImmutableRector` -Переносит конструкцию DateTime и объявления конкретных типов в - DateTimeImmutable. - - | Вариант | По умолчанию | Что это позволяет | - |---|---|---| - | `КОНСТРУКТОРЫ` | `правда` | `new \DateTime(...)`, общие статические фабрики, включая `createFromTimestamp()`, и две процедурные `date_create*()` | - | `ПОДСКАЗКИ` | `правда` | `\DateTime` в именованных функциях, методах, замыканиях, стрелочных функциях и методах перечислений (включая типы, допускающие значение NULL, и типы объединения) | - | `НЕДВИЖИМОСТЬ` | `правда` | `\DateTime` в типизированных свойствах и расширенных параметрах конструктора | - | `ALLOW_SUBCLASS` | `ложь` | переписать `class X расширяет \DateTime` на `extends \DateTimeImmutable` (рискованно — разрывы мутаций на месте в нисходящем направлении; пара с `LostDateTimeMutationRector`) | - | `DOCTRINE_COLUMNS` | `ложь` | совместная миграция столбцов Doctrine с сопоставлением атрибутов вместе с их типом сопоставления (см. «Совместная миграция столбцов Doctrine») | - - Миграция также сохраняет целостность файла: - - - типы докблоков `@var`/`@param`/`@return` перенесенного объявления - (включая варианты тега `@psalm-`/`@phpstan-`) перезаписываются на - `DateTimeImmutable` — меняется только токен типа, описания остаются; - объявления только для докблоков без собственного типа никогда не перезаписываются; - — импорт `use DateTime;` (с псевдонимом или без него) удаляется, если в файле - больше ничего не ссылается на него — код, блок документации и комментарии ссылаются на все счетчики +Переносит конструкцию DateTime и объявления конкретных типов в + DateTimeImmutable. + + | Вариант | По умолчанию | Что это позволяет | + |---|---|---| + | `КОНСТРУКТОРЫ` | `правда` | `new \DateTime(...)`, общие статические фабрики, включая `createFromTimestamp()`, и две процедурные `date_create*()` | + | `ПОДСКАЗКИ` | `правда` | `\DateTime` в именованных функциях, методах, замыканиях, стрелочных функциях и методах перечислений (включая типы, допускающие значение NULL, и типы объединения) | + | `НЕДВИЖИМОСТЬ` | `правда` | `\DateTime` в типизированных свойствах и расширенных параметрах конструктора | + | `ALLOW_SUBCLASS` | `ложь` | переписать `class X расширяет \DateTime` на `extends \DateTimeImmutable` (рискованно — разрывы мутаций на месте в нисходящем направлении; пара с `LostDateTimeMutationRector`) | + | `DOCTRINE_COLUMNS` | `ложь` | совместная миграция столбцов Doctrine с сопоставлением атрибутов вместе с их типом сопоставления (см. «Совместная миграция столбцов Doctrine») | + + Миграция также сохраняет целостность файла: + + - типы докблоков `@var`/`@param`/`@return` перенесенного объявления + (включая варианты тега `@psalm-`/`@phpstan-`) перезаписываются на + `DateTimeImmutable` — меняется только токен типа, описания остаются; + объявления только для докблоков без собственного типа никогда не перезаписываются; + — импорт `use DateTime;` (с псевдонимом или без него) удаляется, если в файле + больше ничего не ссылается на него — код, блок документации и комментарии ссылаются на все счетчики , и сканер ошибается, сохраняя импорт. @@ЛИНИЯ@@ ```php ->withConfiguredRule(DateTimeImmutableRector::class, [ @@ -303,65 +303,65 @@ return RectorConfig::configure() DateTimeImmutableRector::ALLOW_SUBCLASS => false, ]) ``` -Явное отключение одной категории поддерживается для поэтапной миграции, но промежуточный этап - может оказаться невыполнимым до тех пор, пока не будут перенесены связанные конструкции и объявления типа -. Запускайте статический анализ и тесты после каждого этапа. - - Никогда не трогал: - - | Дело | Почему | - |---|---| - | `класс X расширяет \DateTime` (без `ALLOW_SUBCLASS`) | переписывание родителя нарушает локальную мутацию подкласса | - | Сигнатуры и свойства, объявленные предком/интерфейсом/признаком | реализации должны сохранять унаследованные контракты | - | Интерфейсы, особенности, абстрактные классы | их подписи являются договорами на внедрение | - | `#[Column]` / `@ORM\Column` сопоставленные элементы | ORM определяет конкретный класс для каждого отображаемого типа | - | Все, чей блок документации содержит `@mutable-datetime` | явный маркер отказа | - | `\DateTime::createFromImmutable(...)` | не имеет аналога DateTimeImmutable; содержащий возвращаемый тип также остается изменяемым | - | Конструирование внутри анонимных/абстрактных/типовых областей, а также значения по умолчанию, прямое присвоение свойств и возвраты с сохранением изменяемых контрактов | предотвращает внедрение неизменяемого значения в пропущенное объявление, не блокируя несвязанные миграции в том же классе/методе | - | Значения, связанные простым присвоением со стабильным вызываемым объектом, доступным только для DateTime, таким как date_modify() или API поставщика | связанные параметры, свойства, возвраты и конструкция остаются неизменными | - | Объединения, уже содержащие `\DateTimeImmutable`, в том числе внутри пересечения DNF | перезапись может привести к созданию дублирующего или избыточного типа | - | Типы возвращаемых значений, `return` которых напрямую возвращает сохраненное изменяемое свойство, в т.ч. `??`/тройные ветви | значение времени выполнения остается `DateTime`; перенесенное объявление будет гарантированно `TypeError` | - | Типы докблоков в объявлениях без перенесенного собственного типа | контракт, содержащий только докблок, не содержит доказательств времени выполнения; теги в перенесенных объявлениях синхронизируются автоматически | +Явное отключение одной категории поддерживается для поэтапной миграции, но промежуточный этап + может оказаться невыполнимым до тех пор, пока не будут перенесены связанные конструкции и объявления типа +. Запускайте статический анализ и тесты после каждого этапа. + + Никогда не трогал: + + | Дело | Почему | + |---|---| + | `класс X расширяет \DateTime` (без `ALLOW_SUBCLASS`) | переписывание родителя нарушает локальную мутацию подкласса | + | Сигнатуры и свойства, объявленные предком/интерфейсом/признаком | реализации должны сохранять унаследованные контракты | + | Интерфейсы, особенности, абстрактные классы | их подписи являются договорами на внедрение | + | `#[Column]` / `@ORM\Column` сопоставленные элементы | ORM определяет конкретный класс для каждого отображаемого типа | + | Все, чей блок документации содержит `@mutable-datetime` | явный маркер отказа | + | `\DateTime::createFromImmutable(...)` | не имеет аналога DateTimeImmutable; содержащий возвращаемый тип также остается изменяемым | + | Конструирование внутри анонимных/абстрактных/типовых областей, а также значения по умолчанию, прямое присвоение свойств и возвраты с сохранением изменяемых контрактов | предотвращает внедрение неизменяемого значения в пропущенное объявление, не блокируя несвязанные миграции в том же классе/методе | + | Значения, связанные простым присвоением со стабильным вызываемым объектом, доступным только для DateTime, таким как date_modify() или API поставщика | связанные параметры, свойства, возвраты и конструкция остаются неизменными | + | Объединения, уже содержащие `\DateTimeImmutable`, в том числе внутри пересечения DNF | перезапись может привести к созданию дублирующего или избыточного типа | + | Типы возвращаемых значений, `return` которых напрямую возвращает сохраненное изменяемое свойство, в т.ч. `??`/тройные ветви | значение времени выполнения остается `DateTime`; перенесенное объявление будет гарантированно `TypeError` | + | Типы докблоков в объявлениях без перенесенного собственного типа | контракт, содержащий только докблок, не содержит доказательств времени выполнения; теги в перенесенных объявлениях синхронизируются автоматически | | `new $class()`, типы пересечений | не является статически доказуемым | @@ЛИНИЯ@@ ### `LostDateTimeMutationRector` -Находит вызовы мутаторов на уровне оператора для объекта DateTimeImmutable, возвращаемое значение - которого отбрасывается: modify, add, sub, setDate, setTime, - setISODate, setTimezone, setTimestamp, setMicro Second. - - | Режим | Поведение | - |---|---| - | `MODE_FIX` (по умолчанию) | перезаписывает `$d->modify(...);` на `$d = $d->modify(...);` для непосредственно инициализированных точных встроенных переменных и конечных подклассов/объединений; никогда не присваивает `$this` | +Находит вызовы мутаторов на уровне оператора для объекта DateTimeImmutable, возвращаемое значение + которого отбрасывается: modify, add, sub, setDate, setTime, + setISODate, setTimezone, setTimestamp, setMicro Second. + + | Режим | Поведение | + |---|---| + | `MODE_FIX` (по умолчанию) | перезаписывает `$d->modify(...);` на `$d = $d->modify(...);` для непосредственно инициализированных точных встроенных переменных и конечных подклассов/объединений; никогда не присваивает `$this` | | `MODE_REPORT` | вместо этого прикрепляет комментарий-маркер `// @todo Lost DateTimeImmutable Mutation…`; запустить с `--dry-run`, чтобы вывести из строя CI, оставив код нетронутым | @@ЛИНИЯ@@ ```php ->withConfiguredRule(LostDateTimeMutationRector::class, [ LostDateTimeMutationRector::MODE => LostDateTimeMutationRector::MODE_REPORT, ]) ``` -Пропускаются в обоих режимах: используемые результаты, изменяемые приемники, не-подтипы (включая оболочки - PHPStan `@mixin`) и статически видимые переопределения мутаторов. Режим исправления - также пропускает `$this`, приемники свойств/вызовов, открытые объявленные типы, такие как параметр - `DateTimeImmutable`, и локальные значения, заполненные открытым типом возвращаемого значения: подкласс времени выполнения - может переопределить мутатор и законно мутировать на месте. Локальное - становится точным только после безусловного присвоения верхнего уровня из прямой встроенной конструкции -, общей статической фабрики, процедурной - `date_create_immutable*()` фабрики, `клона` точного значения или другого - проверенного точного выражения. Простой псевдоним (`$b = $a;`) намеренно не обеспечивает - точности: в изменяемой программе перед миграцией оба имени использовали - один мутировавший объект, поэтому назначение только получателя могло незаметно отличаться от устаревшего поведения - — о таких утверждениях сообщается. - Присвоения, вложенные в условные выражения, циклы, ветки переключения/попробования/сопоставления и сокращенные выражения -, никогда не обеспечивают точности и не делают недействительным открытое - консервативное доказательство. Поэтому присвоение и потерянная мутация, содержащиеся в - одной и той же условной ветви, могут намеренно оставаться неизменными. Финальные подклассы - и объединения финальных подклассов можно безопасно исправить. Режим отчета может диагностически отмечать - как открытый подтип, поскольку он не изменяет программу. Вызовы Nullsafe - (`$d?->modify(...)`) выходят за рамки. - - `MODE_REPORT` перекрывается с PHPStan уровня 4 («вызов на отдельной строке не имеет эффекта +Пропускаются в обоих режимах: используемые результаты, изменяемые приемники, не-подтипы (включая оболочки + PHPStan `@mixin`) и статически видимые переопределения мутаторов. Режим исправления + также пропускает `$this`, приемники свойств/вызовов, открытые объявленные типы, такие как параметр + `DateTimeImmutable`, и локальные значения, заполненные открытым типом возвращаемого значения: подкласс времени выполнения + может переопределить мутатор и законно мутировать на месте. Локальное + становится точным только после безусловного присвоения верхнего уровня из прямой встроенной конструкции +, общей статической фабрики, процедурной + `date_create_immutable*()` фабрики, `клона` точного значения или другого + проверенного точного выражения. Простой псевдоним (`$b = $a;`) намеренно не обеспечивает + точности: в изменяемой программе перед миграцией оба имени использовали + один мутировавший объект, поэтому назначение только получателя могло незаметно отличаться от устаревшего поведения + — о таких утверждениях сообщается. + Присвоения, вложенные в условные выражения, циклы, ветки переключения/попробования/сопоставления и сокращенные выражения +, никогда не обеспечивают точности и не делают недействительным открытое + консервативное доказательство. Поэтому присвоение и потерянная мутация, содержащиеся в + одной и той же условной ветви, могут намеренно оставаться неизменными. Финальные подклассы + и объединения финальных подклассов можно безопасно исправить. Режим отчета может диагностически отмечать + как открытый подтип, поскольку он не изменяет программу. Вызовы Nullsafe + (`$d?->modify(...)`) выходят за рамки. + + `MODE_REPORT` перекрывается с PHPStan уровня 4 («вызов на отдельной строке не имеет эффекта ») — используйте его только в том случае, если ваш конвейер запускает Rector без статического анализатора. @@ЛИНИЯ@@ ### Маркеры -Добавьте `@mutable-datetime` в блок документации, чтобы сохранить изменяемое объявление для цели -: +Добавьте `@mutable-datetime` в блок документации, чтобы сохранить изменяемое объявление для цели +: ```php /** @@ -369,22 +369,22 @@ return RectorConfig::configure() */ private \DateTime $sdkClock; ``` -Добавьте `@mutable-datetime-boundary` в качестве комментария к оператору вызова, чтобы отметить проверенный граничный вызов - — тогда предварительная проверка его пропускает. `--acknowledge-boundaries` - пишет для вас эти комментарии: +Добавьте `@mutable-datetime-boundary` в качестве комментария к оператору вызова, чтобы отметить проверенный граничный вызов + — тогда предварительная проверка его пропускает. `--acknowledge-boundaries` + пишет для вас эти комментарии: ```php // @mutable-datetime-boundary: parameter $object requires DateTime date_modify($moment, '+1 hour'); ``` ## Безопасность -Это миграция, меняющая контракт. Значения по умолчанию переносят конструкцию и - конкретные локальные объявления вместе. Типизированные собственные/поставочные/наследуемые вызываемые границы -, унаследованные свойства/сигнатуры, сопоставления ORM и динамические имена - охраняются. Динамические вызовы, магическая диспетчеризация, отражение и потоки нетипизированных внешних данных - не могут быть подтверждены правилом «от источника к источнику». Просматривайте разницу и запускайте полную сборку проекта - после каждого прохода, особенно при использовании поэтапных параметров или - `ALLOW_SUBCLASS`, которые намеренно изменяют поведение во время выполнения подклассов `DateTime` +Это миграция, меняющая контракт. Значения по умолчанию переносят конструкцию и + конкретные локальные объявления вместе. Типизированные собственные/поставочные/наследуемые вызываемые границы +, унаследованные свойства/сигнатуры, сопоставления ORM и динамические имена + охраняются. Динамические вызовы, магическая диспетчеризация, отражение и потоки нетипизированных внешних данных + не могут быть подтверждены правилом «от источника к источнику». Просматривайте разницу и запускайте полную сборку проекта + после каждого прохода, особенно при использовании поэтапных параметров или + `ALLOW_SUBCLASS`, которые намеренно изменяют поведение во время выполнения подклассов `DateTime` . @@ЛИНИЯ@@ ## Примеры Запускаемые сценарии находятся в [`examples/`](examples/README.md). @@ЛИНИЯ@@ @@ -397,13 +397,13 @@ make mutation # infection, minMsi=100 — gates the Internal/ decision core; # the rule shells run inside rector subprocesses and are # covered by the e2e fixture suites instead ``` -Тестирование мутаций по своей конструкции ограничено `src/Internal/`: ядро ​​принятия решений - (каталог мутаторов, карта фабрики, средства перезаписи типов/докблоков, детектор столбца Doctrine -, средство сопоставления маркеров) работает в процессе и ограничивается значением `minMsi = 100`. Классы общедоступных правил - и CLI выполняются внутри подпроцессов Rector, которые - Infection не может наблюдать — они покрываются наборами фикстур e2e вместо - (выходные данные с байтовым сравнением, `php -l` для каждого преобразованного файла, исполняемые фикстуры - во время выполнения). Таким образом, номера заражений удостоверяют ядро ​​`Internal/`, а не +Тестирование мутаций по своей конструкции ограничено `src/Internal/`: ядро ​​принятия решений + (каталог мутаторов, карта фабрики, средства перезаписи типов/докблоков, детектор столбца Doctrine +, средство сопоставления маркеров) работает в процессе и ограничивается значением `minMsi = 100`. Классы общедоступных правил + и CLI выполняются внутри подпроцессов Rector, которые + Infection не может наблюдать — они покрываются наборами фикстур e2e вместо + (выходные данные с байтовым сравнением, `php -l` для каждого преобразованного файла, исполняемые фикстуры + во время выполнения). Таким образом, номера заражений удостоверяют ядро ​​`Internal/`, а не оценку мутаций всего пакета; обоснование см. в [AGENTS.md](AGENTS.md). @@ЛИНИЯ@@ ## Лицензия [BSD-3-пункт](LICENSE.md)