]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / vendor / monolog / monolog / src / Monolog / Handler / BrowserConsoleHandler.php
1 <?php
2
3 /*
4  * This file is part of the Monolog package.
5  *
6  * (c) Jordi Boggiano <j.boggiano@seld.be>
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 Monolog\Handler;
13
14 use Monolog\Formatter\LineFormatter;
15
16 /**
17  * Handler sending logs to browser's javascript console with no browser extension required
18  *
19  * @author Olivier Poitrey <rs@dailymotion.com>
20  */
21 class BrowserConsoleHandler extends AbstractProcessingHandler
22 {
23     protected static $initialized = false;
24     protected static $records = array();
25
26     /**
27      * {@inheritDoc}
28      *
29      * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
30      *
31      * Example of formatted string:
32      *
33      *     You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white}
34      */
35     protected function getDefaultFormatter()
36     {
37         return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%');
38     }
39
40     /**
41      * {@inheritDoc}
42      */
43     protected function write(array $record)
44     {
45         // Accumulate records
46         self::$records[] = $record;
47
48         // Register shutdown handler if not already done
49         if (!self::$initialized) {
50             self::$initialized = true;
51             $this->registerShutdownFunction();
52         }
53     }
54
55     /**
56      * Convert records to javascript console commands and send it to the browser.
57      * This method is automatically called on PHP shutdown if output is HTML or Javascript.
58      */
59     public static function send()
60     {
61         $format = self::getResponseFormat();
62         if ($format === 'unknown') {
63             return;
64         }
65
66         if (count(self::$records)) {
67             if ($format === 'html') {
68                 self::writeOutput('<script>' . self::generateScript() . '</script>');
69             } elseif ($format === 'js') {
70                 self::writeOutput(self::generateScript());
71             }
72             self::reset();
73         }
74     }
75
76     /**
77      * Forget all logged records
78      */
79     public static function reset()
80     {
81         self::$records = array();
82     }
83
84     /**
85      * Wrapper for register_shutdown_function to allow overriding
86      */
87     protected function registerShutdownFunction()
88     {
89         if (PHP_SAPI !== 'cli') {
90             register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send'));
91         }
92     }
93
94     /**
95      * Wrapper for echo to allow overriding
96      *
97      * @param string $str
98      */
99     protected static function writeOutput($str)
100     {
101         echo $str;
102     }
103
104     /**
105      * Checks the format of the response
106      *
107      * If Content-Type is set to application/javascript or text/javascript -> js
108      * If Content-Type is set to text/html, or is unset -> html
109      * If Content-Type is anything else -> unknown
110      *
111      * @return string One of 'js', 'html' or 'unknown'
112      */
113     protected static function getResponseFormat()
114     {
115         // Check content type
116         foreach (headers_list() as $header) {
117             if (stripos($header, 'content-type:') === 0) {
118                 // This handler only works with HTML and javascript outputs
119                 // text/javascript is obsolete in favour of application/javascript, but still used
120                 if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) {
121                     return 'js';
122                 }
123                 if (stripos($header, 'text/html') === false) {
124                     return 'unknown';
125                 }
126                 break;
127             }
128         }
129
130         return 'html';
131     }
132
133     private static function generateScript()
134     {
135         $script = array();
136         foreach (self::$records as $record) {
137             $context = self::dump('Context', $record['context']);
138             $extra = self::dump('Extra', $record['extra']);
139
140             if (empty($context) && empty($extra)) {
141                 $script[] = self::call_array('log', self::handleStyles($record['formatted']));
142             } else {
143                 $script = array_merge($script,
144                     array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))),
145                     $context,
146                     $extra,
147                     array(self::call('groupEnd'))
148                 );
149             }
150         }
151
152         return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
153     }
154
155     private static function handleStyles($formatted)
156     {
157         $args = array(self::quote('font-weight: normal'));
158         $format = '%c' . $formatted;
159         preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
160
161         foreach (array_reverse($matches) as $match) {
162             $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0]));
163             $args[] = '"font-weight: normal"';
164
165             $pos = $match[0][1];
166             $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
167         }
168
169         array_unshift($args, self::quote($format));
170
171         return $args;
172     }
173
174     private static function handleCustomStyles($style, $string)
175     {
176         static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey');
177         static $labels = array();
178
179         return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) {
180             if (trim($m[1]) === 'autolabel') {
181                 // Format the string as a label with consistent auto assigned background color
182                 if (!isset($labels[$string])) {
183                     $labels[$string] = $colors[count($labels) % count($colors)];
184                 }
185                 $color = $labels[$string];
186
187                 return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px";
188             }
189
190             return $m[1];
191         }, $style);
192     }
193
194     private static function dump($title, array $dict)
195     {
196         $script = array();
197         $dict = array_filter($dict);
198         if (empty($dict)) {
199             return $script;
200         }
201         $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title));
202         foreach ($dict as $key => $value) {
203             $value = json_encode($value);
204             if (empty($value)) {
205                 $value = self::quote('');
206             }
207             $script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value);
208         }
209
210         return $script;
211     }
212
213     private static function quote($arg)
214     {
215         return '"' . addcslashes($arg, "\"\n\\") . '"';
216     }
217
218     private static function call()
219     {
220         $args = func_get_args();
221         $method = array_shift($args);
222
223         return self::call_array($method, $args);
224     }
225
226     private static function call_array($method, array $args)
227     {
228         return 'c.' . $method . '(' . implode(', ', $args) . ');';
229     }
230 }