Использование print_r и var_dump с круговой ссылкой

Я использую структуру MVC Symfony , и кажется, что многие встроенные объекты, которые я хочу отлаживать, имеют циклические ссылки. Это делает невозможным печать переменных с помощью print_r() или var_dump() (поскольку они следуют циклическим ссылкам до бесконечности или до тех пор, пока в процессе не закончится память, в зависимости от того, что наступит раньше).

Вместо того, чтобы писать собственный клон print_r с некоторым интеллектом, есть ли там лучшие альтернативы? Я хочу иметь возможность печатать переменную (объект, массив или скаляр), либо в файле журнала, либо в заголовке http, либо самой веб-странице.

Изменить: чтобы выяснить, в чем проблема, попробуйте этот код:

 <?php class A { public $b; public $c; public function __construct() { $this->b = new B(); $this->c = new C(); } } class B { public $a; public function __construct() { $this->a = new A(); } } class C { } ini_set('memory_limit', '128M'); set_time_limit(5); print_r(new A()); #var_dump(new A()); #var_export(new A()); 

Он не работает с print_r() , var_dump() или var_export() . Сообщение об ошибке:

PHP Неустранимая ошибка: допустимый размер памяти 134217728 байт исчерпан (пытался выделить 523800 байт) в print_r_test.php в строке 10

Мы используем PRADO Framework, и у него есть встроенный класс под названием «TVarDumper», который может обрабатывать такие сложные объекты довольно хорошо – он даже может отформатировать его в хорошем HTML, включая. Подсветка синтаксиса. Вы можете получить этот класс из ЗДЕСЬ .

У доктрины есть тот же класс обслуживания.

Пример использования:

 <?php echo "<pre>"; \Doctrine\Common\Util\Debug::dump($result, 4); echo "</pre>";?> 

Вы можете использовать var_export() .

var_export () не обрабатывает циклические ссылки, так как было бы невозможно создать для него разборный PHP-код. Если вы хотите что-то сделать с полным представлением массива или объекта, используйте serialize ().

ОБНОВЛЕНИЕ: Похоже, я был неправ. Я думал, что использовал эту функцию некоторое время назад для этой цели, но это, должно быть, было пьяное воображение.

Таким образом, единственный совет, который я могу дать, – это установка Xdebug .

 class Test { public $obj; } $obj = new Test(); $obj->obj = $obj; print_r($obj); var_dump($obj); 

Вывод:

 Test Object ( [obj] => Test Object *RECURSION* ) object(Test)[1] public 'obj' => &object(Test)[1] 

Мне кажется, что как print_r() и var_dump() могут обрабатывать рекурсию без проблем. Использование PHP 5.3.5 в Windows.


var_export() не обнаруживает рекурсию, что приводит к мгновенной фатальной ошибке:

 Fatal error: Nesting level too deep - recursive dependency? in \sandbox\index.php on line 28 

TVarDumper

TVarDumper предназначен для замены багги-функции PHP var_dump и print_r , так как он может корректно идентифицировать рекурсивно связанные объекты в сложной структуре объекта. Он также имеет рекурсивный контроль глубины, чтобы избежать неопределенного рекурсивного отображения некоторых особых переменных.

Проверить TVarDumper.php :

 <?php /** * TVarDumper class file * * @author Qiang Xue <qiang.xue@gmail.com> * @link http://www.pradosoft.com/ * @copyright Copyright &copy; 2005-2013 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id$ * @package System.Util */ /** * TVarDumper class. * * TVarDumper is intended to replace the buggy PHP function var_dump and print_r. * It can correctly identify the recursively referenced objects in a complex * object structure. It also has a recursive depth control to avoid indefinite * recursive display of some peculiar variables. * * TVarDumper can be used as follows, * <code> * echo TVarDumper::dump($var); * </code> * * @author Qiang Xue <qiang.xue@gmail.com> * @version $Id$ * @package System.Util * @since 3.0 */ class TVarDumper { private static $_objects; private static $_output; private static $_depth; /** * Converts a variable into a string representation. * This method achieves the similar functionality as var_dump and print_r * but is more robust when handling complex objects such as PRADO controls. * @param mixed variable to be dumped * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. * @return string the string representation of the variable */ public static function dump($var,$depth=10,$highlight=false) { self::$_output=''; self::$_objects=array(); self::$_depth=$depth; self::dumpInternal($var,0); if($highlight) { $result=highlight_string("<?php\n".self::$_output,true); return preg_replace('/&lt;\\?php<br \\/>/','',$result,1); } else return self::$_output; } private static function dumpInternal($var,$level) { switch(gettype($var)) { case 'boolean': self::$_output.=$var?'true':'false'; break; case 'integer': self::$_output.="$var"; break; case 'double': self::$_output.="$var"; break; case 'string': self::$_output.="'$var'"; break; case 'resource': self::$_output.='{resource}'; break; case 'NULL': self::$_output.="null"; break; case 'unknown type': self::$_output.='{unknown}'; break; case 'array': if(self::$_depth<=$level) self::$_output.='array(...)'; else if(empty($var)) self::$_output.='array()'; else { $keys=array_keys($var); $spaces=str_repeat(' ',$level*4); self::$_output.="array\n".$spaces.'('; foreach($keys as $key) { self::$_output.="\n".$spaces." [$key] => "; self::$_output.=self::dumpInternal($var[$key],$level+1); } self::$_output.="\n".$spaces.')'; } break; case 'object': if(($id=array_search($var,self::$_objects,true))!==false) self::$_output.=get_class($var).'#'.($id+1).'(...)'; else if(self::$_depth<=$level) self::$_output.=get_class($var).'(...)'; else { $id=array_push(self::$_objects,$var); $className=get_class($var); $members=(array)$var; $keys=array_keys($members); $spaces=str_repeat(' ',$level*4); self::$_output.="$className#$id\n".$spaces.'('; foreach($keys as $key) { $keyDisplay=strtr(trim($key),array("\0"=>':')); self::$_output.="\n".$spaces." [$keyDisplay] => "; self::$_output.=self::dumpInternal($members[$key],$level+1); } self::$_output.="\n".$spaces.')'; } break; } } } . <?php /** * TVarDumper class file * * @author Qiang Xue <qiang.xue@gmail.com> * @link http://www.pradosoft.com/ * @copyright Copyright &copy; 2005-2013 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id$ * @package System.Util */ /** * TVarDumper class. * * TVarDumper is intended to replace the buggy PHP function var_dump and print_r. * It can correctly identify the recursively referenced objects in a complex * object structure. It also has a recursive depth control to avoid indefinite * recursive display of some peculiar variables. * * TVarDumper can be used as follows, * <code> * echo TVarDumper::dump($var); * </code> * * @author Qiang Xue <qiang.xue@gmail.com> * @version $Id$ * @package System.Util * @since 3.0 */ class TVarDumper { private static $_objects; private static $_output; private static $_depth; /** * Converts a variable into a string representation. * This method achieves the similar functionality as var_dump and print_r * but is more robust when handling complex objects such as PRADO controls. * @param mixed variable to be dumped * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. * @return string the string representation of the variable */ public static function dump($var,$depth=10,$highlight=false) { self::$_output=''; self::$_objects=array(); self::$_depth=$depth; self::dumpInternal($var,0); if($highlight) { $result=highlight_string("<?php\n".self::$_output,true); return preg_replace('/&lt;\\?php<br \\/>/','',$result,1); } else return self::$_output; } private static function dumpInternal($var,$level) { switch(gettype($var)) { case 'boolean': self::$_output.=$var?'true':'false'; break; case 'integer': self::$_output.="$var"; break; case 'double': self::$_output.="$var"; break; case 'string': self::$_output.="'$var'"; break; case 'resource': self::$_output.='{resource}'; break; case 'NULL': self::$_output.="null"; break; case 'unknown type': self::$_output.='{unknown}'; break; case 'array': if(self::$_depth<=$level) self::$_output.='array(...)'; else if(empty($var)) self::$_output.='array()'; else { $keys=array_keys($var); $spaces=str_repeat(' ',$level*4); self::$_output.="array\n".$spaces.'('; foreach($keys as $key) { self::$_output.="\n".$spaces." [$key] => "; self::$_output.=self::dumpInternal($var[$key],$level+1); } self::$_output.="\n".$spaces.')'; } break; case 'object': if(($id=array_search($var,self::$_objects,true))!==false) self::$_output.=get_class($var).'#'.($id+1).'(...)'; else if(self::$_depth<=$level) self::$_output.=get_class($var).'(...)'; else { $id=array_push(self::$_objects,$var); $className=get_class($var); $members=(array)$var; $keys=array_keys($members); $spaces=str_repeat(' ',$level*4); self::$_output.="$className#$id\n".$spaces.'('; foreach($keys as $key) { $keyDisplay=strtr(trim($key),array("\0"=>':')); self::$_output.="\n".$spaces." [$keyDisplay] => "; self::$_output.=self::dumpInternal($members[$key],$level+1); } self::$_output.="\n".$spaces.')'; } break; } } } 

XDebug var_dump

Используйте расширение XDebug PHP, и оно обнаружит и проигнорирует циклические ссылки, например:

 echo xdebug_var_dump($object); 

print_r + array_slice

В соответствии с этим постом вы можете попробовать:

 print_r(array_slice($desiredArray, 0, 4)); 

features_var_export

Используйте следующую функцию, которая является частью модуля « Функции » для Drupal ( features.export.inc ):

 /** * Export var function */ function features_var_export($var, $prefix = '', $init = TRUE, $count = 0) { if ($count > 50) { // Recursion depth reached. return '...'; } if (is_object($var)) { $output = method_exists($var, 'export') ? $var->export() : features_var_export((array) $var, '', FALSE, $count+1); } else if (is_array($var)) { if (empty($var)) { $output = 'array()'; } else { $output = "array(\n"; foreach ($var as $key => $value) { // Using normal var_export on the key to ensure correct quoting. $output .= " " . var_export($key, TRUE) . " => " . features_var_export($value, ' ', FALSE, $count+1) . ",\n"; } $output .= ')'; } } else if (is_bool($var)) { $output = $var ? 'TRUE' : 'FALSE'; } else if (is_int($var)) { $output = intval($var); } else if (is_numeric($var)) { $floatval = floatval($var); if (is_string($var) && ((string) $floatval !== $var)) { // Do not convert a string to a number if the string // representation of that number is not identical to the // original value. $output = var_export($var, TRUE); } else { $output = $floatval; } } else if (is_string($var) && strpos($var, "\n") !== FALSE) { // Replace line breaks in strings with a token for replacement // at the very end. This protects whitespace in strings from // unintentional indentation. $var = str_replace("\n", "***BREAK***", $var); $output = var_export($var, TRUE); } else { $output = var_export($var, TRUE); } if ($prefix) { $output = str_replace("\n", "\n$prefix", $output); } if ($init) { $output = str_replace("***BREAK***", "\n", $output); } return $output; } 

Применение:

 echo features_var_export($object); 

Сериализация

Используйте serialize для выгрузки объекта в сериализованном представлении, например:

 echo serialize($object); 

Кодирование JSON

Используйте json_encode для преобразования его в формат JSON, например:

 echo json_encode($object); 

См. Также: Проверка, если переменная содержит циклические ссылки

У меня тоже была эта проблема, и я решил ее, реализовав метод __get (), чтобы сломать опорный круг. Метод __get () вызывается ПОСЛЕ того, что атрибут не найден в объявлении класса. Метод __get () также получает имя отсутствующего атрибута. Используя это, вы можете определить «виртуальные атрибуты», которые работают так же, как обычные, но arent, упомянутые функцией print_r. Вот пример:

 public function __get($name) { if ($name=="echo") { return Zend_Registry::get('textConfig'); } 

}

Мне показалось, что это сделало для меня работу:

 print_r(json_decode(json_encode($value)));