Номер телефона

Последнее обновление:

Как проследить всю цепочку вызовов в скрипте РНР?

Допустим, у Вас на сервере есть масса скриптов PHP, установлен некий фреймворк, а то и не один. Зачастую, на сайтах, использующих фреймворки, запрос от клиента (например, из браузера) передается к файлу index.php. А вот из него уже – как говорится, во многие веси. И кто же его знает, в какой последовательности и какие конкретно файлы РНР используются при этом. Как это узнать? Рассмотрим несколько способов.

1. Вручную

Самый примитивный способ – это, да, вручную. Открываем каждый файл, смотрим, где там директивы типа include, require, затем открываем файлы, указанные в этих директивах и т.д. Однако, в современных фреймворках таких файлов бывает множество. Часть из них являются исполняемыми, а часть – содержат разные настройки, устанавливают параметры, задают классы и др. И если вручную перебирать их все – на это может быть затрачен не один рабочий день. Т.е. ручной способ можно применять, разве что, для очень небольших проектов.

2. Смотреть логи фреймворков

Многие (если не все) современные фреймрворки позволяют вести логи, т.е. записывают информацию о происходящих процессах в специальные файлы. По умолчанию, туда обычно пишется информация об ошибках, сбоях при работе скриптов РНР. Но, если установить самый подробный уровень логирования (info), то тогда в лог будет сохраняться вся основная информация, происходящая с момента получения клиентского запроса сервером и до момента отправки последнего сообщения сервера клиенту.

Однако, даже при подробном уровне логирования в логи все-таки не включается информация о ВСЕХ подключаемых в процессе обработки клиентского вызова файлах со скриптами РНР. Т.е. логи фреймворков хороши для получения общей информации об обработке вызова (по крайней мере, так обстоит дело с фреймворком laravel). А для детализации все же необходима более подробная информация.

3. Добавить небольшой код в index.php

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

Для этого следует использовать функцию get_included_files().

Кстати, у нее есть псевдоним (алиас): get_required_files().

Для наглядности, сразу рассмотрим практический пример. Пусть есть следующие четыре файла: test.php, t1.php, t2.php, t3.php

test.php:

  1. <?php
  2. function shutdown() {
  3. var_export(get_included_files());
  4. echo 'Script ended';
  5. }
  6. register_shutdown_function('shutdown');
  7. require_once './t1.php';

t1.php:

  1. <?php
  2. include './t2.php';
  3. echo 't1 ';

t2.php:

  1. <?php
  2. // die('fin');
  3. require './t3.php';
  4. echo 't2 ';

t3.php:

  1. <?php
  2. echo 't3 ';

Вот такие простейшие четыре файла. Как видим, в test.php объявлена функция shutdown(), в которой, в свою очередь, использован вызов get_included_files(). Эта функция вызывается через процедуру register_shutdown_function(). Это означает, что shutdown выполнится после того, как скрипт закончит работу.

Примечание: если не использовать register_shutdown_function, то в случае прекращения работы скрипта в результате срабатывания команд die, exit или т.п. управление НЕ вернется в файл test.php и, соответственно, функция get_included_file НЕ сработает.

Кроме того, в этом файле подключен файл t1.php (через директиву require_once), находящийся в том же самом каталоге, что и test.php.

Вообще, подключение файлов (со скриптами) в языке РНР может быть сделано при помощи директив include, require, include_once, require_once. Эти директивы немного различаются по своей функциональности, здесь мы не будем на этом останавливаться. Отметим лишь, что каждый из этих способов подключения будет обнаружен при помощи функции get_included_files().

В свою очередь, в файле t1.php сделано подключение файла t2.php, а в этом файле уже подключен файл t3.php.

Что же, попробуем запустить на выполнение файл test.php. Набрав в браузере что-то типа http://site.ru/test.php (если этот файл расположен в корневом каталоге). И вот что получится:

  1. t3 t2 t1 array (
  2. 0 => 'C:\\home\\site.ru\\www\\TEST\\test.php',
  3. 1 => 'C:\\home\\site.ru\\www\\TEST\\t1.php',
  4. 2 => 'C:\\home\\site.ru\\www\\TEST\\t2.php',
  5. 3 => 'C:\\home\\site.ru\\www\\TEST\\t3.php',
  6. )Script ended

Как видим, вначале сервер выдал t3, t2, t1, а потом – перечень подключенных файлов ко ВСЕМ скриптам РНР, выполнявшимся в процессе обработки запроса. Причем, сам test.php также оказался в этом перечне.

Обратите внимание, что выведены именно полные пути к файлам, а не их URL.

Что будет, если какой-нибудь из промежуточных подключенных файлов завершит работу?

Давайте проверим. Например, добавим в t2.php функцию die(), т.е. раскомментируем ее:

  1. t2.php:
  2. <?php
  3. die('fin');
  4. require './t3.php';
  5. echo 't2 ';

Вот что получится:

  1. fin
  2. array (
  3. 0 => 'C:\\home\\site.ru\\www\\TEST\\test.php',
  4. 1 => 'C:\\home\\site.ru\\www\\TEST\\t1.php',
  5. 2 => 'C:\\home\\site.ru\\www\\TEST\\t2.php',
  6. )Script ended

Как видим, выполнение скрипта не дошло до файла t3.php. Так как выполнилась команда die('fin'), а команда require './t3.php' выполниться уже не смогла, соответственно, файл t3.php подключиться не смог. Поэтому он и не попал в перечень подключенных файлов.

Можно немного разнообразить ситуацию. Например, добавим условие:

t2.php:

  1. <?php
  2. //die('fin');
  3. if(0)
  4. require './t3.php';
  5. echo 't2 ';

При этом файл t3.php не будет подключаться, но echo 't2 ' – сработает. Получим:

  1. t2 t1 array (
  2. 0 => 'C:\\home\\site.ru\\www\\TEST\\test.php',
  3. 1 => 'C:\\home\\site.ru\\www\\TEST\\t1.php',
  4. 2 => 'C:\\home\\site.ru\\www\\TEST\\t2.php',
  5. )Script ended

Да, как и ожидалось: раз t3.php не подключался, то, соответственно, он и не присутствует в перечне подключенных файлов при работе РНР-скрипта.

Выводы

Таким образом, добавление небольшой конструкции в самый первый РНР-файл, которым сервер начинает обрабатывать запрос клиента, позволяет проследить всю цепочку подключенных файлов, причем, в их «хронологическом» порядке. Т.е. именно в том порядке, в каком они подключались фактически и, соответственно, выполнялись.

Это позволит облегчить анализ работы сложного скрипта РНР, например, в условиях работы какого-нибудь незнакомого фреймворка.

Также обратим внимание, что при этом вообще не использовалось никаких отладчиков (типа Xdebug), соответственно, не требовалась их установка на сервер.


Как вывести перечень названий всех функций в РНР-скриптах?

Современные сайты, выполненные на языке РНР, представляют собой достаточно сложные, объемные решения. Отчасти это вызвано все усложняющимся функционалом сайтов, а отчасти – и тем, что они, как правило, выполняются в составе той или иной среды – фреймворка. Могут также подключаться дополнительные библиотеки, функции. И иногда, при отладке работы сайта, требуется выяснить – какие функции и в каком порядке выполняются; какие классы реализованы. Посмотрим, как вывести на экран перечень всех функций, реализованных в выполняющихся скриптах РНР.

Вот короткий программный код, который можно вставить, по сути, в любое место выполняющихся скриптов. Проще всего вставить в файл типа index.php (в любое место):

  1. $funct = get_defined_functions();
  2. var_export($funct['user']);

Здесь используется функция get_defined_functions, которая выводит создает массив из имен ВСЕХ функций, которые используются в выполняющихся PHP-скриптах. В том числе – и пользовательских, т.е. которые сформированы программистом – разработчиком сайта. Это – ассоциативный массив массивов, имеющий два индекса:

  1. ‘internal’,
  2. ‘user’.

Индекс ‘user’ содержит только пользовательские функции. Если же вывести массив без индексов вообще, то выведутся и системные (внутренние) функции тоже. Причем, к сожалению, выводятся они НЕ в порядке алфавита. Их количество довольно велико. Так, например, в Denwer (PHP 5.3.29) вывелось 1387 названий внутренних функций. Понятно, что в РНР7/8 их будет еще больше.

Тогда как по индексу ‘user’ будут выведены только те функции, которые написаны программистами и, самое главное, которые использовались в процессе выполнения скрипта. Так, например, если в скрипте будет конструкция типа

  1. if(0) {
  2. function f1() {…};
  3. }

то функция f1 отображена не будет. Так как она не будет выполняться.

А как насчет классов?

В рамках ООП (объектно-ориентированное программирование) использование классов, в том числе многоуровневых, типичная практика. Чтобы вывести перечень классов (по аналогии с перечнем функций), можно применять следующий код:

  1. $classes = get_declared_classes();
  2. var_export($classes);

Название классов будут выводиться, в том числе, и для внутренних, т.е. встроенных. Например, в Denwer их вывелось 136.

Константы

Перечень используемых в скриптах констант тоже можно вывести, используя функцию

get_defined_constants()

Которая задает ассоциативный массив, содержащий все константы, в том числе, и пользовательские (т.е. введенные программистами).

Переменные

Чтобы вывести перечень всех переменных, применяющихся в скриптах, можно воспользоваться функцией

get_defined_vars()

Она возвращает ассоциативный массив, в который войдут все переменные - переменные окружения, серверные переменные, а также переменные, определенные пользователем (программистом).

Таким образом, зная информацию о функциях, классах, переменных, константах, можно, по сути, составить, своего рода, «карту» выполняющегося скрипта. Впрочем, для этой цели есть и готовые отладчики, тот же Xdebug.


Комментарии:
Всего комментариев:0
Пожалуйста, не забудьте ознакомиться с правилами оставления комментариев.



Подписаться на комментарии на этой странице

Мы можем выполнить

Другие услуги
Интересная и полезная
информация
НАПИШИТЕ НАМ
Яндекс.Метрика
Номер телефона
© Copyright Все права защищены 2013-2024 Научный консалтинг