Using print_r and var_dump with circular reference
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.
What would cause a print_r and/or a var_dump to fail debugging a variable?
First, PHP never "just white pages". When you get a blank screen, that means PHP's execution has halted fro some reason. However, unless your server has been configured to not log errors, the PHP error log or the Magento exception log should have an error for you.
As far as your specific problem goes, many of Magento's objects contain reference to a large amount of information — and sometimes the references are circular. PHP's var_dump
and print_r
functions will blindly follow these circular references and attempt to print everything out. This eventually leads to PHP using more memory than is allowed by the memory_limit
ini setting, and execution halts.
Most PHP professionals use the xDebug extension to work around this. The xDebug extension has a modified var_dump
that will limit the amount of information dumped, which prevents the above memory limit problems. The xdebug.var_display_max_children
, xdebug.var_display_max_data
, and xdebug.var_display_max_depth
ini settings are the ones you'll want to tweak if xDebug's still not helping with the memory limit problem. (some PHP distributions have these set too high initially)
If that's not a possibility, a little caution with your var_dump
's can still help.
Use this to figure out the variable type
var_dump(get_class($thing));
If it's a Magento object, use this to see its data keys
var_dump(array_keys($thing->getData()));
And then output individual data members with
var_dump($thing->getData('key_name'));
var_dump($thing->getKeyName()));
How to get proper debug context from production PHP code? print_r vs var_export vs var_dump corner cases
The comment from @BlackXero is correct and works for me.
I did not find a build-in printing function which does not cause errors / warnings when containing a mysqli object with a closed connection (which I would actually classify as bug / unwanted behavior).
We ended up adding the Symfony Vardumper via
composer require symfony/var-dumper
and writing a little helper function for displaying proper and nice output both from cli scripts or the browser:
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
class Debug {
/**
* Method provides a function which can handle all our corner-cases for producing
* debug output.
*
* The corner cases are:
* - objects with recursion
* - mysqli references (also to closed connections in error handling)
*
* The returned result will be:
* - formatted for CLI if the script is run from cli
* - HTML formatted otherwise
* - The HTML formatted output is collapsed by default. Use CTRL-left click to
* expand/collapse all children
* - You can force html|cli formatting using the optional third parameter
*
* Uses the Symfony VarDumper composer module.
*
* @see https://github.com/symfony/var-dumper
* @see https://stackoverflow.com/questions/57520457/how-to-get-proper-debug-context-from-production-php-code-print-r-vs-var-export
* @param mixed $val - variable to be dumped
* @param bool $return - if true, will return the result as string
* @param string|null $format null|cli|html for forcing output format
* @return bool|string
*/
public static function varDump($val, $return = false, $format = null) {
if (is_null($format)) {
$format = php_sapi_name() == 'cli' ? 'cli' : 'html';
}
$cloner = new VarCloner();
if ($format === 'cli') {
$dumper = new CliDumper();
} else {
$dumper = new HtmlDumper();
}
$output = fopen('php://memory', 'r+b');
$dumper->dump($cloner->cloneVar($val), $output);
$res = stream_get_contents($output, -1, 0);
if ($return) {
return $res;
} else {
echo $res;
return true;
}
}
}
That method
- can handle all input I pass to it without errors or warnings
- format nicely for both CLI and HTML
- return the result as a string for forwarding it to external error tracking systems like sentry
So it ticks all boxes I asked for in the initial question.
Thanks @BlackXero for understanding the question correctly and pointing me in the right direction.
Test if variable contains circular references
Would this do it?
function isRecursive($array) {
foreach($array as $v) {
if($v === $array) {
return true;
}
}
return false;
}
PHP's var_dump / print_r output is garbled - encoding issue?
For what it's worth, I finally got to the bottom of this problem (I think!)
The problem seems to be that the API's output was being run through json_decode
whether it was JSON or not. MySQL errors were causing an error page, not a JSON response, which when run through json_decode
(by the API-handling code that received it) before var_dump
produced garbled character salad, as above.
How can I capture the result of var_dump to a string?
Use output buffering:
<?php
ob_start();
var_dump($someVar);
$result = ob_get_clean();
?>
How to check for circular references in PHP when recursively parsing an associative array?
An adapted version of your code, using the strict in_array
check from the answer linked by Ryan Vincent, is shown below:
function HTMLStringify($arr, array $seen = array()) {
if (is_array($arr)) {
$seen[] = $arr;
$html = '<ul>';
foreach ($arr as $key => $value) {
$html .= '<li>' . $key;
if (is_array($value)) {
if (in_array($value, $seen, true)) {
// Deal with recursion in your own way here
$html .= ' [RECURSION]';
} else {
$html .= HTMLStringify($value, $seen);
}
} elseif (is_numeric($value) || is_string($value) || is_null($value)) {
$html .= ' = ' . $value;
} else {
$html .= ' [couldn\'t parse ' . gettype($value) . ']';
}
$html .= '</li>';
}
return $html . '</ul>';
} else {
return null;
}
}
$arr = array(1 => 'one', 2 => 'two');
$arr[3] = &$arr;
echo HTMLStringify($arr);
Comparing across a number of PHP versions, it looks like this will work for PHP 5.3.15+ and PHP 5.4.5+.
Is there a way to detect circular arrays in pure PHP?
The isRecursiveArray(array) method below detects circular/recursive arrays. It keeps track of which arrays have been visited by temporarily adding an element containing a known object reference to the end of the array.
If you want help writing the serialization method, please update your topic question and provide a sample serialization format in your question.
function removeLastElementIfSame(array & $array, $reference) {
if(end($array) === $reference) {
unset($array[key($array)]);
}
}
function isRecursiveArrayIteration(array & $array, $reference) {
$last_element = end($array);
if($reference === $last_element) {
return true;
}
$array[] = $reference;
foreach($array as &$element) {
if(is_array($element)) {
if(isRecursiveArrayIteration($element, $reference)) {
removeLastElementIfSame($array, $reference);
return true;
}
}
}
removeLastElementIfSame($array, $reference);
return false;
}
function isRecursiveArray(array $array) {
$some_reference = new stdclass();
return isRecursiveArrayIteration($array, $some_reference);
}
$array = array('a','b','c');
var_dump(isRecursiveArray($array));
print_r($array);
$array = array('a','b','c');
$array[] = $array;
var_dump(isRecursiveArray($array));
print_r($array);
$array = array('a','b','c');
$array[] = &$array;
var_dump(isRecursiveArray($array));
print_r($array);
$array = array('a','b','c');
$array[] = &$array;
$array = array($array);
var_dump(isRecursiveArray($array));
print_r($array);
Related Topics
How to Use Laravel Passport with Password Grant Tokens
.Htaccess Redirect Non-Www to Www Preserving Uri String
PHP Array Printing Using a Loop
How to Send Money to Paypal Using PHP
Extending Sonata User Bundle and Adding New Fields
How to Connect to Database on Another Server
Recreate Original PHP Array from Print_R Output
Change Tag Attribute Value with PHP Domdocument
Query MySQL and Export Data as CSV in PHP
Native PHP Support in Visual Studio 2010
Fatal Error: Call to Undefined Function Pg_Connect()
How to Change the Array Key to Start from 1 Instead of 0
How to Add a Watermark to an Existing PDF File Using PHP
Php, MySQL - Too Many Connections to Database Error
HTML Form PHP Post to Self to Validate or Submit to New Page
Using the Browser Prompt to Download a File
Is This the Most Efficient Way to Get and Remove First Line in File