Когда только начинал работать - выводил просто var_dump() и все. Далее узнал о функции print_r() - стал чередовать эти 2 функции.
Потом, когда начались работы по доработкам на боевых сайтах - потребовалась проверка того, чтобы диагностические сообщения были доступны только админам - конструкция обросла еще и проверкой того, что пользователь админ (с периодической заменой проверки на админа, на проверку на конкретный ID-пользователя).
И вот, уже больше года пользуюсь своей собственной функцией:
function pre($var,$stop=false,$inconsole = false, $UID = false){
$bt = debug_backtrace();
$bt = $bt[0];
$dRoot = $_SERVER["DOCUMENT_ROOT"];
$dRoot = str_replace("/", "\\", $dRoot);
$bt["file"] = str_replace($dRoot, "", $bt["file"]);
$dRoot = str_replace("\\", "/", $dRoot);
$bt["file"] = str_replace($dRoot, "", $bt["file"]);
if ($GLOBALS['USER']->IsAdmin()){
if($UID && intval($UID)!==$GLOBALS['USER']->GetID()) return;
if($inconsole){
echo "<script>console.log('File: ".$bt['file']." [".$bt['line']."]');console.log(".json_encode($var).");</script>";
}
else{
echo '<div style="padding:3px 5px; background:#99CCFF; font-weight:bold;">File: '.$bt["file"].' ['.$bt["line"].']</div>';
echo '<pre>';
((is_array($var) || is_object($var)) ? print_r($var) : var_dump($var));
echo '</pre>';
}
if($stop) exit(0);
}
}
$var - переменная для анализа; $stop - флаг, который при необходимости останавливает выполненеи дальнейших операций после данной функции; $inconsole - при истинном значении вывод осуществляется не в окно пользователю, а в консоль браузера; $UID - используется для вывода диагностического сообщения только пользователю с заданным ID.
Функция эта уже давным давно не актуальна, давно уже использую для этих целей полноценный Debug-класс. Вот модифицированная данная функция:
Дебаг-функция в пределах дебаг-класса
Подключаем класс в init.php:
CModule::AddAutoloadClasses('', ['\Pai\CDev' => '/local/php_interface/include/classes/CDev.php',]);
И по указанному пути размещаем сам файл с классом "CDev":
namespace Pai;
use ReflectionClass;
class CDev
{
...
/**
* Улучшенная debug-функция с поддержкой eval()
*
* @param mixed $var Переменная для отладки
* @param array $options Дополнительные опции:
* - 'depth' => int Глубина вложенности (по умолчанию 3)
* - 'showHidden' => bool Показывать скрытые свойства (по умолчанию false)
* - 'stop' => bool Остановить выполнение после вывода (по умолчанию false)
* - 'toConsole' => bool Выводить в консоль браузера (по умолчанию false)
* - 'onlyForUser' => int|false Показывать только для пользователя с указанным ID (по
* умолчанию false - для всех)
* - 'rawOutput' => bool Выводить как raw HTML (по умолчанию false, автоматически
* определяется)
*/
public static function debug($var, array $options = [])
{
// Настройки по умолчанию
$defaults = [
'depth' => 3,
'showHidden' => false,
'stop' => false,
'toConsole' => false,
'onlyForUser' => false,
'rawOutput' => null, // Автоматическое определение
];
$options = array_merge($defaults, $options);
// Автоматическое определение rawOutput
if ($options['rawOutput'] === null) {
// Определяем контекст выполнения
$isHttp = isset($_SERVER['HTTP_HOST']) || isset($_SERVER['REQUEST_URI']);
$isCli = php_sapi_name() === 'cli' || defined('STDIN');
$isEval = false;
// Проверяем, выполняется ли код через eval()
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
foreach ($backtrace as $trace) {
if (isset($trace['function']) && $trace['function'] === 'eval') {
$isEval = true;
break;
}
}
// Логика определения формата вывода
$options['rawOutput'] = ($isHttp && !$isCli && !$isEval);
}
if ($options['toJson']) {
header('Content-Type: application/json');
echo json_encode(['debug' => $var, 'trace' => $backtrace], JSON_PRETTY_PRINT);
exit;
}
// Проверка прав доступа
global $USER;
if ($options['onlyForUser'] !== false &&
(!$USER->IsAuthorized() || $USER->GetID() != $options['onlyForUser'])) {
return;
}
// Получаем информацию о месте вызова
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
$file = str_replace($_SERVER['DOCUMENT_ROOT'], '', $backtrace['file']);
$line = $backtrace['line'];
// Формируем заголовок с информацией о месте вызова
$header = sprintf(
'Debug: %s [Line: %d] [Type: %s]',
$file,
$line,
is_object($var) ? get_class($var) : gettype($var)
);
// Если вывод в консоль браузера
if ($options['toConsole']) {
$output = [
'header' => $header,
'data' => $var,
'backtrace' => $backtrace
];
echo '';
if ($options['stop']) {
die();
}
return;
}
// Подготовка вывода
ob_start();
if ($options['rawOutput']) {
// Вывод как HTML
echo '';
echo '' . $header . '';
echo '';
self::printDebugVariable($var, $options['depth'], $options['showHidden'], 0, true);
echo '';
// JavaScript для переключения видимости
echo '';
} else {
// Вывод как текст (для eval и CLI)
echo "=== DEBUG ===\n";
echo $header . "\n";
self::printDebugVariable($var, $options['depth'], $options['showHidden'], 0, false);
echo "\n=============\n";
}
$output = ob_get_clean();
// Для AJAX-запросов устанавливаем правильный Content-Type
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
header('Content-Type: ' . ($options['rawOutput'] ? 'text/html' : 'text/plain') . '; charset=utf-8');
}
echo $output;
if ($options['stop']) {
die();
}
}
/**
* Рекурсивная функция для вывода переменной
*/
protected static function printDebugVariable($var, $depth = 3, $showHidden = false, $level = 0, $isHtml = true)
{
if ($depth <= 0) {
if ($isHtml) {
echo '[max depth reached]';
} else {
echo '[max depth reached]';
}
return;
}
$type = gettype($var);
if ($isHtml) {
// HTML-вывод
switch ($type) {
case 'string':
echo '"' . htmlspecialchars($var) . '"';
echo ' (string, length: ' . strlen($var) . ')';
break;
case 'integer':
echo '' . $var . '';
echo ' (integer)';
break;
case 'double':
echo '' . $var . '';
echo ' (float)';
break;
case 'boolean':
echo '' . ($var ? 'true' : 'false') . '';
echo ' (boolean)';
break;
case 'NULL':
echo 'null';
echo ' (null)';
break;
case 'array':
echo 'array (count: ' . count($var) . ')';
if (!empty($var)) {
$id = uniqid('array_');
echo ' ▶';
echo '';
}
break;
case 'object':
$class = get_class($var);
echo 'object(' . $class . ')';
$id = uniqid('object_');
echo ' ▶';
echo '';
break;
default:
echo '' . print_r($var, true) . '';
echo ' (' . $type . ')';
}
} else {
// Текстовый вывод (для eval и CLI)
switch ($type) {
case 'string':
echo '"' . $var . '" (string, length: ' . strlen($var) . ')';
break;
case 'integer':
echo $var . ' (integer)';
break;
case 'double':
echo $var . ' (float)';
break;
case 'boolean':
echo ($var ? 'true' : 'false') . ' (boolean)';
break;
case 'NULL':
echo 'null (null)';
break;
case 'array':
echo 'array (count: ' . count($var) . ')';
if (!empty($var)) {
echo "\n";
foreach ($var as $key => $value) {
echo str_repeat(' ', $level + 1) . '[' . $key . '] => ';
self::printDebugVariable($value, $depth - 1, $showHidden, $level + 1, $isHtml);
echo "\n";
}
}
break;
case 'object':
$class = get_class($var);
echo 'object(' . $class . ')';
echo "\n";
// Выводим свойства объекта
$reflection = new ReflectionClass($var);
$properties = $reflection->getProperties();
foreach ($properties as $property) {
$property->setAccessible(true);
if (!$showHidden && $property->isPrivate()) {
continue;
}
echo str_repeat(' ', $level + 1);
echo $property->isPrivate() ? 'private ' :
($property->isProtected() ? 'protected ' : 'public ');
echo '$' . $property->getName() . ' = ';
self::printDebugVariable(
$property->getValue($var),
$depth - 1,
$showHidden,
$level + 1,
$isHtml
);
echo "\n";
}
break;
default:
echo print_r($var, true) . ' (' . $type . ')';
}
}
}
...
}
Обращение к функции идет в таком виде:
// В обычном веб-контексте (HTML) Pai\CDev::debug($variable); // В eval() или CLI (текстовый вывод) Pai\CDev::debug($variable, ['rawOutput' => false]); // С максимальной глубиной 5 Pai\CDev::debug($variable, ['depth' => 5]); // С выводом скрытых свойств Pai\CDev::debug($variable, ['showHidden' => true]);

