使用带有循环引用的 print_r 和 var_dump

发布于 2024-11-14 12:52:14 字数 971 浏览 2 评论 0 原文

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

I'm using the MVC framework Symfony, and it seems a lot of the built-in objects I want to debug have circular references. This makes it impossible to print the variables with print_r() or var_dump() (since they follow circular references ad infinitum or until the process runs out of memory, whichever comes first).

Instead of writing my own print_r clone with some intelligence, are there better alternatives out there? I only want to be able to print a variable (object, array or scalar), either to a log file, http header or the web page itself.

Edit: to clarify what the problem is, try this code:

<?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());

It doesn't work with print_r(), var_dump() or var_export(). The error message is:

PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 523800 bytes) in print_r_test.php on line 10

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

稚然 2024-11-21 12:52:14

Doctrine 具有相同的服务类。

使用示例:

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

Doctrine have the same service class.

Example of usage:

<?php echo "<pre>"; \Doctrine\Common\Util\Debug::dump($result, 4); echo "</pre>";?>
狂之美人 2024-11-21 12:52:14

我们正在使用 PRADO 框架,它有一个名为“TVarDumper”的内置类,它可以很好地处理此类复杂的对象 - 它甚至可以将其格式化为漂亮的 HTML(包括)。语法突出显示。您可以从此处获取该类。

We are using the PRADO Framework and it has a built in class called "TVarDumper" which can handle such complex objects pretty well - it even can format it in nice HTML incl. Syntax Highlighting. You can get that class from HERE.

夜空下最亮的亮点 2024-11-21 12:52:14

TVarDumper

TVarDumper 旨在替换有缺陷的 PHP 函数 < code>var_dump 和 print_r,因为它可以正确识别复杂对象结构中递归引用的对象。它还具有递归深度控制,以避免某些特殊变量的无限递归显示。

检查 TVarDumper.php :

<?php
/**
 * TVarDumper class file
 *
 * @author Qiang Xue <[email protected]>
 * @link http://www.pradosoft.com/
 * @copyright Copyright © 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('/<\\?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):

/**
 * 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 Encode

Use < code>json_encode 将其转换为 JSON 格式,例如:

echo json_encode($object);

另请参阅:测试变量是否包含循环引用

TVarDumper

TVarDumper is intended to replace the buggy PHP function var_dump and print_r, since 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.

Check TVarDumper.php:

<?php
/**
 * TVarDumper class file
 *
 * @author Qiang Xue <[email protected]>
 * @link http://www.pradosoft.com/
 * @copyright Copyright © 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('/<\\?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

Use XDebug PHP extension, and it'll detect and ignore the circular references, e.g.:

echo xdebug_var_dump($object);

print_r + array_slice

As per this post, you may try:

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

features_var_export

Use the following function which is part of Features module for 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;
}

Usage:

echo features_var_export($object);

Serialize

Use serialize to dump the object in serialized representation, e.g.:

echo serialize($object);

JSON Encode

Use json_encode to convert it into JSON format, e.g.:

echo json_encode($object);

See also: Test if variable contains circular references

最美的太阳 2024-11-21 12:52:14

这似乎为我完成了工作:

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

This seemed to get the job done for me:

print_r(json_decode(json_encode($value)));
岁月无声 2024-11-21 12:52:14

<罢工>
您可以使用var_export()。< /罢工>

<罢工>

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


更新: 看来我错了。我以为我不久前使用这个功能就是为了这个目的,但这一定是一些醉酒的想象。

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


You could use var_export().

var_export() does not handle circular
references as it would be close to
impossible to generate parsable PHP
code for that. If you want to do
something with the full representation
of an array or object, use
serialize().

UPDATE: Seems like I was wrong. I thought I used this function a while ago for this purpose, but it must have been some drunken imagination.

This way, the only advice I can give is installing Xdebug.

攒一口袋星星 2024-11-21 12:52:14

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

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

}

I had this problem too and i solved it by implementing the __get() Method to break the reference circle. The __get() Method is called AFTER an attribute isnt found in the class declaration. The __get() Method also gets the name of the missing attribute. Using this you can define "virtual attributes" that work kind of the same way as usual ones but arent mentioned by the print_r function. Here an example:

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

}

寂寞清仓 2024-11-21 12:52:14
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
class Test {
    public $obj;
}
$obj = new Test();
$obj->obj = $obj;
print_r($obj);
var_dump($obj);

Output:

Test Object
(
    [obj] => Test Object
 *RECURSION*
)

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

It seems to me that both print_r() and var_dump() can handle recursion with no problems. Using PHP 5.3.5 on Windows.


var_export() does not detect recursion, which results in instant fatal error:

Fatal error:  Nesting level too deep - recursive dependency? in \sandbox\index.php on line 28
执妄 2024-11-21 12:52:14

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

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

安装非常简单:

composer require symfony/var-dumper --dev

然后你可以使用全局函数dump(我想composer的autoload.php已经包含在内):

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

Symfony nowadays also has VarDumer component:
https://symfony.com/doc/current/components/var_dumper.html

It handles circular references and supports remote dump server.

Installation is pretty easy:

composer require symfony/var-dumper --dev

Then you can use global function dump (I suppose composer's autoload.php is already included):

<?php
/* ... */
dump($someVar);
随心而道 2024-11-21 12:52:14

2022年遇到这个,希望这对某人有帮助;

PHP 有一个神奇的方法,您可以在类中实现来清理递归对象:

__debugInfo()

这是您的函数的一个很好的起点:

public function __debugInfo(){
    // clone the object so you can modify as needed
    $object = clone($this);
    // overwrite properties with meaningful info
    $object->recursive_object_prop = 'Removed for debugging...';
    // return a typecasted array, __debugInfo() requires it.
    return (Array) $object; 
}

您还可以在对结果数组使用 unset() 对 $object 进行类型转换后完全删除属性。

Came across this in 2022, hope this helps someone;

PHP has a magic method you could implement into your class to clean up recursive objects:

__debugInfo()

Here's a good starting point for your function:

public function __debugInfo(){
    // clone the object so you can modify as needed
    $object = clone($this);
    // overwrite properties with meaningful info
    $object->recursive_object_prop = 'Removed for debugging...';
    // return a typecasted array, __debugInfo() requires it.
    return (Array) $object; 
}

You could also remove properties entirely after typecasting $object using unset() on the resulting array.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文