【问题标题】:Using print_r and var_dump with circular reference将 print_r 和 var_dump 与循环引用一起使用
【发布时间】:2011-09-11 15:20:44
【问题描述】:

我正在使用MVC framework 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 致命错误:第 10 行的 print_r_test.php 中允许的内存大小为 134217728 字节已用尽(尝试分配 523800 字节)

【问题讨论】:

  • 没有得到它... PHP 5.3.5 抛出“致命错误:达到 '100' 的最大函数嵌套级别,正在中止!”在B 的构造函数中,创建了新的A,这又创建了新的B,这又创建了新的A 等。
  • 尝试在结果上不使用print_r() 的情况下创建新的A,即使用$obj = new A() 而不是print_r(new A())。你仍然遇到同样的错误吗?

标签: php debugging circular-reference


【解决方案1】:

Doctrine 具有相同的服务等级。

使用示例:

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

【讨论】:

【解决方案2】:

我们使用的是 PRADO 框架,它有一个名为“TVarDumper”的内置类,它可以很好地处理这些复杂的对象——它甚至可以用漂亮的 HTML 格式化它,包括。语法高亮。您可以从HERE 获取该课程。

【讨论】:

  • 与 Drupal 完美配合!
【解决方案3】:

TVarDumper

TVarDumper 旨在替换有缺陷的 PHP 函数 var_dumpprint_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;
        }
    }
}

XDebug var_dump

使用 XDebug PHP 扩展,它会检测并忽略循环引用,例如:

echo xdebug_var_dump($object);

print_r + array_slice

按照这个post,你可以试试:

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

features_var_export

使用以下函数,它是 Drupal (features.export.inc) 的 Features 模块的一部分:

/**
 * 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);

另见:Test if variable contains circular references

【讨论】:

    【解决方案4】:

    你可以使用var_export()

    var_export() 不处理循环 参考,因为它会接近 无法生成可解析的 PHP 代码。如果你想做 具有完整代表性的东西 数组或对象,使用 连载()。

    更新:好像我错了。之前还以为是为了这个目的才用过这个功能,没想到是醉了。

    这样,我能给出的唯一建议就是安装Xdebug

    【讨论】:

    • 我不会使用var_export()。我相信“不处理”意味着“无法检测”或类似的东西。在 PHP 5.3.5 上,var_export() 结果为 Fatal error: Nesting level too deep - recursive dependency?
    • 它不起作用:“致命错误:嵌套级别太深 - 递归依赖?”
    • @binaryLV @Christian 感谢您的澄清。
    • 这篇文章提供了一些见解,它与在不同版本的 php richardlord.net/blog/… 中执行对象的非严格比较的方式有关。
    • “这一定是一些醉酒的想象”——我最近也遇到了鲍尔默巅峰时期的坏事,兄弟。
    【解决方案5】:

    这似乎帮我完成了工作:

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

    【讨论】:

    • 对我有用,不是很整洁,但可以满足我的需要。谢谢!
    【解决方案6】:
    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() 都可以毫无问题地处理递归。在 Windows 上使用 PHP 5.3.5。


    var_export() 没有检测到递归,导致即时致命错误:

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

    【讨论】:

    • 并不总是有效。我面临同样的问题。由于循环引用,无法打印 debug_backtrace() 对象
    • @kervin,你用的是哪个版本的 PHP?
    • 很高兴知道“不总是”是什么时候。对我来说, print_r 直到最近也能正常工作。它只是为递归生成了 RECURSION。但自从一段时间以来(PHP 版本?)这似乎不再起作用了。
    【解决方案7】:

    我也遇到了这个问题,我通过实现 __get() 方法来打破参考圈解决了这个问题。 __get() 方法在类声明中找不到属性后调用。 __get() 方法还获取缺失属性的名称。使用它,您可以定义“虚拟属性”,其工作方式与通常的方式相同,但 print_r 函数未提及。举个例子:

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

    }

    【讨论】:

      【解决方案8】:

      Symfony 现在也有 VarDumer 组件: https://symfony.com/doc/current/components/var_dumper.html

      它处理循环引用并支持远程转储服务器。

      安装非常简单:

      composer require symfony/var-dumper --dev
      

      然后就可以使用全局函数dump(我猜composer的autoload.php已经包含了):

      <?php
      /* ... */
      dump($someVar);
      

      【讨论】:

        猜你喜欢
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        • 2016-09-15
        • 2016-09-17
        • 2013-01-22
        • 2012-04-20
        • 2016-08-15
        • 2014-04-11
        相关资源
        最近更新 更多