1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: 15: 16: 17: 18: 19: 20: 21: 22:
23: class Jyxo_FirePhp
24: {
25: 26: 27: 28: 29:
30: const INFO = 'INFO';
31:
32: 33: 34: 35: 36:
37: const WARNING = 'WARN';
38:
39: 40: 41: 42: 43:
44: const ERROR = 'ERROR';
45:
46: 47: 48: 49: 50:
51: const LOG = 'LOG';
52:
53: 54: 55: 56: 57:
58: const TRACE = 'TRACE';
59:
60: 61: 62: 63: 64:
65: const TABLE = 'TABLE';
66:
67: 68: 69: 70: 71:
72: private static $enabled = true;
73:
74: 75: 76: 77: 78:
79: public static function setEnabled($flag = true)
80: {
81: self::$enabled = (bool) $flag;
82: }
83:
84: 85: 86: 87: 88: 89: 90:
91: public static function dump($variable, $label = '')
92: {
93: return self::log($variable, (string) $label);
94: }
95:
96: 97: 98: 99: 100: 101: 102:
103: public static function info($message, $label = '')
104: {
105: return self::log((string) $message, (string) $label, self::INFO);
106: }
107:
108: 109: 110: 111: 112: 113: 114:
115: public static function warning($message, $label = '')
116: {
117: return self::log((string) $message, (string) $label, self::WARNING);
118: }
119:
120: 121: 122: 123: 124: 125: 126:
127: public static function error($message, $label = '')
128: {
129: return self::log((string) $message, (string) $label, self::ERROR);
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139:
140: public static function log($message, $label = '', $type = self::LOG)
141: {
142: $output = array(
143: array(
144: 'Type' => $type,
145: 'Label' => $label
146: ),
147: self::encodeVariable($message)
148: );
149:
150: return self::send($output);
151: }
152:
153: 154: 155: 156: 157: 158: 159: 160: 161:
162: public static function trace($message, $file, $line, array $trace)
163: {
164: $output = array(
165: array(
166: 'Type' => self::TRACE,
167: 'Label' => null
168: ),
169: array(
170: 'Message' => Jyxo_Charset::fixUtf($message),
171: 'File' => $file,
172: 'Line' => $line,
173: 'Trace' => self::replaceVariable($trace)
174: )
175: );
176:
177: return self::send($output);
178: }
179:
180: 181: 182: 183: 184: 185: 186: 187: 188:
189: public static function table($label, array $header, array $data, $ident = '')
190: {
191: $output = array(
192: array(
193: 'Type' => self::TABLE,
194: 'Label' => $label
195: ),
196: array_merge(array($header), $data)
197: );
198:
199: return self::send($output, $ident);
200: }
201:
202: 203: 204: 205: 206: 207:
208: public static function exception(Exception $e)
209: {
210: $result = self::trace(
211: 'Exception: ' . $e->getMessage() . ' [' . $e->getCode() . ']',
212: $e->getFile(),
213: $e->getLine(),
214: $e->getTrace()
215: );
216: while ($e = $e->getPrevious()) {
217: self::trace(
218: 'Previous exception: ' . $e->getMessage() . ' [' . $e->getCode() . ']',
219: $e->getFile(),
220: $e->getLine(),
221: $e->getTrace()
222: );
223: }
224: return $result;
225: }
226:
227: 228: 229: 230: 231: 232: 233:
234: private static function send(array $output, $ident = '')
235: {
236:
237: if (headers_sent()) {
238: return false;
239: }
240:
241:
242: if (!self::$enabled) {
243: return false;
244: }
245:
246:
247: if (!self::isInstalled()) {
248: return false;
249: }
250:
251:
252: static $no = 0;
253:
254:
255: $first = reset($output);
256: if (empty($first['File'])) {
257:
258: $first = array_shift($output);
259:
260:
261: $backtrace = debug_backtrace();
262: $hop = array_shift($backtrace);
263:
264:
265: while (__FILE__ === $hop['file']) {
266: $hop = array_shift($backtrace);
267: }
268:
269:
270: $first['File'] = $hop['file'];
271: $first['Line'] = $hop['line'];
272:
273:
274: array_unshift($output, $first);
275: }
276:
277:
278: $parts = str_split(json_encode($output), 5000);
279:
280:
281: if (!empty($ident)) {
282: static $idents = array();
283:
284:
285: if (isset($idents[$ident])) {
286: for ($i = $idents[$ident][0]; $i <= $idents[$ident][1]; $i++) {
287: header_remove('X-Wf-Jyxo-1-1-Jyxo' . $i);
288: }
289: }
290:
291:
292: $idents[$ident] = array($no + 1, $no + count($parts));
293: }
294:
295:
296: header('X-Wf-Protocol-Jyxo: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
297: header('X-Wf-Jyxo-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
298: header('X-Wf-Jyxo-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3');
299: foreach ($parts as $part) {
300: $no++;
301: header(sprintf('X-Wf-Jyxo-1-1-Jyxo%s: |%s|\\', $no, $part));
302: }
303:
304: header(sprintf('X-Wf-Jyxo-1-1-Jyxo%s: |%s|', $no, $part));
305:
306: return true;
307: }
308:
309: 310: 311: 312: 313:
314: private static function isInstalled()
315: {
316:
317: if (isset($_SERVER['HTTP_X_FIREPHP_VERSION'])) {
318: return true;
319: }
320:
321:
322: if (isset($_SERVER['HTTP_X_INSIGHT']) && 'activate' === $_SERVER['HTTP_X_INSIGHT']) {
323: return true;
324: }
325:
326:
327: if (isset($_SERVER['HTTP_USER_AGENT']) && false !== strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/')) {
328: return true;
329: }
330:
331:
332: return false;
333: }
334:
335: 336: 337: 338: 339: 340:
341: private static function replaceVariable($variable)
342: {
343: if (is_object($variable)) {
344: return 'object ' . get_class($variable);
345: } elseif (is_resource($variable)) {
346: return (string) $variable;
347: } elseif (is_array($variable)) {
348: foreach ($variable as $k => $v) {
349: unset($variable[$k]);
350: $variable[$k] = self::replaceVariable($v);
351: }
352: return $variable;
353: } else {
354: return $variable;
355: }
356: }
357:
358: 359: 360: 361: 362: 363: 364: 365: 366:
367: private static function encodeVariable($variable, $objectDepth = 1, $arrayDepth = 1, $totalDepth = 1)
368: {
369: static $maxObjectDepth = 5;
370: static $maxArrayDepth = 5;
371: static $maxTotalDepth = 10;
372: static $stack = array();
373:
374: if ($totalDepth > $maxTotalDepth) {
375: return sprintf('** Max Depth (%s) **', $maxTotalDepth);
376: }
377:
378: if (is_resource($variable)) {
379: return sprintf('** %s **', (string) $variable);
380: } elseif (is_object($variable)) {
381: if ($objectDepth > $maxObjectDepth) {
382: return sprintf('** Max Object Depth (%s) **', $maxObjectDepth);
383: }
384:
385: $class = get_class($variable);
386:
387:
388: foreach ($stack as $item) {
389: if ($item === $variable) {
390: return sprintf('** Recursion (%s) **', $class);
391: }
392: }
393: array_push($stack, $variable);
394:
395:
396: $return = array('__className' => $class);
397:
398:
399: $reflectionClass = new ReflectionClass($class);
400: foreach ($reflectionClass->getProperties() as $property) {
401: $name = $property->getName();
402: $rawName = $name;
403:
404: if ($property->isStatic()) {
405: $name = 'static:' . $name;
406: }
407:
408: if ($property->isPublic()) {
409: $name = 'public:' . $name;
410: } elseif ($property->isProtected()) {
411: $name = 'protected:' . $name;
412: $rawName = "\0" . '*' . "\0" . $rawName;
413: } elseif ($property->isPrivate()) {
414: $name = 'private:' . $name;
415: $rawName = "\0" . $class . "\0" . $rawName;
416: }
417:
418: if (!$property->isPublic()) {
419: $property->setAccessible(true);
420: }
421: $return[$name] = self::encodeVariable($property->getValue($variable), $objectDepth + 1, 1, $totalDepth + 1);
422: }
423:
424:
425: $members = (array) $variable;
426: foreach ($members as $rawName => $member) {
427: $name = $rawName;
428: if ("\0" === $name[0]) {
429: $parts = explode("\0", $name);
430: $name = $parts[2];
431: }
432: if (!$reflectionClass->hasProperty($name)) {
433: $name = 'undeclared:' . $name;
434: $return[$name] = self::encodeVariable($member, $objectDepth + 1, 1, $totalDepth + 1);
435: }
436: }
437: unset($members);
438:
439: array_pop($stack);
440:
441: return $return;
442: } elseif (is_array($variable)) {
443: if ($arrayDepth > $maxArrayDepth) {
444: return sprintf('** Max Array Depth (%s) **', $maxArrayDepth);
445: }
446:
447: $return = array();
448: foreach ($variable as $k => $v) {
449:
450: if ('GLOBALS' === $k && is_array($v) && array_key_exists('GLOBALS', $v)) {
451: $v['GLOBALS'] = '** Recursion (GLOBALS) **';
452: }
453: $return[$k] = self::encodeVariable($v, 1, $arrayDepth + 1, $totalDepth + 1);
454: }
455:
456: return $return;
457: } else {
458: return $variable;
459: }
460: }
461: }
462: