Рубрики
Uncategorized

Написание пользовательского сниффа для PHP CodeSniffer

Недавно я пришел к идее, что в PHP все классы должны быть окончательными по умолчанию и иметь st… Помеченный php.

Недавно я пришел к мысли, что в 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”