我正在尝试在PHP中创建print_r()的替代方法,该方法将与我公司的彩色日志记录软件一起使用。我在PHP文档页面上为print_r()删除了一个示例的初始源代码,添加了颜色和递归保护,并且它运行良好。
然而,有一件小事让我对我的递归保护感到不满。在引用递归之前,自引用数组将打印两次。这是我的测试代码:
$array = array(
1,
2,
3,
$object = (object) array(
'foo' => 'bar',
'baz' => 'qux',
),
$object,
);
$array[] = &$array;
$array[] = &$array;
logDebug($array);
这是输出:
___________________________________________________________
|
:: 2013-11-15 17:02:06 ( debug ) :: test_SplitLog.php [23] ::
|
| Array (
|
| [0] => 1
| [1] => 2
| [2] => 3
| [3] => Object [stdClass] {
|
| [foo] => "bar"
| [baz] => "qux"
|
| }
| [4] => Object [stdClass] {*** Recursion ***}
| [5] => Array (
|
| [0] => 1
| [1] => 2
| [2] => 3
| [3] => Object [stdClass] {*** Recursion ***}
| [4] => Object [stdClass] {*** Recursion ***}
| [5] => Array (*** Recursion ***)
| [6] => Array (*** Recursion ***)
|
| )
| [6] => Array (*** Recursion ***)
|
| )
|___________________________________________________________
如您所见,数组在顶层打印一次,然后再次作为其自身内的元素打印(元素5)。然而,一旦它到达元素6(以及内部数组中的元素5和6),它就会打印递归消息。
我通过向每个被记录的数组添加一个带有非常特定键的元素来处理数组递归。如果稍后在数组中找到该键,则表示该数组已经转换为字符串,应该跳过它。
我使用普通的print_r()进行了一些测试。当我添加密钥时,它会立即显示在数组的print_r()中(有意义)。然后我遍历每个数组元素,然后递归调用print_r()。当数组自身(元素5)时,print_r()显示标记元素不存在。
一方面看起来像一个价值vs参考的东西 - 就像我忘了添加&某处。但是当元素6出现时,以及在数组的递归处理的5和6中都会显示密钥。
我的另一个想法是,在引用识别出标记键的存在之前,需要重置数组内部的某些内容。我试过reset()无济于事。有没有人知道为什么这样做?
谢谢! ~Nate
来源:
/**
* Alternative to print_r() that makes use of ANSIColor. Adapted
* from function in print_r() documentation on PHP site.
*/
public function convertRecursivelyToString(&$thing, $settings = array()) {
//:: Init
$object_dictionary = array();
$array_dictionary = array();
$settings = (array) $settings;
$palette = $this->colorPalette();
//:: Convert
$string = $this->convertRecursivelyToStringHelper($thing, $object_dictionary, $array_dictionary, 0, $palette, $settings);
//:: Undo Array Tracking
foreach ($array_dictionary as &$array) {
unset($array['__LogFormatter::convertRecursivelyToStringHelper()__']);
unset($array);
}
//:: Return String
return $string;
}
/**
* Alias for LogFormatter::convertRecursivelyToString().
*/
public function printR(&$thing, $settings = array()) {
return $this->convertRecursivelyToString($thing, $settings);
}
/**
* Performs the actual recursive conversion.
*/
private function convertRecursivelyToStringHelper(&$thing, &$object_dictionary, &$array_dictionary, $level, $palette, $settings) {
//:: Init
$tval = $thing; // value of $thing to eliminate reference
$spaces = "";
$space = " ";
$newline = "\n";
$title = "";
$array = is_array($tval);
$object = is_object($tval);
$output = "";
$title = $level === 0 && isset($settings['initial_title']) ? $settings['initial_title'] : null;
$obrace = $level === 0 && isset($settings['initial_brace']) ? $settings['initial_brace'] : ($array ? '(' : '{');
$cbrace = $level === 0 && isset($settings['closing_brace']) ? $settings['closing_brace'] : ($array ? ')' : '}');
//:: Generate Spaces And Tabs
for ($i = 1; $i <= 6; $i++) {
$spaces .= $space;
}
$tabs = "";
for ($i = 0; $i < $level; $i++) {
$tabs .= $spaces;
}
//:: Handle Recursion
if (
($array && isset($tval['__LogFormatter::convertRecursivelyToStringHelper()__'])) ||
($object && isset($object_dictionary[spl_object_hash($tval)]))
) {
if ($array) {
$output = "Array $obrace*** Recursion ***$cbrace";
} else {
$output = "Object [" . $palette->dimYellow(get_class($tval)) . "] $obrace*** Recursion ***$cbrace";
}
//:: Handle Scalars
} else if (is_scalar($tval) || is_null($tval)) {
// booleans
if (is_bool($tval)) {
$output = $tval ? $palette->dimCyan('true') : $palette->dimRed('false');
// null
} else if (is_null($tval)) {
$output = $palette->brightBlack('null');
// strings
} else if (is_string($tval)) {
$output = '"' . $palette->dimGreen($tval) . '"';
// everything else
} else {
$output = (string) $tval;
}
//:: Handle Arrays
} else if ($array) {
// title
$title = $title === null ? "Array" : $title;
$output = $title . ' ' . $obrace . $newline . $newline;
// contents
$thing['__LogFormatter::convertRecursivelyToStringHelper()__'] = true; // prevents recursion
reset($thing);
$array_dictionary[] = &$thing;
foreach($thing as $key => &$value) {
if ($key !== '__LogFormatter::convertRecursivelyToStringHelper()__') {
$string_value = $this->convertRecursivelyToStringHelper($value, $object_dictionary, $array_dictionary, $level + 1, $palette, $settings);
$output .= $tabs . $spaces . "[" . $palette->brightGreen($key) . "] => " . $string_value . $newline;
}
unset($value);
}
$output .= $newline . $tabs . $cbrace;
//:: Handle Objects
} else if ($object) {
// title
$title = $title === null ? "Object [" . $palette->dimYellow(get_class($tval)) . "]" : $title;
$output = $title . ' ' . $obrace . $newline . $newline;
// contents
$object_dictionary[spl_object_hash($tval)] = true; // prevents recursion
if ($object && ($tval instanceof Loggable)) {
$output .= $tval->convertToLogString();
} else {
foreach($tval as $key => &$value) {
$string_value = $this->convertRecursivelyToStringHelper($value, $object_dictionary, $array_dictionary, $level + 1, $palette, $settings);
$output .= $tabs . $spaces . "[" . $palette->brightGreen($key) . "] => " . $string_value . $newline;
}
}
$output .= $newline . $tabs . $cbrace;
//:: Handle Other
} else {
$output = gettype($tval);
}
//:: Return
unset($thing);
unset($object_dictionary);
unset($array_dictionary);
return $output;
}