Недавно я пришел к мысли, что в PHP все классы должны быть окончательными по умолчанию , и начал делать это как само собой разумеющееся. Однако, когда вы начинаете делать что-то подобное, легко пропустить несколько файлов, которые не были обновлены, или забыть это сделать, поэтому я хотел найти способ обнаружить классы PHP, которые не заданы как абстрактные или окончательные, и, если возможно, автоматически установите их как окончательные. Я уже упоминал ранее, что я широко использую PHP CodeSniffer, и у него есть возможность как находить, так и устранять отклонения от стиля кодирования, поэтому прошлой ночью я начал изучать возможность создания стандарта кодирования для этого. Потребовалось немного поработать, чтобы понять, как это сделать поэтому я решил использовать этот нюх в качестве простого примера.
Первая часть состоит в том, чтобы определить структуру каталогов. Существует очень специфический макет, которому вы должны следовать для PHP CodeSniffer:
- Папка для стандарта должна иметь имя стандарта и находиться в исходной папке, заданной композитором (в данном случае
src/AbstractOrFinalClassesOnly. - Эта папка должна содержать
ruleset.xmlфайл, определяющий название и описание стандарта, а также любое другое требуемое содержимое. - Все определенные нюхательные запахи должны находиться в папке
Нюхательные запахи.
Тот ruleset.xml файл в данном случае был довольно простым, так как это очень простой стандарт:
Checks all classes are marked as either abstract or final.
Нюх предназначен для выполнения следующих действий:
- Проверьте, что все классы имеют либо ключевое слово
final, либо набор ключевых словabstract - При запуске исправления сделайте все классы без ключевого слова
abstractокончательными
Прежде всего, наш класс должен реализовать интерфейс PHP_CodeSniffer\Нюхает\Нюхает , для которого требуются следующие методы:
public function register(): array;
public function process(File $file, $position): void;
Обратите внимание, что Файл вот пример PHP_CodeSniffer\Файлы\Файл . Первый метод регистрирует код, с которым должен работать sniff. Здесь нас интересуют только классы, поэтому мы возвращаем массив, содержащий T_КЛАСС . Это определено в списке маркеров синтаксического анализатора, используемых PHP , и представляет классы и объекты:
public function register(): array
{
return [T_CLASS];
}
Для метода process() мы получаем два аргумента: сам файл и позицию. Нам нужно вести учет жетонов, которые мы проверяем, поэтому мы делаем это в частной собственности:
private $tokens = [
T_ABSTRACT,
T_FINAL,
];
Затем нам нужно найти ошибку:
if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
}
Мы используем $file для получения токена перед классом и передаем свойство $tokens в виде списка допустимых значений. Если предыдущий токен не является ни абстрактным , ни окончательным , мы добавляем исправляемую ошибку. Первый аргумент – это строковое сообщение об ошибке, второй – местоположение, а третий – класс сбоя sniff.
Это устранит проблему, но на самом деле не исправит ее. Для этого нам нужно получить исправление из объекта file и вызвать его метод addContent() , чтобы добавить ключевое слово final . Мы изменяем process() для извлечения исправления, добавляем его в качестве свойства, а затем вызываем метод fix() , когда сталкиваемся с исправляемой ошибкой:
public function process(File $file, $position): void
{
$this->fixer = $file->fixer;
$this->position = $position;
if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
$this->fix();
}
}
Затем мы определяем метод fix() :
private function fix(): void
{
$this->fixer->addContent($this->position - 1, 'final ');
}
Вот законченный урок:
fixer = $file->fixer;
$this->position = $position;
if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
$this->fix();
}
}
private function fix(): void
{
$this->fixer->addContent($this->position - 1, 'final ');
}
}
Я сделал полученный стандарт доступным через Github .
Это немного грубо и готово, и я, вероятно, немного переработаю его, когда у меня будет время. Кроме того, он не совсем отображает поведение, которое я хочу, как должно, поскольку в идеале он должен искать только абстрактный и окончательный ключевые слова в классах, реализующих интерфейс. Тем не менее, оказалось довольно легко создать этот нюх, за исключением того факта, что мне пришлось копаться в различных учебных пособиях, которые были не совсем понятны. Надеюсь, этот пример немного проще и ему легче следовать.
Оригинал: “https://dev.to/matthewbdaly/writing-a-custom-sniff-for-php-codesniffer-4b82”