Skip to content

Commit 9c88851

Browse files
committedMay 28, 2015
Create a new DebugIterator
Yep, now I really need this, to see what's going on inside the stupid forecast-to-scale loops, that just won't work.
1 parent 6ef14fb commit 9c88851

File tree

3 files changed

+290
-0
lines changed

3 files changed

+290
-0
lines changed
 

‎DebugIterator.php

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
/**
3+
* The <b>DebugIterator</b> wraps as an {@see IteratorIterator} around
4+
* any other {@see Iterator} and logs all calls to it.
5+
*
6+
* <code>
7+
* // Take any iterator...
8+
* $originalIterator = new ArrayIterator(range(1, 5));
9+
*
10+
* // ..and wrap it in a DebugIterator, to log calls to it.
11+
* $iterator = new DebugIterator($originalIterator);
12+
*
13+
* // This will now print something like
14+
* // DebugIterator(ArrayIterator)::rewind()
15+
* // DebugIterator(ArrayIterator)::valid() => TRUE
16+
* // DebugIterator(ArrayIterator)::current() => 1
17+
* // DebugIterator(ArrayIterator)::next()
18+
* // ...
19+
* foreach($iterator as $item) {
20+
* // ...
21+
* }
22+
* </code>
23+
*
24+
* @author Beat Vontobel
25+
* @since 2015-05-2i
26+
* @package MeteoNews\phplib
27+
* @subpackage Iterators
28+
*/
29+
class DebugIterator extends IteratorIterator {
30+
/**
31+
* Logs a message.
32+
*
33+
* Override this, if you want to send your log output to different
34+
* destinations or with different formatting. Per default each
35+
* message will be just printed to STDOUT with a newline appended.
36+
*
37+
* @param string The message to be logged.
38+
*
39+
* @return void
40+
*/
41+
protected function writeLogMessage($message) {
42+
printf("%s\n", $message);
43+
}
44+
45+
/**
46+
* Logs one method call.
47+
*
48+
* Override this, if you're unhappy with the default formatting.
49+
*
50+
* The default internally uses {@see formatValue()} to format
51+
* individual arguments and return values to/from method calls and
52+
* {@see writeLogMessage()} to send the completed message to an
53+
* output channel. If you just want to change one of those, it's
54+
* better to override those methods and leave this one as it is.
55+
*
56+
* @param string The name of the called method.
57+
*
58+
* @param boolean If the given method has a return value (TRUE) or
59+
* is void (FALSE).
60+
*
61+
* @param mixed The return value from the called method (ignored if
62+
* the previous argument is FALSE).
63+
*
64+
* @param array The arguments to the method call.
65+
*
66+
* @return void
67+
*
68+
* @see writeLogMessage(), formatValue()
69+
*/
70+
protected function logCall($method,
71+
$hasReturnValue = FALSE,
72+
$returnValue = NULL,
73+
array $args = array()) {
74+
$message = sprintf("%s(%s)::%s(%s)",
75+
get_called_class(),
76+
get_class(parent::getInnerIterator()),
77+
$method,
78+
implode(", ", array_map(array($this, 'formatValue'), $args)));
79+
if($hasReturnValue)
80+
$message .= sprintf(" => %s", $this->formatValue($returnValue));
81+
$this->writeLogMessage($message);
82+
}
83+
84+
/**
85+
* Formats a PHP value as a string.
86+
*
87+
* Used for arguments and return values to/from method calls in log
88+
* messages. Override this, if you're unhappy with the default
89+
* format.
90+
*
91+
* @param mixed The value to be formatted as a string.
92+
*
93+
* @return string
94+
*/
95+
protected function formatValue($value) {
96+
if(is_object($value))
97+
if(is_a($value, "ifMNDebugName"))
98+
return $value->getDebugName();
99+
else
100+
return sprintf("object(%s)",
101+
get_class($value));
102+
elseif(is_array($value))
103+
return sprintf("array(%s)",
104+
implode(", ",
105+
array_map(array($this, 'formatValue'),
106+
$value)));
107+
elseif(is_bool($value))
108+
return $value ? "TRUE" : "FALSE";
109+
elseif(is_null($value))
110+
return "NULL";
111+
elseif(is_float($value) || is_int($value))
112+
return (string)$value;
113+
else
114+
return sprintf("'%s'",
115+
str_replace("'", "\\'", $value));
116+
}
117+
118+
119+
/*
120+
* Just override all the IteratorIterator methods here to hook in
121+
* the logging calls.
122+
*/
123+
124+
public function __construct(Traversable $iterator) {
125+
parent::__construct($iterator);
126+
$this->logCall(__FUNCTION__, TRUE, $this, array($iterator));
127+
}
128+
129+
public function current() {
130+
$current = parent::current();
131+
$this->logCall(__FUNCTION__, TRUE, $current);
132+
return $current;
133+
}
134+
135+
public function getInnerIterator() {
136+
$innerIterator = parent::getInnerIterator();
137+
$this->logCall(__FUNCTION__, TRUE, $innerIterator);
138+
return $innerIterator;
139+
}
140+
141+
public function key() {
142+
$key = parent::key();
143+
$this->logCall(__FUNCTION__, TRUE, $key);
144+
return $key;
145+
}
146+
147+
public function next() {
148+
parent::next();
149+
$this->logCall(__FUNCTION__);
150+
}
151+
152+
public function rewind() {
153+
parent::rewind();
154+
$this->logCall(__FUNCTION__);
155+
}
156+
157+
public function valid() {
158+
$valid = parent::valid();
159+
$this->logCall(__FUNCTION__, TRUE, $valid);
160+
return $valid;
161+
}
162+
}
163+
?>

‎tests/DebugIterator_000.phpt

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
--TEST--
2+
DebugIterator: Automatic smoke test (syntax, warnings, silence)
3+
--FILE--
4+
<?php
5+
$source = FALSE;
6+
$output = FALSE;
7+
$global_vars = array();
8+
$cr_okay = FALSE;
9+
10+
$source = file_get_contents("DebugIterator.php");
11+
$lines = preg_split('/(\\x0a|\\x0d)+/', $source, 0, PREG_SPLIT_NO_EMPTY);
12+
13+
if(preg_match('/\?>\s*$/', $source))
14+
echo "DebugIterator has closing PHP tag at end\n";
15+
else
16+
echo "DebugIterator is missing closing PHP tag at end\n";
17+
18+
if(file_exists(preg_replace("|[^/]+$|", "DOS_LINE_ENDINGS", "DebugIterator"))) {
19+
// Check for a consistent use of only CRLF if a "DOS_LINE_ENDINGS" marker
20+
// file exists in the containing folder
21+
$cr_okay = (preg_match_all('/\x0d\x0a/', $source) == preg_match_all('/\x0d/', $source)
22+
&& preg_match_all('/\x0d\x0a/', $source) == preg_match_all('/\x0a/', $source));
23+
} else {
24+
// Otherwise (normal situation) any CR character is treated as a bug
25+
$cr_okay = !preg_match('/\x0d/', $source);
26+
}
27+
28+
if($cr_okay)
29+
echo "DebugIterator does not contain CR characters (or is consistent and in a folder marked with DOS_LINE_ENDINGS)\n";
30+
else
31+
echo "DebugIterator does contain CR characters (and is not consistent or not in a folder marked with DOS_LINE_ENDINGS)\n";
32+
33+
$global_vars = array_keys($GLOBALS);
34+
35+
ob_start();
36+
require_once("DebugIterator.php");
37+
$output = ob_get_contents();
38+
ob_end_clean();
39+
if($output === "")
40+
echo "Parsing of DebugIterator was silent\n";
41+
else
42+
echo "Parsing of DebugIterator was not silent:\n$output";
43+
44+
// XXX: Currently, we have these exceptions of global variables being added...
45+
$global_vars = array_merge($global_vars,
46+
array('db_host', 'db_user', 'db_pass', 'db_name',
47+
'memcache_hostconfig',
48+
'global_script_resources_object'));
49+
50+
if(count(array_diff(array_keys($GLOBALS), $global_vars)) == 0)
51+
print "DebugIterator did not pollute global variable space\n";
52+
else
53+
print "DebugIterator added these variables to global space: ".
54+
implode(", ", array_diff(array_keys($GLOBALS), $global_vars)).
55+
"\n";
56+
57+
$class_defs = array();
58+
foreach($lines as $line) {
59+
if(preg_match('/^\s*((abstract)\s+)?(class|interface|trait)\s+([a-z0-9_]+)/i', $line, $matches)) {
60+
$new_def = "$matches[3] $matches[4]";
61+
if($matches[3] == 'class'
62+
&& preg_match('/Exception$/', $matches[4])) {
63+
foreach($class_defs as $class_def) {
64+
if(preg_match("/^".preg_replace("/^(trait|interface)/", "class", $class_def)."/", $new_def))
65+
continue 2;
66+
}
67+
}
68+
$class_defs[] = $new_def;
69+
}
70+
}
71+
if(count($class_defs) <= 1) {
72+
print "DebugIterator contains at most one class, interface, or trait (except for Exception classes)\n";
73+
} else {
74+
print "DebugIterator contains multiple classes/interfaces/traits:\n";
75+
print " ".implode("\n ", $class_defs)."\n";
76+
}
77+
?>
78+
--EXPECT--
79+
DebugIterator has closing PHP tag at end
80+
DebugIterator does not contain CR characters (or is consistent and in a folder marked with DOS_LINE_ENDINGS)
81+
Parsing of DebugIterator was silent
82+
DebugIterator did not pollute global variable space
83+
DebugIterator contains at most one class, interface, or trait (except for Exception classes)

‎tests/DebugIterator_001.phpt

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
DebugIterator: Basic test
3+
--FILE--
4+
<?php
5+
require_once("DebugIterator.php");
6+
$iterator = new DebugIterator(new ArrayIterator(array("one",
7+
new stdClass(),
8+
3,
9+
array("1 o'clock", "2 o'clock"),
10+
TRUE,
11+
NULL
12+
)));
13+
foreach($iterator as $key => $item) {
14+
// NOP, the DebugIterator does everything
15+
}
16+
?>
17+
--EXPECT--
18+
DebugIterator(ArrayIterator)::__construct(object(ArrayIterator)) => object(DebugIterator)
19+
DebugIterator(ArrayIterator)::rewind()
20+
DebugIterator(ArrayIterator)::valid() => TRUE
21+
DebugIterator(ArrayIterator)::current() => 'one'
22+
DebugIterator(ArrayIterator)::key() => 0
23+
DebugIterator(ArrayIterator)::next()
24+
DebugIterator(ArrayIterator)::valid() => TRUE
25+
DebugIterator(ArrayIterator)::current() => object(stdClass)
26+
DebugIterator(ArrayIterator)::key() => 1
27+
DebugIterator(ArrayIterator)::next()
28+
DebugIterator(ArrayIterator)::valid() => TRUE
29+
DebugIterator(ArrayIterator)::current() => 3
30+
DebugIterator(ArrayIterator)::key() => 2
31+
DebugIterator(ArrayIterator)::next()
32+
DebugIterator(ArrayIterator)::valid() => TRUE
33+
DebugIterator(ArrayIterator)::current() => array('1 o\'clock', '2 o\'clock')
34+
DebugIterator(ArrayIterator)::key() => 3
35+
DebugIterator(ArrayIterator)::next()
36+
DebugIterator(ArrayIterator)::valid() => TRUE
37+
DebugIterator(ArrayIterator)::current() => TRUE
38+
DebugIterator(ArrayIterator)::key() => 4
39+
DebugIterator(ArrayIterator)::next()
40+
DebugIterator(ArrayIterator)::valid() => TRUE
41+
DebugIterator(ArrayIterator)::current() => NULL
42+
DebugIterator(ArrayIterator)::key() => 5
43+
DebugIterator(ArrayIterator)::next()
44+
DebugIterator(ArrayIterator)::valid() => FALSE

0 commit comments

Comments
 (0)
Please sign in to comment.