Как найти синтаксическую ошибку в коде php

Вчера всё работало, а сегодня не работает / Код не работает как задумано

или

Debugging (Отладка)


В чем заключается процесс отладки? Что это такое?

Процесс отладки состоит в том, что мы останавливаем выполнения скрипта в любом месте, смотрим, что находится в переменных, в функциях, анализируем и переходим в другие места; ищем те места, где поведение отклоняется от правильного.


Важное замечание:

Есть много IDE и редакторов кода, которые позволяют производить отладку. Процесс настройки в них у всех различается. Поэтому стОит обратиться к документации по настройке отладки для непосредственно той среды разработки и той версии, в которой работаете именно ВЫ.

На текущий момент будет рассмотрен пример с PHPStorm 2017.


Подготовка

Для начала необходимо, чтобы в PHP имелась библиотека для отладки под названием xdebug. Если её еще нет, то надо установить.

ВАЖНО! Для очень новых версий PHP (например 8), требуется и новый xdebug, а он, в свою очередь, работает на порту 9003. Не пропустите указание правильного порта в IDE!! (Примерно в разделе PHP -> Debug -> Debug Port . Где точно — зависит от конкретной IDE)

Для WINDOWS:

скачать dll, например на xdebug.org.

Обычно все библиотеки лежат в папке ext внутри папки PHP. Туда и надо поместить dll.

Далее в php.ini прописываем настройки:

[Xdebug]
zend_extension="C:/server/php/ext/php_xdebug.dll" // <!-- тут свой путь до dll!!! Это для среды Windows. 
; Для Linux путь должен быть что-то типа zend_extension=/usr/lib/php/20151012/xdebug.so 
xdebug.default_enable = 1
xdebug.remote_enable = 1
xdebug.remote_handler = "dbgp"
xdebug.remote_host = "localhost"
xdebug.remote_port = 9000
xdebug.auto_trace = 0

Перезагружаем сервер, на всякий случай.

Для UBUNTU:

  • sudo apt update ИЛИ sudo apt-get update

  • sudo apt install php-xdebug или если нужнен отладчик для конкретной версии PHP, то sudo apt install php7.0-xdebug где 7.0 указывается версия PHP

  • sudo nano /etc/php/7.0/mods-available/xdebug.ini

    вписываем строки:

     zend_extension=/usr/lib/php/20151012/xdebug.so
     xdebug.remote_autostart = 1
     xdebug.remote_enable = 1
     xdebug.remote_handler = dbgp
     xdebug.remote_host = 127.0.0.1
     xdebug.remote_log = /tmp/xdebug_remote.log
     xdebug.remote_mode = req
    

    Примечание: каталог 20151012, скорее всего, будет другим. cd в /usr/lib/php и проверьте, в каком каталоге в этом формате находится файл xdebug.so, и используйте этот путь. 7.0 — тоже отличается, в зависимости от того, какая версия у вас используется

  • Перезагружаем сервер, на всякий случай.


Теперь если в файле .php написать phpinfo(); то можно будет увидеть в самом низу такую картину:

введите сюда описание изображения

Открываем PHPStorm

  • нажимаем create project from existing files
  • выбираем Web server is installed locally, source files are located under its document root
  • выбираем папку с файлами, и нажав вверху кнопку «Project Root» помечаем папку как корень проекта
  • нажимаем «Next»
  • нажимаем Add new local server

введите сюда описание изображения

  • вводим имя сервера любое и Web Server root URL. В рассматриваемом примере это http://localhost/testy2

введите сюда описание изображения

  • нажимаем «Next» и затем «Finish»

Запуск

Для начала в левой части панели с кодом на любой строке можно кликнуть ЛКМ, тем самым поставив точку останова (breakpoint — брейкпойнт). Это то место, где отладчик автоматически остановит выполнение PHP, как только до него дойдёт. Количество breakpoint’ов не ограничено. Можно ставить везде и много.

введите сюда описание изображения

Если кликнуть ПКМ и во всплывающем меню выбрать Debug (или в верхнем меню — RunDebug), то при первом запуске PHPStorm попросит настроить интерпретатор. Т.е. надо выбрать версию PHP из папки, где он лежит, чтобы шторм знал, какую версию он будет отлаживать.

введите сюда описание изображения

Теперь можно нажать Debug!!!

В данном случае, т.к. функция вызывается сразу на той же странице, то при нажатии кнопки Debug — отладчик моментально вызовет функцию, выполнение «заморозится» на первом же брейкпойнте. В ином случае, для активации требуется исполнить действие, при котором произойдет исполнение нужного участка кода (клик на кнопку, передача POST-запроса с формы с данными и другие действия).

введите сюда описание изображения

Цифрами обозначены:

  1. Стэк вызовов, все вложенные вызовы, которые привели к текущему месту кода.
  2. Переменные. На текущий момент строки ниже номера 3 ещё не выполнились, поэтому определена лишь $data
  3. Показывает текущие значения любых переменных и выражений. В любой момент здесь можно нажать на +, вписать имя любой переменной и посмотреть её значение в реальном времени. Например: $data или $nums[0], а можно и $nums[i] и item['test']['data'][$name[5]][$info[$key[1]]] и т.д. На текущий момент строки ниже номера 3 ещё не выполнились, поэтому $sum и $output обозначены красным цветом с надписью «cannot evaluate expression».

Процесс

Для самого процесса используются элементы управления (см. изображение выше, выделено зеленым прямоугольником) и немного из дополнительно (см. изображение выше, выделено оранжевым прямоугольником).

введите сюда описание изображения

Show Execution Point (Alt+F10) — переносит в файл и текущую линию отлаживаемого скрипта. Например, если файлов много, решили посмотреть что в других вкладках, а потом забыли где у вас отладка :)

Step Over (F8) — делает один шаг, не заходя внутрь функции. Т.е. если на текущей линии есть какая-то функция, а не просто переменная со значением, то при клике данной кнопки, отладчик не будет заходить внутрь неё.

Step Into (F7) — делает шаг. Но в отличие от предыдущей, если есть вложенный вызов (например функция), то заходит внутрь неё.

Step Out (Shift+F8) — выполняет команды до завершения текущей функции. Удобно, если случайно вошли во вложенный вызов и нужно быстро из него выйти, не завершая при этом отладку.

Rerun (Ctrl+F5) — перезапускает отладку.

Resume Program(F9) — продолжает выполнение скрипта с текущего момента. Если больше нет других точек останова, то отладка заканчивается и скрипт продолжает работу. В ином случае работа прерывается на следующей точке останова.

Stop (Ctrl+F2) — завершает отладку.

View Breakpoints (Ctrl+Shift+F8) — просмотр всех установленных брейкпойнтов.

Mute Breakpoints — отключает брейкпойнты.

Итак, в текущем коде видно значение входного параметра:

  • $data = "23 24 11 18" — строка с данными через пробел
  • $nums = (4) ["23", "24", "11", "18"] — массив, который получился из входной переменной.

введите сюда описание изображения

Если нажмем F8 2 раза, то окажемся на строке 7; во вкладках Watches и Variables и в самой странице с кодом увидим, что переменная $sum была инициализирована и её значение равно 0.

Если теперь нажмем F8, то попадем внутрь цикла foreach и, нажимая теперь F8, пока не окончится цикл, можно будет наблюдать на каждой итерации, как значения $num и $sum постоянно изменяются. Тем самым мы можем проследить шаг за шагом весь процесс изменения любых переменных и значений на любом этапе, который интересует.

Дальнейшие нажатия F8 переместят линию кода на строки 11, 12 и, наконец, 15.


Дополнительно

Если нажать на View Breakpoints в левой панели, то можно не только посмотреть все брейкпойнты, но в появившемся окне можно еще более тонко настроить условие, при котором на данной отметке надо остановиться.
В функции выше, например, нужно остановиться только когда $sum превысит значение 20.

введите сюда описание изображения

Это удобно, если останов нужен только при определённом значении, а не всегда (особенно в случае с циклами).

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    Syntax checking is one of the most important tasks in programming. Our compiler checks our code and shows relevant errors if there is any in the code i.e. compile time, run time, syntax, etc. We can do the same thing i.e. syntax checking in PHP.

    In this article, we are going to learn how we can do syntax checking in PHP. The syntax is basically a set of rules that defines the structure of a programming language. We can not manually change the syntax and we have to follow the rules of the language.

    Syntax:

    php -ln filename

    We can use –syntax-check in place of -ln, it is more readable.

    php --syntax-check filename

    It returnsNo syntax errors detected in <filename>” if the provided PHP file has no syntax errors and passed the syntax check. Otherwise, it returns “Errors parsing <filename>” if there is a syntax error in the PHP file.

    The “-l” or  “–syntax-check” argument performs a syntax check on the given PHP file.

    Example 1: The following PHP Code is without Syntax error. It finds the sum of two numbers.

    PHP

    <?php

        $x = 10;

        $y = 20;

        $c = $x + $y;

        echo "Sum is: ", $c;

        echo "n"

    ?>

    Command for syntax checking:

    php -ln /home/rich/Documents/geeks/geek.php

    Output:

    No Syntax Error

    Example 2: In the above code, we simply replaced the “Sum: “, $z; with  “Sum:, $z; then it throwing a syntax error.

    PHP

    <?PHP

        $x = 15;

        $y = 30;

        $z = $x + $y;

        echo "Sum:, $z;

        echo "n";

    ?>

    Command to do syntax checking:

    php -ln /home/rich/Documents/geeks/geek.php

    Output:

    Syntax Error

    Last Updated :
    21 Sep, 2021

    Like Article

    Save Article

    Что этот инструмент может

    Этот инструмент проверяет синтаксис введенного кода PHP и выделяет любые синтаксические ошибки, если они есть.
    Описание, которое вызывает синтаксическую ошибку, отличается в зависимости от версии PHP, поэтому выберите произвольную версию и используйте ее.

    Полезно для

    • разработчики, чтобы найти причину ошибок в программах PHP

    How to use the free code checker

    Code

    Copy and paste your PHP code into the editor.

    Language

    Select your language from the dropdown.

    Check

    Click the Check code button.

    Improve

    Use the results to improve your PHP code.

    Get your PHP code bug-free and secure right from the IDE

    This free code checker can find critical vulnerabilities and security issues in PHP code bases with a click. To take your application security to the next level, we recommend using Snyk Code for free right from your IDE.

    Improved PHP code security powered by Snyk Code

    This free web based PHP code checker is powered by Snyk Code. Sign up now to get access to all the features including vulnerability alerts, real time scan results, and actionable fix advice within your IDE.

    Human-in-the-Loop PHP Code Checker

    Snyk Code is an expert-curated, AI-powered PHP code checker that analyzes your code for security issues, providing actionable advice directly from your IDE to help you fix vulnerabilities quickly.

    Real-time

    Scan and fix source code in minutes.

    Actionable

    Fix vulns with dev friendly remediation.

    Integrated in IDE

    Find vulns early to save time & money.

    Ecosystems

    Integrates into existing workflow.

    More than syntax errors

    Comprehensive semantic analysis.

    AI powered by people

    Modern ML directed by security experts.

    In-workflow testing

    Automatically scan every PR and repo.

    CI/CD security gate

    Integrate scans into the build process.

    Frequently asked questions

    Время на прочтение
    10 мин

    Количество просмотров 12K

    Моя самая любимая часть в статическом анализе кода — это выдвижение гипотез о потенциальных ошибках в коде с последующей их проверкой.

    Пример гипотезы:

    Функции strpos легко передать аргументы в неправильном порядке. 

    Но есть вероятность, что даже на нескольких миллионах строк кода подобная диагностика не «выстрелит», поэтому на неудачные гипотезы тратить много времени не хочется.

    Сегодня я покажу как выполнять простейший статический анализ с помощью утилиты phpgrep без написания кода.

    Предпосылки

    Вот уже несколько месяцев я занимаюсь поддержкой PHP линтера NoVerify (почитать о котором можно в статье NoVerify: линтер для PHP от Команды ВКонтакте).

    Время от времени в команде появляются идеи для новых диагностик. Идей может быть много, а проверить хочется все, особенно если предложенная проверка нацелена на выявление критических дефектов.

    Ранее я активно разрабатывал go-critic и ситуация была схожей, с той лишь разницей, что анализировались исходники на Go, а не на PHP. Когда я узнал об утилите gogrep, мой мир перевернулся. Как видно из названия, эта утилита имеет что-то общее с grep’ом, только поиск производится не по регулярным выражениям, а по синтаксическим шаблонам (позже объясню, что это значит).

    Я уже не хотел жить без умного grep, поэтому в один вечер решил сесть и написать phpgrep.

    Анализируемый корпус

    Чтобы было увлекательно, мы сразу погрузимся в применение. Будем анализировать небольшой набор достаточно известных и крупных PHP проектов, доступных на GitHub.

    В наш набор попали следующие проекты:

    Для людей, которые замышляют то, что мы с вами замышляем, это очень аппетитный набор.

    Итак, поехали!

    Использование присваивания в качестве выражения

    Если присваивание используется как выражение, причём:

    • контекст ожидает результат логической операции (логическое условие) и
    • правая часть выражения не имеет побочных эффектов и является константной,

    то, скорее всего, в коде ошибка.

    Для начала, возьмём за «логический контекст» следующие конструкции:

    1. Выражение внутри «if ($cond)«.
    2. Условие тернарного оператора: «$cond ? $x : $y«.
    3. Условия продолжений циклов «while ($cond)» и «for ($init; $cond; $post)«.

    В правой части присваивания мы ожидаем константы или литералы.

    Зачем на нужны такие ограничения?

    Начнём с (1):

    # утилита поиска по синтаксическим шаблонам
    # |     производим поиск по текущей директории (и всем дочерним)
    # |     |
    # |     |
    phpgrep . 'if ($_ = []) $_' # 1
    #         |               
    #         |               
    #         строка шаблона поиска
    
    # Дополнительные 3 шаблона через отдельные запуски.
    phpgrep . 'if ($_ = ${"const"}) $_' # 2
    phpgrep . 'if ($_ = ${"str"}) $_'   # 3
    phpgrep . 'if ($_ = ${"num"}) $_'   # 4

    Здесь мы видим 4 шаблона, единственным различием между которыми выступает присваиваемое выражение (RHS). Начнём с первого из них.

    Шаблон «if ($_ = []) $_» захватывает if, у которого любому выражению присваивается пустой массив. $_ сопоставляется с любым expression или statement.

             литерал пустого массива (RHS)
             |
    if ($_ = []) $_
        |        |
        |        любое тело if'а, причём не важно, с {} или без
        любой LHS у присваивания

    В следующих примерах используются более сложные группы const, str и num. В отличие от $_ они описывают ограничения на совместимые операции.

    • const — именованная константа или константа класса.
    • str — строковой литерал любого типа.
    • num — числовой литерал любого типа.

    Этих шаблонов достаточно, чтобы добиться нескольких срабатываний на корпусе.

    ⎆ moodle/blocks/rss_client/viewfeed.php#L37:

    if ($courseid = SITEID) {
        $courseid = 0;
    }

    Вторым срабатыванием в moodle стала зависимость ADOdb. В upstream библиотеки проблема всё ещё присутствует.

    ⎆ ADOdb/drivers/adodb-odbtp.inc.php#L741:

    В этом фрагменте прекрасно многое, но для нас релевантна только первая строка. Вместо сравнения поля databaseType мы выполняем присваивание и всегда входим внутрь условия.

    Ещё одно интересное место, где мы хотим выполнять действия только для «корректных» записей, но, вместо этого, выполняем их всегда и, более того, отмечаем любую запись как корректную!

    ⎆ moodle/question/format/blackboard_six/formatqti.php#L598:

    // For BB Fill in the Blank, only interested in correct answers.
    if ($response->feedback = 'correct') {
        // ...
    }

    Расширенный список шаблонов для этой проверки

    Повторим то, что мы изучили:

    • Шаблоны выглядят как PHP-код, который они находят.
    • $_ обозначает «что угодно». Можно сравнить с . в регулярных выражениях.
    • ${"<class>"} работает как $_ с ограничением на тип элементов AST.

    Стоит ещё подчеркнуть, что всё, кроме переменных, сопоставляется дословно (literally). Это значит, что шаблону «array(1, 2 + 3)» будет удовлетворять лишь идентичный по синтаксической структуре код (пробелы не влияют). С другой стороны, шаблону «array($_, $_)» удовлетворяет любой литерал массива из двух элементов.

    Сравнение выражения с самим собой

    Потребность сравнить что-либо с самим собой возникает очень редко. Это может быть проверка на NaN, но как минимум в половине случаев это ошибка copy/paste.

    ⎆ Wikia/app/extensions/SemanticDrilldown/includes/SD_FilterValue.php#L103:

    if ( $fv1->month == $fv1->month ) return 0;

    Справа должно быть «$fv2->month«.

    Для выражения повторяющихся частей в шаблоне мы используем переменные с именами, отличными от «_«. Механизм повторений в шаблоне похож на обратные ссылки в регулярных выражениях.

    Шаблон «$x == $x» будет как раз тем, что найдёт пример выше. Вместо «x» может использоваться любое имя. Здесь важно лишь то, чтобы имена были идентичны. Переменные шаблона, которые имеют различающиеся имена, не обязаны иметь совпадающее содержимое при захвате.

    Следующий пример найден с помощью «$x <= $x«.

    ⎆ Drupal/core/modules/views/tests/src/Unit/ViewsDataTest.php#L166:

    $prev = $base_tables[$base_tables_keys[$i - 1]];
    $current = $base_tables[$base_tables_keys[$i]];
    $this->assertTrue(
      $prev['weight'] <= $current['weight'] &&
      $prev['title'] <= $prev['title'], // <-------------- ошибка
      'The tables are sorted as expected.');

    Дублирующиеся подвыражения

    Теперь, когда мы знаем про возможности повторяемых подвыражений, мы можем составить множество любопытных шаблонов.

    Один из моих любимцев — «$_ ? $x : $x«.
    Это тернарный оператор, у которого true/false ветки идентичны.

    ⎆ joomla-cms/libraries/src/User/UserHelper.php#L522:

    return ($show_encrypt) ? '{SHA256}' . $encrypted : '{SHA256}' . $encrypted;

    Обе ветви дублируются, что подсказывает о потенциальной проблеме в коде. Если мы посмотрим на код вокруг, то сможем понять, что там должно было быть вместо этого. В угоду читабельности я вырезал часть кода и сократил название переменной $encrypted до $enc.

    case 'crypt-blowfish':
        return ($show_encrypt ? '{crypt}' : '') . crypt($plaintext, $salt);
    case 'md5-base64':
        return ($show_encrypt) ? '{MD5}' . $enc : $enc;
    case 'ssha':
        return ($show_encrypt) ? '{SSHA}' . $enc : $enc;
    case 'smd5':
        return ($show_encrypt) ? '{SMD5}' . $enc : $enc;
    case 'sha256':
        return ($show_encrypt) ? '{SHA256}' . $enc : '{SHA256}' . $enc;
    default:
        return ($show_encrypt) ? '{MD5}' . $enc : $enc;

    Я бы поставил на то, что коду необходим следующий патч:

    - ($show_encrypt) ? '{SHA256}' . $encrypted : '{SHA256}' . $encrypted;
    + ($show_encrypt) ? '{SHA256}' . $encrypted : $encrypted;

    Опасные приоритеты операций в PHP

    Хорошей мерой предосторожности в PHP является использование группирующих скобочек везде, где важно иметь правильный порядок вычислений.

    Во многих языках программирования выражение «x & mask != 0» имеет интуитивный смысл. Если mask описывает какой-то бит, то данный код проверяет, что в x этот бит не равен нулю. К сожалению, для PHP это выражение будет вычисляться так: «x & (mask != 0)«, что почти всегда не то что вам нужно.

    WordPress, Joomla и moodle используют SimplePie.

    ⎆ SimplePie/library/SimplePie/Locator.php#L254
    ⎆ SimplePie/library/SimplePie/Locator.php#L384
    ⎆ SimplePie/library/SimplePie/Locator.php#L412
    ⎆ SimplePie/library/SimplePie/Sanitize.php#L349
    ⎆ SimplePie/library/SimplePie.php#L1634

    $feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0

    SIMPLEPIE_FILE_SOURCE_REMOTE определён как 1, поэтому выражение будет эквивалентно:

    $feed->method & (1 === 0)
    // =>
    $feed->method & false

    Примеры шаблонов для поиска

    Продолжая тему неожиданных приоритетов операций, вы можете почитать про тернарный оператор в PHP. На хабре этому даже посвятили статью: Порядок выполнения тернарного оператора.

    Можно ли такие места найти с помощью phpgrep? Ответ: да!

    phpgrep . '$_ == $_ ? $_ : $_ ? $_ : $_'
    phpgrep . '$_ != $_ ? $_ : $_ ? $_ : $_'

    Прелести проверки URL с помощью регулярных выражений

    ⎆ Wikia/app/maintenance/wikia/updateCentralInterwiki.inc#L95:

    if ( preg_match( '/(wowwiki.com|wikia.com|falloutvault.com)/', $url ) ) {
        $local = 1;
    } else {
        $local = 0;
    }

    По задумке автора кода, мы проверяем URL на совпадение с одним из 3-х вариантов. К сожалению, символ . не экранирован, что приведёт к тому, что вместо falloutvault.com мы можем завести себе falloutvaultxcom на любом домене и пройти проверку.

    Это не PHP-специфичная ошибка. В любом приложении, где валидация выполняется через регулярные выражения и частью проверяемой строки является мета-символ, есть риск забыть экранирование там, где оно нужно, и получить уязвимость.

    Найти такие места можно с помощью запуска phpgrep:

    phpgrep . 'preg_match(${"pat:str"}, ${"*"})' 'pat~[^\].(com|ru|net|org)b'

    Мы вводим именованный подшаблон pat, который захватывает любой строковой литерал, а затем применяем к нему фильтр из регулярного выражения.

    Фильтры можно применять к любой переменной шаблона. Кроме регулярных выражений есть также структурные операторы = и !=. Полный список можно найти в документации.

    ${"*"} захватывает произвольное количество любых аргументов, поэтому нам можно не волноваться за опциональные параметры функции preg_match.

    Дубликаты ключей в литерале массива

    В PHP вы не получите никакого предупреждения, если выполните этот код:

    <?php
    var_dump(['a' => 1, 'a' => 2]);
    // Результат: array(1) {["a"]=> int(2)}

    Мы можем найти такие массивы с помощью phpgrep:

    [${"*"}, $k => $_, ${"*"}, $k => $_, ${"*"}]

    Этот шаблон можно расшифровать так: «литерал массива, в котором есть хотя бы два идентичных ключа в произвольной позиции». Выражения ${"*"} помогают нам описать «произвольную позицию», допуская 0-N элементов до, между и после интересующих нас ключей.

    ⎆ Wikia/app/extensions/wikia/WikiaMiniUpload/WikiaMiniUpload_body.php#L23:

    $script_a = [
        'wmu_back' => wfMessage( 'wmu_back' )->escaped(),
        'wmu_back' => wfMessage( 'wmu_back' )->escaped(),
        // ...
    ];

    В данном случае это не является грубой ошибкой, но мне известны случаи, когда дублирование ключей в крупных (100+ элементов) массивах несло как минимум неожиданное поведение, в котором один из ключей перекрывал значение другого.


    На этом наш краткий экскурс на примерах окончен. Если вам хочется ещё, в конце статьи описано, как получить все результаты.

    Что же такое phpgrep?

    Большая часть редакторов и IDE используют для поиска кода (если это не поиск по специальному символу типа класса или переменной) обычный текстовой поиск — проще говоря, что-то вроде grep’а.

    Вы вводите «$x«, находите «$x«. Вам могут быть доступны регулярные выражения, тогда вы можете пытаться, по сути, парсить PHP-код регулярками. Иногда это даже работает, если ищется что-то вполне определённое и простое — например, «любая переменная с некоторым суффиксом». Но если эта переменная с суффиксом должна быть частью другого составного выражения, возникают трудности.

    phpgrep — это инструмент для удобного поиска PHP-кода, который позволяет искать не с помощью text-oriented регулярок, а с помощью syntax-aware шаблонов.

    Syntax-aware означает, что язык шаблонов отражает целевой язык, а не оперирует отдельными символами, как это делают регулярные выражения. Нам также нет никакой разницы до форматирования кода, важна лишь его структура.

    Опциональный контент: Quick Start

    Quick start

    Установка

    Для amd64 есть готовые релизные сборки под Linux и Windows, но если у вас установлен Go, то достаточно одной команды, чтобы получить свежий бинарник под вашу платформу:

    go get -v github.com/quasilyte/phpgrep/cmd/phpgrep

    Если $GOPATH/bin находится в системном $PATH, то команда phpgrep станет сразу же доступной. Чтобы это проверить, попробуйте запустить команду с параметром -help:

    phpgrep -help

    Если же ничего не происходит, найдите, куда Go установил бинарник и добавьте его в переменную окружения $PATH.

    Старый и надёжный способ посмотреть $GOPATH, даже если он не выставлен явно:

    go env GOPATH

    Использование

    Создайте тестовый файл hello.php:

    <?php
    function f(...$xs) {}
    f(10);
    f(20);
    f(30);
    f($x);
    f();

    Запустите на нём phpgrep:

    # phpgrep hello.php 'f(${"x:int"})' 'x!=20'
    hello.php:3: f(10)
    hello.php:5: f(30)

    Мы нашли все вызовы функции f с одним аргументом-числом, значение которого не равно 20.

    Как работает phpgrep

    Для разбора PHP используется библиотека github.com/z7zmey/php-parser. Она достаточно хороша, но некоторые ограничения phpgrep следуют из особенностей используемого парсера. Особенно много трудностей возникает при попытках нормально работать со скобочками.

    Принцип работы phpgrep прост:

    • строится AST из входного шаблона, разбираются фильтры;
    • для каждого входного файла строится полное AST-дерево;
    • обходим AST каждого файла, пытаясь найти такие поддеревья, которые совпадают с шаблоном;
    • для каждого результата применяется список фильтров;
    • все результаты, которые прошли фильтры, печатаются на экран.

    Самое интересное — это как именно сопоставляются на равенство два AST-узла. Иногда тривиально: один-к-одному, а мета-узлы могут захватывать более одного элемента. Примерами мета-узлов является ${"*"} и ${"str"}.

    Заключение

    Было бы нечестно говорить о phpgrep, не упомянув structural search and replace (SSR) из PhpStorm. Они решают похожие задачи, причём у SSR есть свои преимущества, например, интеграция в IDE, а phpgrep может похвастаться тем, что является standalone программой, которую гораздо проще поставить, например, на CI.

    Помимо прочего, phpgrep — это ещё и библиотека, которую можно использовать в своих программах для матчинга PHP кода. Особенно это полезно для линтеров и кодогенерации.

    Буду рад, если этот инструмент будет вам полезен. Если же эта статья просто мотивирует вас посмотреть в сторону вышеупомянутого SSR, тоже хорошо.

    Дополнительные материалы

    Полный список шаблонов, который был использован для анализа, можно найти в файле patterns.txt. Рядом с этим файлом можно найти скрипт phpgrep-lint.sh, упрощающий запуск phpgrep со списком шаблонов.

    В статье не дан полный список срабатываний, но вы можете воспроизвести эксперимент, произведя клонирование всех названых репозиториев и запустив phpgrep-lint.sh на них.

    Черпать вдохновение на шаблоны проверок можно, например, из статей PVS studio. Мне очень понравилась Logical Expressions: Mistakes Made by Professionals, которая трансформируется во что-то такое:

    # Для "x != y || x != z":
    phpgrep . '$x != $a || $x != $b'
    phpgrep . '$x !== $a || $x != $b'
    phpgrep . '$x != $a || $x !== $b'
    phpgrep . '$x !== $a || $x !== $b'

    Вам также может быть интересна презентация phpgrep: syntax-aware code search.

    В статье используются изображения гоферов, которые были созданы через gopherkon.

    Понравилась статья? Поделить с друзьями:
  • Как найти предельная ошибка выборки
  • Как найти пакеты с ошибками
  • Как найти ошибку тома диска
  • Как найти ошибку на флешке
  • Как найти ошибку на удаленном сервере