⚡ Быстрый форматтер HTML + PHP для view-файлов Yii 2 • Rust 2024 edition
🔍 Lexer + AST parser • 🎨 HTML + PHP formatting • 🔀 Smart line splitting
🏗 Yii 2 widgets • 📁 Recursive directory walk • ⚙️ Конфиг .phew.toml
View-файлы Yii 2 - это .php с HTML, PHP-вставками, виджетами и альтернативным синтаксисом (foreach(): ... endforeach;) вперемешку. Готовые форматтеры с этим не справляются: Prettier и HTMLBeautifier ломаются на <?php, PHP CS Fixer не видит HTML и пропускает view-файлы, Blade Formatter заточен под Laravel, а PhpStorm/Intelephense живут только внутри IDE - из консоли, CI или pre-commit хука их не вызвать.
phew - один CLI-инструмент, который понимает и HTML, и PHP в контексте друг друга.
cargo install --git https://github.com/WarLikeLaux/phew --force# Вывести отформатированный файл в stdout
phew views/site/index.php
# Перезаписать файл на месте
phew -w views/site/index.php
# Отформатировать всю директорию рекурсивно (.php и .html), обход и форматирование параллельны
# Файлы из .gitignore и .phewignore пропускаются
phew -w views/
# Прочитать из stdin, отформатированный вывести в stdout (для интеграции с редактором)
phew - < views/site/index.php# Режим для CI: ничего не пишет, выходит с кодом ≠0, если файлы не отформатированы
phew --check views/
# Показать, что изменилось бы, без записи (unified diff)
phew --diff views/Прогнать все вьюхи перед коммитом:
phew -w views/ widgets/ && git add -uИли как git pre-commit хук (.git/hooks/pre-commit):
#!/bin/sh
phew --check views/ || { echo "Запусти: phew -w views/"; exit 1; }На больших проектах проверяйте только staged-файлы, чтобы не блокировать коммит из-за чужого кода в соседних файлах:
git diff --cached --name-only -z --diff-filter=d | grep -zE '\.php$' | xargs -0 -r phew --checkОтладочные режимы:
phew --tokens views/site/index.php # токены лексера
phew --tree views/site/index.php # AST-деревоРежимы --write, --check, --diff, --tokens, --tree взаимоисключающие.
По умолчанию отступ - 4 пробела, целевая длина строки - 120 символов. Это настраивается через файл .phew.toml: phew ищет его от текущего каталога вверх по дереву и останавливается на границе git-репозитория.
# Стиль отступа: "spaces" или "tabs"
indent_style = "spaces"
# Размер отступа в пробелах (игнорируется при "tabs")
indent_size = 4
# Целевая максимальная длина строки
max_line_length = 120Создать файл с дефолтами:
phew --initУказать конфиг явно или переопределить отдельные значения флагами:
phew --config path/to/.phew.toml views/
phew --line-length 100 --indent-style tabs views/
phew --indent-size 2 views/Приоритет: CLI-флаги → .phew.toml → значения по умолчанию.
До: (всё в одну строку, без пробелов, смешанные кавычки)
<div class="catalog">
<?php $form=ActiveForm::begin(['options'=>['data-config'=>'{"ajax":true,"perPage":20}']]);?>
<?=$form->field($model,'category')->dropDownList($categories,['prompt'=>Yii::t('app','Выберите категорию товара')])?>
<?=GridView::widget(['dataProvider'=>$dataProvider,'columns'=>['id',['attribute'=>'type','value'=>fn($m)=>match($m->type){'book'=>Yii::t('app','Книга'),'article'=>Yii::t('app','Статья'),default=>Yii::t('app','Неизвестно')}],['class'=>ActionColumn::class]]])?>
<?php ActiveForm::end();?>
</div>После:
<div class="catalog">
<?php $form = ActiveForm::begin(['options' => ['data-config' => '{"ajax":true,"perPage":20}']]); ?>
<?= $form->field($model, 'category')
->dropDownList($categories, ['prompt' => Yii::t('app', 'Выберите категорию товара')]) ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
[
'attribute' => 'type',
'value' => fn ($m) => match ($m->type) {
'book' => Yii::t('app', 'Книга'),
'article' => Yii::t('app', 'Статья'),
default => Yii::t('app', 'Неизвестно'),
},
],
['class' => ActionColumn::class],
],
]) ?>
<?php ActiveForm::end(); ?>
</div>- HTML + PHP в едином AST — отступы вложенных элементов и блоков, alt-синтаксис (
endforeach;) и brace-стиль - Разбивка длинных строк (≤120) — аргументы, цепочки
->, массивы, тернарники,match - Yii 2 — виджеты (
GridView,ActiveForm,Nav…),::begin()/::end(), PHP внутри атрибутов - Header-блоки — PSR-12 порядок
declare → use → docblock, сортировка/дедупuse,@var - PHP 8.x —
match,enum, named args, first-class callable - Надёжность — идемпотентность, round-trip,
.gitignore/.phewignore, параллельный обход - CI и редакторы —
--check,--diff, stdin → stdout
Известные ограничения — в docs/known-issues.md.
| Правило | Значение |
|---|---|
| Целевая длина строки | ≤120 символов по умолчанию (настраивается) |
| Исключения | <?= ... ?> echo-блоки, где перенос ухудшает читаемость или ломает выражение |
| Отступ | 4 пробела по умолчанию, пробелы или табы (настраивается) |
| Trailing comma | Да, в многострочных вызовах |
| EOF | Файл заканчивается ровно одним \n (POSIX) |
94 unit-теста, 140 fixture-пар (tests/fixtures/input/ → tests/fixtures/expected/) и property-тесты на идемпотентность, round-trip и фаззинг (tests/properties.rs). Полная проверка перед коммитом:
just check # clippy + test + fixturesMIT