Overview

Namespaces

  • Budovy
  • Kdyby
    • BootstrapFormRenderer
      • DI
      • Latte
  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • Nextras
    • Datagrid
  • None
  • PHP
  • Tester
    • CodeCoverage
    • Runner
      • Output
  • Vodacek
    • Forms
      • Controls
  • WebLoader
    • Filter
    • Nette

Classes

  • Assert
  • DataProvider
  • DomQuery
  • Dumper
  • Environment
  • Helpers
  • TestCase

Exceptions

  • AssertException
  • TestCaseException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Tester.
  5:  * Copyright (c) 2009 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Tester;
  9: 
 10: 
 11: /**
 12:  * Dumps PHP variables.
 13:  *
 14:  * @author     David Grudl
 15:  */
 16: class Dumper
 17: {
 18:     public static $maxLength = 70;
 19:     public static $maxDepth = 50;
 20:     public static $dumpDir = 'output';
 21:     public static $maxPathSegments = 3;
 22: 
 23:     /**
 24:      * Dumps information about a variable in readable format.
 25:      * @param  mixed  variable to dump
 26:      * @return string
 27:      */
 28:     public static function toLine($var)
 29:     {
 30:         static $table;
 31:         if ($table === NULL) {
 32:             foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
 33:                 $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
 34:             }
 35:             $table["\\"] = '\\\\';
 36:             $table["\r"] = '\r';
 37:             $table["\n"] = '\n';
 38:             $table["\t"] = '\t';
 39:         }
 40: 
 41:         if (is_bool($var)) {
 42:             return $var ? 'TRUE' : 'FALSE';
 43: 
 44:         } elseif ($var === NULL) {
 45:             return 'NULL';
 46: 
 47:         } elseif (is_int($var)) {
 48:             return "$var";
 49: 
 50:         } elseif (is_float($var)) {
 51:             if (!is_finite($var)) {
 52:                 return var_export($var, TRUE);
 53:             }
 54:             $var = json_encode($var);
 55:             return strpos($var, '.') === FALSE ? $var . '.0' : $var;
 56: 
 57:         } elseif (is_string($var)) {
 58:             if ($cut = @iconv_strlen($var, 'UTF-8') > self::$maxLength) {
 59:                 $var = iconv_substr($var, 0, self::$maxLength, 'UTF-8') . '...';
 60:             } elseif ($cut = strlen($var) > self::$maxLength) {
 61:                 $var = substr($var, 0, self::$maxLength) . '...';
 62:             }
 63:             return (preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $var) || preg_last_error() ? '"' . strtr($var, $table) . '"' : "'$var'");
 64: 
 65:         } elseif (is_array($var)) {
 66:             $out = '';
 67:             $counter = 0;
 68:             foreach ($var as $k => & $v) {
 69:                 $out .= ($out === '' ? '' : ', ');
 70:                 if (strlen($out) > self::$maxLength) {
 71:                     $out .= '...';
 72:                     break;
 73:                 }
 74:                 $out .= ($k === $counter ? '' : self::toLine($k) . ' => ')
 75:                     . (is_array($v) ? 'array(...)' : self::toLine($v));
 76:                 $counter = is_int($k) ? max($k + 1, $counter) : $counter;
 77:             }
 78:             return "array($out)";
 79: 
 80:         } elseif ($var instanceof \Exception) {
 81:             return 'Exception ' . get_class($var) . ': ' . ($var->getCode() ? '#' . $var->getCode() . ' ' : '') . $var->getMessage();
 82: 
 83:         } elseif (is_object($var)) {
 84:             return get_class($var) . '(#' . substr(md5(spl_object_hash($var)), 0, 4) . ')';
 85: 
 86:         } elseif (is_resource($var)) {
 87:             return 'resource(' . get_resource_type($var) . ')';
 88: 
 89:         } else {
 90:             return 'unknown type';
 91:         }
 92:     }
 93: 
 94: 
 95:     /**
 96:      * Dumps variable in PHP format.
 97:      * @param  mixed  variable to dump
 98:      * @return string
 99:      */
100:     public static function toPhp($var)
101:     {
102:         return self::_toPhp($var);
103:     }
104: 
105: 
106:     /**
107:      * @return string
108:      */
109:     private static function _toPhp(&$var, $level = 0)
110:     {
111:         if (is_float($var)) {
112:             $var = json_encode($var);
113:             return strpos($var, '.') === FALSE ? $var . '.0' : $var;
114: 
115:         } elseif (is_bool($var)) {
116:             return $var ? 'TRUE' : 'FALSE';
117: 
118:         } elseif (is_string($var) && (preg_match('#[^\x09\x20-\x7E\xA0-\x{10FFFF}]#u', $var) || preg_last_error())) {
119:             static $table;
120:             if ($table === NULL) {
121:                 foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
122:                     $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
123:                 }
124:                 $table['\\'] = '\\\\';
125:                 $table["\r"] = '\r';
126:                 $table["\n"] = '\n';
127:                 $table["\t"] = '\t';
128:                 $table['$'] = '\$';
129:                 $table['"'] = '\"';
130:             }
131:             return '"' . strtr($var, $table) . '"';
132: 
133:         } elseif (is_array($var)) {
134:             $space = str_repeat("\t", $level);
135: 
136:             static $marker;
137:             if ($marker === NULL) {
138:                 $marker = uniqid("\x00", TRUE);
139:             }
140:             if (empty($var)) {
141:                 $out = '';
142: 
143:             } elseif ($level > self::$maxDepth || isset($var[$marker])) {
144:                 return '/* Nesting level too deep or recursive dependency */';
145: 
146:             } else {
147:                 $out = '';
148:                 $outAlt = "\n$space";
149:                 $var[$marker] = TRUE;
150:                 $counter = 0;
151:                 foreach ($var as $k => &$v) {
152:                     if ($k !== $marker) {
153:                         $item = ($k === $counter ? '' : self::_toPhp($k, $level + 1) . ' => ') . self::_toPhp($v, $level + 1);
154:                         $counter = is_int($k) ? max($k + 1, $counter) : $counter;
155:                         $out .= ($out === '' ? '' : ', ') . $item;
156:                         $outAlt .= "\t$item,\n$space";
157:                     }
158:                 }
159:                 unset($var[$marker]);
160:             }
161:             return 'array(' . (strpos($out, "\n") === FALSE && strlen($out) < self::$maxLength ? $out : $outAlt) . ')';
162: 
163:         } elseif (is_object($var)) {
164:             $arr = (array) $var;
165:             $space = str_repeat("\t", $level);
166: 
167:             static $list = array();
168:             if (empty($arr)) {
169:                 $out = '';
170: 
171:             } elseif ($level > self::$maxDepth || in_array($var, $list, TRUE)) {
172:                 return '/* Nesting level too deep or recursive dependency */';
173: 
174:             } else {
175:                 $out = "\n";
176:                 $list[] = $var;
177:                 foreach ($arr as $k => &$v) {
178:                     if ($k[0] === "\x00") {
179:                         $k = substr($k, strrpos($k, "\x00") + 1);
180:                     }
181:                     $out .= "$space\t" . self::_toPhp($k, $level + 1) . ' => ' . self::_toPhp($v, $level + 1) . ",\n";
182:                 }
183:                 array_pop($list);
184:                 $out .= $space;
185:             }
186:             return get_class($var) === 'stdClass'
187:                 ? "(object) array($out)"
188:                 : get_class($var) . "::__set_state(array($out))";
189: 
190:         } elseif (is_resource($var)) {
191:             return '/* resource ' . get_resource_type($var) . ' */';
192: 
193:         } else {
194:             return var_export($var, TRUE);
195:         }
196:     }
197: 
198: 
199:     /** @internal */
200:     public static function dumpException(\Exception $e)
201:     {
202:         $trace = $e->getTrace();
203:         array_splice($trace, 0, $e instanceof \ErrorException ? 1 : 0, array(array('file' => $e->getFile(), 'line' => $e->getLine())));
204: 
205:         $testFile = NULL;
206:         foreach (array_reverse($trace) as $item) {
207:             if (isset($item['file'])) { // in case of shutdown handler, we want to skip inner-code blocks and debugging calls
208:                 $testFile = $item['file'];
209:                 break;
210:             }
211:         }
212: 
213:         if ($e instanceof AssertException) {
214:             if (is_object($e->expected) || is_array($e->expected) || (is_string($e->expected) && strlen($e->expected) > self::$maxLength)
215:                 || is_object($e->actual) || is_array($e->actual) || (is_string($e->actual) && strlen($e->actual) > self::$maxLength)
216:             ) {
217:                 $args = isset($_SERVER['argv'][1]) ? '.[' . preg_replace('#[^a-z0-9-. ]+#i', '_', $_SERVER['argv'][1]) . ']' : '';
218:                 $stored[] = self::saveOutput($testFile, $e->expected, $args . '.expected');
219:                 $stored[] = self::saveOutput($testFile, $e->actual, $args . '.actual');
220:             }
221: 
222:             if ((is_string($e->actual) && is_string($e->expected))) {
223:                 for ($i = 0; $i < strlen($e->actual) && isset($e->expected[$i]) && $e->actual[$i] === $e->expected[$i]; $i++);
224:                 $i = max(0, min($i, max(strlen($e->actual), strlen($e->expected)) - self::$maxLength + 3));
225:                 for (; $i && $i < count($e->actual) && $e->actual[$i-1] >= "\x80" && $e->actual[$i] >= "\x80" && $e->actual[$i] < "\xC0"; $i--);
226:                 if ($i) {
227:                     $e->expected = substr_replace($e->expected, '...', 0, $i);
228:                     $e->actual = substr_replace($e->actual, '...', 0, $i);
229:                 }
230:             }
231: 
232:             $message = 'Failed: ' . $e->getMessage();
233:             if (((is_string($e->actual) && is_string($e->expected)) || (is_array($e->actual) && is_array($e->expected)))
234:                 && preg_match('#^(.*)(%\d)(.*)(%\d.*)\z#s', $message, $m)
235:             ) {
236:                 if (($delta = strlen($m[1]) - strlen($m[3])) >= 3) {
237:                     $message = "$m[1]$m[2]\n" . str_repeat(' ', $delta - 3) . "...$m[3]$m[4]";
238:                 } else {
239:                     $message = "$m[1]$m[2]$m[3]\n" . str_repeat(' ', strlen($m[1]) - 4) . "... $m[4]";
240:                 }
241:             }
242:             $message = strtr($message, array(
243:                 '%1' => "\033[1;33m" . Dumper::toLine($e->actual) . "\033[1;37m",
244:                 '%2' => "\033[1;33m" . Dumper::toLine($e->expected) . "\033[1;37m",
245:             ));
246:         } else {
247:             $message = ($e instanceof \ErrorException ? Helpers::errorTypeToString($e->getSeverity()) : get_class($e))
248:                 . ": {$e->getMessage()}";
249:         }
250: 
251:         $s = "\033[1;37m$message\033[0m\n\n"
252:             . (isset($stored) ? 'diff ' . Helpers::escapeArg($stored[0]) . ' ' . Helpers::escapeArg($stored[1]) . "\n\n" : '');
253: 
254:         foreach ($trace as $item) {
255:             $s .= 'in '
256:                 . (isset($item['file'])
257:                     ? (
258:                         ($item['file'] === $testFile ? "\033[1;37m" : '')
259:                         . implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $item['file']), -self::$maxPathSegments))
260:                         . "($item[line])\033[1;30m "
261:                     )
262:                     : '[internal function]'
263:                 )
264:                 . (isset($item['class']) ? $item['class'] . $item['type'] : '')
265:                 . (isset($item['function']) ? $item['function'] . '()' : '')
266:                 . "\033[0m\n";
267:         }
268: 
269:         if ($e->getPrevious()) {
270:             $s .= "\n(previous) " . static::dumpException($e->getPrevious());
271:         }
272:         return $s;
273:     }
274: 
275: 
276:     /**
277:      * Dumps data to folder 'output'.
278:      * @return string
279:      * @internal
280:      */
281:     public static function saveOutput($testFile, $content, $suffix = '')
282:     {
283:         $path = self::$dumpDir . DIRECTORY_SEPARATOR . basename($testFile, '.phpt') . $suffix;
284:         if (!preg_match('#/|\w:#A', self::$dumpDir)) {
285:             $path = dirname($testFile) . DIRECTORY_SEPARATOR . $path;
286:         }
287:         @mkdir(dirname($path)); // @ - directory may already exist
288:         file_put_contents($path, is_string($content) ? $content : self::toPhp($content));
289:         return $path;
290:     }
291: 
292: 
293:     public static function removeColors($s)
294:     {
295:         return preg_replace('#\033\[[\d;]+m#', '', $s);
296:     }
297: 
298: }
299: 
API documentation generated by ApiGen 2.8.0