Использование дампа print r и var с круговой ссылкой


Я использую платформу 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

Author: Christian Davén, 2011-06-09

8 answers

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

 11
Author: ChrFin, 2016-04-08 01:03:00

Доктрина имеет тот же класс обслуживания.

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

<?php echo "<pre>"; \Doctrine\Common\Util\Debug::dump($result, 4); echo "</pre>";?>
 12
Author: Alorian, 2016-04-07 15:12:27

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

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

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

Это кстати, единственный совет, который я могу дать, - это установить Xdebug.

 4
Author: kapa, 2011-06-09 12:36:36

Твардампер

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

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

<?php
/**
 * TVarDumper class file
 *
 * @author Qiang Xue <[email protected]>
 * @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 <[email protected]>
 * @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 переменная_дампа

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

echo xdebug_var_dump($object);

print_r + array_slice

Согласно этому сообщению , вы можете попробовать:

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

Особенности_вар_экспорта

Используйте следующую функцию, которая является частью Функций модуля для 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);

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

 4
Author: kenorb, 2017-05-23 11:46:38
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
 2
Author: binaryLV, 2011-06-09 11:47:28

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

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

}

 1
Author: Lucian Depold, 2012-09-21 11:01:27

Это, казалось, сделало всю работу за меня:

print_r(json_decode(json_encode($value)));
 0
Author: fcrick, 2017-03-01 21:16:06

В настоящее время в Symfony также есть компонент VarDumer: https://symfony.com/doc/current/components/var_dumper.html

Он обрабатывает циклические ссылки и поддерживает удаленный сервер дампа.

Установка довольно проста:

composer require symfony/var-dumper --dev

Затем вы можете использовать глобальную функцию dump (я полагаю, что композитор autoload.php уже включено):

<?php
/* ... */
dump($someVar);
 0
Author: Ostin, 2018-09-19 07:56:24