1<?php 2 /** 3 * base include file for SimpleTest 4 * @package SimpleTest 5 * @subpackage UnitTester 6 * @version $Id: dumper.php,v 1.1 2005/11/09 23:41:18 gsmet Exp $ 7 */ 8 /** 9 * does type matter 10 */ 11 define('TYPE_MATTERS', true); 12 13 /** 14 * Displays variables as text and does diffs. 15 * @package SimpleTest 16 * @subpackage UnitTester 17 */ 18 class SimpleDumper { 19 20 /** 21 * Renders a variable in a shorter form than print_r(). 22 * @param mixed $value Variable to render as a string. 23 * @return string Human readable string form. 24 * @access public 25 */ 26 function describeValue($value) { 27 $type = $this->getType($value); 28 switch($type) { 29 case "Null": 30 return "NULL"; 31 case "Boolean": 32 return "Boolean: " . ($value ? "true" : "false"); 33 case "Array": 34 return "Array: " . count($value) . " items"; 35 case "Object": 36 return "Object: of " . get_class($value); 37 case "String": 38 return "String: " . $this->clipString($value, 100); 39 default: 40 return "$type: $value"; 41 } 42 return "Unknown"; 43 } 44 45 /** 46 * Gets the string representation of a type. 47 * @param mixed $value Variable to check against. 48 * @return string Type. 49 * @access public 50 */ 51 function getType($value) { 52 if (! isset($value)) { 53 return "Null"; 54 } elseif (is_bool($value)) { 55 return "Boolean"; 56 } elseif (is_string($value)) { 57 return "String"; 58 } elseif (is_integer($value)) { 59 return "Integer"; 60 } elseif (is_float($value)) { 61 return "Float"; 62 } elseif (is_array($value)) { 63 return "Array"; 64 } elseif (is_resource($value)) { 65 return "Resource"; 66 } elseif (is_object($value)) { 67 return "Object"; 68 } 69 return "Unknown"; 70 } 71 72 /** 73 * Creates a human readable description of the 74 * difference between two variables. Uses a 75 * dynamic call. 76 * @param mixed $first First variable. 77 * @param mixed $second Value to compare with. 78 * @param boolean $identical If true then type anomolies count. 79 * @return string Description of difference. 80 * @access public 81 */ 82 function describeDifference($first, $second, $identical = false) { 83 if ($identical) { 84 if (! $this->_isTypeMatch($first, $second)) { 85 return "with type mismatch as [" . $this->describeValue($first) . 86 "] does not match [" . $this->describeValue($second) . "]"; 87 } 88 } 89 $type = $this->getType($first); 90 if ($type == "Unknown") { 91 return "with unknown type"; 92 } 93 $method = '_describe' . $type . 'Difference'; 94 return $this->$method($first, $second, $identical); 95 } 96 97 /** 98 * Tests to see if types match. 99 * @param mixed $first First variable. 100 * @param mixed $second Value to compare with. 101 * @return boolean True if matches. 102 * @access private 103 */ 104 function _isTypeMatch($first, $second) { 105 return ($this->getType($first) == $this->getType($second)); 106 } 107 108 /** 109 * Clips a string to a maximum length. 110 * @param string $value String to truncate. 111 * @param integer $size Minimum string size to show. 112 * @param integer $position Centre of string section. 113 * @return string Shortened version. 114 * @access public 115 */ 116 function clipString($value, $size, $position = 0) { 117 $length = strlen($value); 118 if ($length <= $size) { 119 return $value; 120 } 121 $position = min($position, $length); 122 $start = ($size/2 > $position ? 0 : $position - $size/2); 123 if ($start + $size > $length) { 124 $start = $length - $size; 125 } 126 $value = substr($value, $start, $size); 127 return ($start > 0 ? "..." : "") . $value . ($start + $size < $length ? "..." : ""); 128 } 129 130 /** 131 * Creates a human readable description of the 132 * difference between two variables. The minimal 133 * version. 134 * @param null $first First value. 135 * @param mixed $second Value to compare with. 136 * @return string Human readable description. 137 * @access private 138 */ 139 function _describeGenericDifference($first, $second) { 140 return "as [" . $this->describeValue($first) . 141 "] does not match [" . 142 $this->describeValue($second) . "]"; 143 } 144 145 /** 146 * Creates a human readable description of the 147 * difference between a null and another variable. 148 * @param null $first First null. 149 * @param mixed $second Null to compare with. 150 * @param boolean $identical If true then type anomolies count. 151 * @return string Human readable description. 152 * @access private 153 */ 154 function _describeNullDifference($first, $second, $identical) { 155 return $this->_describeGenericDifference($first, $second); 156 } 157 158 /** 159 * Creates a human readable description of the 160 * difference between a boolean and another variable. 161 * @param boolean $first First boolean. 162 * @param mixed $second Boolean to compare with. 163 * @param boolean $identical If true then type anomolies count. 164 * @return string Human readable description. 165 * @access private 166 */ 167 function _describeBooleanDifference($first, $second, $identical) { 168 return $this->_describeGenericDifference($first, $second); 169 } 170 171 /** 172 * Creates a human readable description of the 173 * difference between a string and another variable. 174 * @param string $first First string. 175 * @param mixed $second String to compare with. 176 * @param boolean $identical If true then type anomolies count. 177 * @return string Human readable description. 178 * @access private 179 */ 180 function _describeStringDifference($first, $second, $identical) { 181 if (is_object($second) || is_array($second)) { 182 return $this->_describeGenericDifference($first, $second); 183 } 184 $position = $this->_stringDiffersAt($first, $second); 185 $message = "at character $position"; 186 $message .= " with [" . 187 $this->clipString($first, 100, $position) . "] and [" . 188 $this->clipString($second, 100, $position) . "]"; 189 return $message; 190 } 191 192 /** 193 * Creates a human readable description of the 194 * difference between an integer and another variable. 195 * @param integer $first First number. 196 * @param mixed $second Number to compare with. 197 * @param boolean $identical If true then type anomolies count. 198 * @return string Human readable description. 199 * @access private 200 */ 201 function _describeIntegerDifference($first, $second, $identical) { 202 if (is_object($second) || is_array($second)) { 203 return $this->_describeGenericDifference($first, $second); 204 } 205 return "because [" . $this->describeValue($first) . 206 "] differs from [" . 207 $this->describeValue($second) . "] by " . 208 abs($first - $second); 209 } 210 211 /** 212 * Creates a human readable description of the 213 * difference between two floating point numbers. 214 * @param float $first First float. 215 * @param mixed $second Float to compare with. 216 * @param boolean $identical If true then type anomolies count. 217 * @return string Human readable description. 218 * @access private 219 */ 220 function _describeFloatDifference($first, $second, $identical) { 221 if (is_object($second) || is_array($second)) { 222 return $this->_describeGenericDifference($first, $second); 223 } 224 return "because " . $this->describeValue($first) . 225 "] differs from [" . 226 $this->describeValue($second) . "]"; 227 } 228 229 /** 230 * Creates a human readable description of the 231 * difference between two arrays. 232 * @param array $first First array. 233 * @param mixed $second Array to compare with. 234 * @param boolean $identical If true then type anomolies count. 235 * @return string Human readable description. 236 * @access private 237 */ 238 function _describeArrayDifference($first, $second, $identical) { 239 if (! is_array($second)) { 240 return $this->_describeGenericDifference($first, $second); 241 } 242 if (! $this->_isMatchingKeys($first, $second, $identical)) { 243 return "as key list [" . 244 implode(", ", array_keys($first)) . "] does not match key list [" . 245 implode(", ", array_keys($second)) . "]"; 246 } 247 foreach (array_keys($first) as $key) { 248 if ($identical && ($first[$key] === $second[$key])) { 249 continue; 250 } 251 if (! $identical && ($first[$key] == $second[$key])) { 252 continue; 253 } 254 return "with member [$key] " . $this->describeDifference( 255 $first[$key], 256 $second[$key], 257 $identical); 258 } 259 return ""; 260 } 261 262 /** 263 * Compares two arrays to see if their key lists match. 264 * For an identical match, the ordering and types of the keys 265 * is significant. 266 * @param array $first First array. 267 * @param array $second Array to compare with. 268 * @param boolean $identical If true then type anomolies count. 269 * @return boolean True if matching. 270 * @access private 271 */ 272 function _isMatchingKeys($first, $second, $identical) { 273 $first_keys = array_keys($first); 274 $second_keys = array_keys($second); 275 if ($identical) { 276 return ($first_keys === $second_keys); 277 } 278 sort($first_keys); 279 sort($second_keys); 280 return ($first_keys == $second_keys); 281 } 282 283 /** 284 * Creates a human readable description of the 285 * difference between a resource and another variable. 286 * @param resource $first First resource. 287 * @param mixed $second Resource to compare with. 288 * @param boolean $identical If true then type anomolies count. 289 * @return string Human readable description. 290 * @access private 291 */ 292 function _describeResourceDifference($first, $second, $identical) { 293 return $this->_describeGenericDifference($first, $second); 294 } 295 296 /** 297 * Creates a human readable description of the 298 * difference between two objects. 299 * @param object $first First object. 300 * @param mixed $second Object to compare with. 301 * @param boolean $identical If true then type anomolies count. 302 * @return string Human readable description. 303 * @access private 304 */ 305 function _describeObjectDifference($first, $second, $identical) { 306 if (! is_object($second)) { 307 return $this->_describeGenericDifference($first, $second); 308 } 309 return $this->_describeArrayDifference( 310 get_object_vars($first), 311 get_object_vars($second), 312 $identical); 313 } 314 315 /** 316 * Find the first character position that differs 317 * in two strings by binary chop. 318 * @param string $first First string. 319 * @param string $second String to compare with. 320 * @return integer Position of first differing 321 * character. 322 * @access private 323 */ 324 function _stringDiffersAt($first, $second) { 325 if (! $first || ! $second) { 326 return 0; 327 } 328 if (strlen($first) < strlen($second)) { 329 list($first, $second) = array($second, $first); 330 } 331 $position = 0; 332 $step = strlen($first); 333 while ($step > 1) { 334 $step = (integer)(($step + 1)/2); 335 if (strncmp($first, $second, $position + $step) == 0) { 336 $position += $step; 337 } 338 } 339 return $position; 340 } 341 342 /** 343 * Sends a formatted dump of a variable to a string. 344 * @param mixed $variable Variable to display. 345 * @return string Output from print_r(). 346 * @access public 347 * @static 348 */ 349 function dump($variable) { 350 ob_start(); 351 print_r($variable); 352 $formatted = ob_get_contents(); 353 ob_end_clean(); 354 return $formatted; 355 } 356 357 /** 358 * Extracts the last assertion that was not within 359 * Simpletest itself. The name must start with "assert". 360 * @param array $stack List of stack frames. 361 * @param string $format String formatting. 362 * @param string $prefix Prefix of method to search for. 363 * @access public 364 * @static 365 */ 366 function getFormattedAssertionLine($stack, $format = '%d', $prefix = 'assert') { 367 foreach ($stack as $frame) { 368 if (isset($frame['file']) && strpos($frame['file'], 'simpletest') !== false) { // dirname() is a bit slow. 369 if (substr(dirname($frame['file']), -10) == 'simpletest') { 370 continue; 371 } 372 } 373 if (strncmp($frame['function'], $prefix, strlen($prefix)) == 0) { 374 return sprintf($format, $frame['line']); 375 } 376 } 377 return ''; 378 } 379 } 380?>