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
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.
Doctrine have the same service class.
Example of usage:
<?php echo "<pre>"; \Doctrine\Common\Util\Debug::dump($result, 4); echo "</pre>";?>
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.
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 <qiang.xue@gmail.com>
* @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 <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('/<\\?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
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
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');
}
}
This seemed to get the job done for me:
print_r(json_decode(json_encode($value)));
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);