【发布时间】:2013-11-29 11:58:11
【问题描述】:
我正在尝试在 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() 显示标记元素不存在。
一方面看起来像是价值与参考的东西 - 就像我忘记在某处添加 & 一样。但是当第 6 个元素出现时,以及在数组的递归处理的第 5 和第 6 位出现时,键确实会出现。
我的另一个想法是,在引用识别标记键的存在之前,需要重置数组内部的某些内容。我尝试了 reset() 无济于事。有没有人知道为什么会这样?
谢谢! 〜内特
来源:
/**
* 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;
}
【问题讨论】: