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