Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • ActiveRecord
  • Cache
  • Configs
  • Core
  • DateTime
  • Debug
  • DeleteOperation
  • Errors
  • Event
  • EventHook
  • Events
  • FileCache
  • FormattedString
  • Helpers
  • I18n
  • Image
  • Inflections
  • Inflector
  • Models
  • Module
  • Modules
  • Object
  • Operation
  • PingOperation
  • Prototype
  • Route
  • Routes
  • SaveOperation
  • Session
  • TimeZone
  • TimeZoneLocation
  • Uploaded
  • Vars
  • VarsIterator

Interfaces

  • StorageInterface
  • ToArray
  • ToArrayRecursive

Traits

  • PrototypeTrait
  • ToArrayRecursiveTrait

Exceptions

  • AlreadyAuthenticated
  • AuthenticationRequired
  • Exception
  • ModuleConstructorMissing
  • ModuleIsDisabled
  • ModuleNotDefined
  • OffsetError
  • OffsetNotDefined
  • OffsetNotReadable
  • OffsetNotWritable
  • PermissionRequired
  • PropertyError
  • PropertyIsReserved
  • PropertyNotDefined
  • PropertyNotReadable
  • PropertyNotWritable
  • RouteNotDefined
  • SecurityException

Constants

  • TOKEN_ALPHA
  • TOKEN_ALPHA_UPCASE
  • TOKEN_NUMERIC
  • TOKEN_SYMBOL
  • TOKEN_SYMBOL_WIDE

Functions

  • array_flatten
  • array_insert
  • array_merge_recursive
  • camelize
  • capitalize
  • downcase
  • dump
  • escape
  • escape_all
  • exact_array_merge_recursive
  • excerpt
  • format
  • generate_token
  • generate_token_wide
  • generate_v4_uuid
  • get_autoconfig
  • humanize
  • hyphenate
  • log
  • log_error
  • log_info
  • log_success
  • log_time
  • normalize
  • normalize_namespace_part
  • normalize_url_path
  • pbkdf2
  • pluralize
  • remove_accents
  • shorten
  • singularize
  • sort_by_weight
  • stable_sort
  • strip_root
  • titleize
  • unaccent_compare
  • unaccent_compare_ci
  • underscore
  • upcase
  1 <?php
  2 
  3 /*
  4  * This file is part of the ICanBoogie package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace ICanBoogie;
 13 
 14 class Debug
 15 {
 16     const MODE_DEV = 'dev';
 17     const MODE_TEST = 'test';
 18     const MODE_PRODUCTION = 'production';
 19 
 20     const MAX_MESSAGES = 100;
 21 
 22     static public $mode = 'dev';
 23 
 24     /**
 25      * Last error.
 26      *
 27      * @var array[string]mixed
 28      */
 29     static public $last_error;
 30 
 31     /**
 32      * Last error message.
 33      *
 34      * @var string
 35      */
 36     static public $last_error_message;
 37 
 38     static public function synthesize_config(array $fragments)
 39     {
 40         $config = call_user_func_array('ICanBoogie\array_merge_recursive', $fragments);
 41         $config = array_merge($config, $config['modes'][$config['mode']]);
 42 
 43         return $config;
 44     }
 45 
 46     static private $config;
 47 
 48     static private $config_code_sample = true;
 49     static private $config_line_number = true;
 50     static private $config_report = false;
 51     static private $config_report_address = null;
 52     static private $config_stack_trace = true;
 53     static private $config_exception_chain = true;
 54     static private $config_verbose = true;
 55 
 56     static public function is_dev()
 57     {
 58         return self::$mode == self::MODE_DEV;
 59     }
 60 
 61     static public function is_test()
 62     {
 63         return self::$mode == self::MODE_TEST;
 64     }
 65 
 66     static public function is_production()
 67     {
 68         return self::$mode == self::MODE_PRODUCTION;
 69     }
 70 
 71     /**
 72      * Configures the class.
 73      *
 74      * @param array $config A config such as one returned by `$core->configs['debug']`.
 75      */
 76     static public function configure(array $config)
 77     {
 78         $mode = self::$mode;
 79         $modes = [];
 80 
 81         foreach ($config as $directive => $value)
 82         {
 83             if ($directive == 'mode')
 84             {
 85                 $mode = $value;
 86 
 87                 continue;
 88             }
 89             else if ($directive == 'modes')
 90             {
 91                 $modes = $value;
 92 
 93                 continue;
 94             }
 95 
 96             $directive = 'config_' . $directive;
 97 
 98             self::$$directive = $value;
 99         }
100 
101         self::$mode = $mode;
102 
103         if (isset($modes[$mode]))
104         {
105             foreach ($modes[$mode] as $directive => $value)
106             {
107                 $directive = 'config_' . $directive;
108 
109                 self::$$directive = $value;
110             }
111         }
112     }
113 
114     /**
115      * Stores logged messages in the session and report fatal errors.
116      */
117     static public function shutdown_handler()
118     {
119         global $core;
120 
121         if (self::$logs)
122         {
123             if (!headers_sent() && isset($core))
124             {
125                 $core->session;
126             }
127 
128             $_SESSION['alerts'] = self::$logs;
129         }
130 
131         $error = error_get_last();
132 
133         if ($error && $error['type'] == E_ERROR)
134         {
135             $message = self::format_alert($error);
136 
137             self::report($message);
138         }
139     }
140 
141     static public function restore_logs(Event $event, Session $session)
142     {
143         if ($session->alerts)
144         {
145             self::$logs = array_merge($session->alerts, self::$logs);
146         }
147 
148         $session->alerts = [];
149     }
150 
151     /*
152     **
153 
154     DEBUG & TRACE
155 
156     **
157     */
158 
159     /**
160      * Handles errors.
161      *
162      * The {@link $last_error} and {@link $last_error_message} properties are updated.
163      *
164      * The alert is formatted, reported and if the `verbose` option is true the alert is displayed.
165      *
166      * @param int $no The level of the error raised.
167      * @param string $str The error message.
168      * @param string $file The filename that the error was raised in.
169      * @param int $line The line number the error was raised at.
170      * @param array $context The active symbol table at the point the error occurred.
171      */
172     static public function error_handler($no, $str, $file, $line, $context)
173     {
174         if (!headers_sent())
175         {
176             header('HTTP/1.0 500 ' . strip_tags($str));
177         }
178 
179         $trace = debug_backtrace();
180 
181         array_shift($trace); // remove the trace of our function
182 
183         $error = [
184 
185             'type' => $no,
186             'message' => $str,
187             'file' => $file,
188             'line' => $line,
189             'context' => $context,
190             'trace' => $trace
191 
192         ];
193 
194         self::$last_error = $error;
195         self::$last_error_message = $str;
196 
197         $message = self::format_alert($error);
198 
199         self::report($message);
200 
201         if (self::$config_verbose)
202         {
203             echo $message;
204 
205             flush();
206         }
207     }
208 
209     /**
210      * Basic exception handler.
211      *
212      * @param \Exception $exception
213      */
214     static public function exception_handler(\Exception $exception)
215     {
216         if (!headers_sent())
217         {
218             $code = $exception->getCode();
219 
220             $message = $exception->getMessage();
221             $message = strip_tags($message);
222             $message = str_replace([ "\r\n", "\n" ], '', $message);
223 
224             header("HTTP/1.0 $code $message");
225         }
226 
227         $message = self::format_alert($exception);
228 
229         self::report($message);
230 
231         exit($message);
232     }
233 
234     const MAX_STRING_LEN = 16;
235 
236     static private $error_names = [
237 
238         E_ERROR => 'Error',
239         E_WARNING => 'Warning',
240         E_PARSE => 'Parse error',
241         E_NOTICE => 'Notice'
242 
243     ];
244 
245     /**
246      * Formats an alert into a HTML element.
247      *
248      * An alert can be an exception or an array representing an error triggered with the
249      * trigger_error() function.
250      *
251      * @param \Exception|array $alert
252      *
253      * @return string
254      */
255     static public function format_alert($alert)
256     {
257         $type = 'Error';
258         $class = 'error';
259         $file = null;
260         $line = null;
261         $message = null;
262         $trace = null;
263         $more = null;
264 
265         if (is_array($alert))
266         {
267             $file = $alert['file'];
268             $line = $alert['line'];
269             $message = $alert['message'];
270 
271             if (isset(self::$error_names[$alert['type']]))
272             {
273                 $type = self::$error_names[$alert['type']];
274             }
275 
276             if (isset($alert['trace']))
277             {
278                 $trace = $alert['trace'];
279             }
280         }
281         else if ($alert instanceof \Exception)
282         {
283             $type = get_class($alert);
284             $class = 'exception';
285             $file = $alert->getFile();
286             $line = $alert->getLine();
287             $message = $alert->getMessage();
288             $trace = $alert->getTrace();
289         }
290 
291         $message = strip_tags($message, '<a><em><q><strong>');
292 
293         if ($trace)
294         {
295             $more .= self::format_trace($trace);
296         }
297 
298         if (is_array($alert) && $file)
299         {
300             $more .= self::format_code_sample($file, $line);
301         }
302 
303         $file = strip_root($file);
304 
305         $previous = null;
306 
307         if (/*self::$config_exception_chain &&*/ $alert instanceof \Exception)
308         {
309             $previous = $alert->getPrevious();
310 
311             if ($previous)
312             {
313                 $previous = self::format_alert($previous);
314             }
315         }
316 
317         return <<<EOT
318 <pre class="alert alert-error $class">
319 <strong>$type with the following message:</strong>
320 
321 $message
322 
323 in <em>$file</em> at line <em>$line</em>$more{$previous}
324 </pre>
325 EOT;
326     }
327 
328     /**
329      * Formats a stack trace into an HTML element.
330      *
331      * @param array $trace
332      *
333      * @return string
334      */
335     static public function format_trace(array $trace)
336     {
337         $root = str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']);
338         $count = count($trace);
339         $count_max = strlen((string) $count);
340 
341         $rc = "\n\n<strong>Stack trace:</strong>\n";
342 
343         foreach ($trace as $i => $node)
344         {
345             $trace_file = null;
346             $trace_line = 0;
347             $trace_class = null;
348             $trace_type = null;
349             $trace_args = null;
350 
351             extract($node, EXTR_PREFIX_ALL, 'trace');
352 
353             if ($trace_file)
354             {
355                 $trace_file = str_replace('\\', '/', $trace_file);
356                 $trace_file = str_replace($root, '', $trace_file);
357             }
358 
359             $params = [];
360 
361             if ($trace_args)
362             {
363                 foreach ($trace_args as $arg)
364                 {
365                     switch (gettype($arg))
366                     {
367                         case 'array': $arg = 'Array'; break;
368                         case 'object': $arg = 'Object of ' . get_class($arg); break;
369                         case 'resource': $arg = 'Resource of type ' . get_resource_type($arg); break;
370                         case 'null': $arg = 'null'; break;
371 
372                         default:
373                         {
374                             if (strlen($arg) > self::MAX_STRING_LEN)
375                             {
376                                 $arg = substr($arg, 0, self::MAX_STRING_LEN) . '...';
377                             }
378 
379                             $arg = '\'' . $arg .'\'';
380                         }
381                         break;
382                     }
383 
384                     $params[] = $arg;
385                 }
386             }
387 
388             $rc .= sprintf
389             (
390                 "\n%{$count_max}d. %s(%d): %s%s%s(%s)",
391 
392                 $count - $i, $trace_file, $trace_line, $trace_class, $trace_type,
393                 $trace_function, escape(implode(', ', $params))
394             );
395         }
396 
397         return $rc;
398     }
399 
400     /**
401      * Extracts and formats a code sample around the line that triggered the alert.
402      *
403      * @param string $file
404      * @param int $line
405      *
406      * @return string
407      */
408     static public function format_code_sample($file, $line=0)
409     {
410         $sample = '';
411         $fh = new \SplFileObject($file);
412         $lines = new \LimitIterator($fh, $line < 5 ? 0 : $line - 5, 10);
413 
414         foreach ($lines as $i => $str)
415         {
416             $i++;
417 
418             $str = escape(rtrim($str));
419 
420             if ($i == $line)
421             {
422                 $str = '<ins>' . $str . '</ins>';
423             }
424 
425             $str = str_replace("\t", "\xC2\xA0\xC2\xA0\xC2\xA0\xC2\xA0", $str);
426             $sample .= sprintf("\n%6d. %s", $i, $str);
427         }
428 
429         return "\n\n<strong>Code sample:</strong>\n$sample";
430     }
431 
432     /**
433      * Reports the alert to the admin of the website.
434      *
435      * The method sends an email to the admin of the website defined whose email address is defined
436      * in the debug config using the "report_address" key.
437      *
438      * @param string $message
439      */
440     static public function report($message)
441     {
442         $report_address = self::$config_report_address;
443 
444         if (!$report_address)
445         {
446             return;
447         }
448 
449         $more = "\n\n<strong>Request URI:</strong>\n\n" . escape($_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI']);
450 
451         if (!empty($_SERVER['HTTP_REFERER']))
452         {
453             $more .= "\n\n<strong>Referer:</strong>\n\n" . escape($_SERVER['HTTP_REFERER']);
454         }
455 
456         if (!empty($_SERVER['HTTP_USER_AGENT']))
457         {
458             $more .= "\n\n<strong>User Agent:</strong>\n\n" . escape($_SERVER['HTTP_USER_AGENT']);
459         }
460 
461         $more .= "\n\n<strong>Remote address:</strong>\n\n" . escape($_SERVER['REMOTE_ADDR']);
462 
463         if ($message instanceof \Exception)
464         {
465             $message = self::format_alert($message);
466         }
467 
468         $message = str_replace('</pre>', '', $message);
469         $message = trim($message) . $more . '</pre>';
470 
471         #
472         # during the same session, same messages are only reported once
473         #
474 
475         $hash = md5($message);
476 
477         if (isset($_SESSION['wddebug']['reported'][$hash]))
478         {
479             return;
480         }
481 
482         $_SESSION['wddebug']['reported'][$hash] = true;
483 
484         #
485         #
486         #
487 
488         $host = $_SERVER['SERVER_NAME'];
489         $host = str_replace('www.', '', $host);
490 
491         $parts = [
492 
493             'From' => 'icanboogie@' . $host,
494             'Content-Type' => 'text/html; charset=' . CHARSET
495 
496         ];
497 
498         $headers = '';
499 
500         foreach ($parts as $key => $value)
501         {
502             $headers .= $key .= ': ' . $value . "\r\n";
503         }
504 
505         mail($report_address, __CLASS__ . ': Report from ' . $host, $message, $headers);
506     }
507 
508     static public $logs = [];
509 
510     static public function log($type, $message, array $params=[], $message_id=null)
511     {
512         if (empty(self::$logs[$type]))
513         {
514             self::$logs[$type] = [];
515         }
516 
517         #
518         # limit the number of messages
519         #
520 
521         $messages = &self::$logs[$type];
522 
523         if ($messages)
524         {
525             $max = self::MAX_MESSAGES;
526             $count = count($messages);
527 
528             if ($count > $max)
529             {
530                 $messages = array_splice($messages, $count - $max);
531 
532                 array_unshift($messages, [ '*** SLICED', [] ]);
533             }
534         }
535 
536         $message_id ? $messages[$message_id] = [ $message, $params ] : $messages[] = [ $message, $params ];
537     }
538 
539     /**
540      * Returns the messages available in a given log.
541      *
542      * @param string $type The log type.
543      *
544      * @return array The messages available in the given log.
545      */
546     static public function get_messages($type)
547     {
548         if (empty(self::$logs[$type]))
549         {
550             return [];
551         }
552 
553         $rc = [];
554 
555         foreach (self::$logs[$type] as $message)
556         {
557             list($message, $args) = $message;
558 
559             if ($args)
560             {
561                 $message = format($message, $args);
562             }
563 
564             $rc[] = (string) $message;
565         }
566 
567         return $rc;
568     }
569 
570     /**
571      * Similar to the {@link get_message()} method, the method returns the messages available in a
572      * given log, but clear the log after the messages have been extracted.
573      *
574      * @param string $type
575      *
576      * @return array The messages fetched from the given log.
577      */
578     static public function fetch_messages($type)
579     {
580         $rc = self::get_messages($type);
581 
582         self::$logs[$type] = [];
583 
584         return $rc;
585     }
586 
587     /**
588      * Removes the DOCUMENT_ROOT part from the provided path.
589      *
590      * @param string $path
591      *
592      * @return string
593      */
594     static private function strip_root($path)
595     {
596         $root = DOCUMENT_ROOT;
597 
598         if (strpos($path, $root) === 0)
599         {
600             return substr($path, strlen($root) - 1);
601         }
602 
603         return $path;
604     }
605 }
Autodoc API documentation generated by ApiGen 2.8.0