Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
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
use Monolog\Formatter\FormatterInterface;
16
use Monolog\Logger;
17
use Monolog\Utils;
18
use PhpConsole\Connector;
19
use PhpConsole\Handler as VendorPhpConsoleHandler;
20
use PhpConsole\Helper;
21
 
22
/**
23
 * Monolog handler for Google Chrome extension "PHP Console"
24
 *
25
 * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely
26
 *
27
 * Usage:
28
 * 1. Install Google Chrome extension [now dead and removed from the chrome store]
29
 * 2. See overview https://github.com/barbushin/php-console#overview
30
 * 3. Install PHP Console library https://github.com/barbushin/php-console#installation
31
 * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png)
32
 *
33
 *      $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler()));
34
 *      \Monolog\ErrorHandler::register($logger);
35
 *      echo $undefinedVar;
36
 *      $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012));
37
 *      PC::debug($_SERVER); // PHP Console debugger for any type of vars
38
 *
39
 * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
40
 *
41
 * @phpstan-import-type Record from \Monolog\Logger
42
 * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4
43
 */
44
class PHPConsoleHandler extends AbstractProcessingHandler
45
{
46
    /** @var array<string, mixed> */
47
    private $options = [
48
        'enabled' => true, // bool Is PHP Console server enabled
49
        'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
50
        'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled
51
        'useOwnErrorsHandler' => false, // bool Enable errors handling
52
        'useOwnExceptionsHandler' => false, // bool Enable exceptions handling
53
        'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths
54
        'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s')
55
        'serverEncoding' => null, // string|null Server internal encoding
56
        'headersLimit' => null, // int|null Set headers size limit for your web-server
57
        'password' => null, // string|null Protect PHP Console connection by password
58
        'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed
59
        'ipMasks' => [], // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1')
60
        'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required)
61
        'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings
62
        'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level
63
        'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number
64
        'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item
65
        'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON
66
        'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug
67
        'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ)
68
    ];
69
 
70
    /** @var Connector */
71
    private $connector;
72
 
73
    /**
74
     * @param  array<string, mixed> $options   See \Monolog\Handler\PHPConsoleHandler::$options for more details
75
     * @param  Connector|null       $connector Instance of \PhpConsole\Connector class (optional)
76
     * @throws \RuntimeException
77
     */
78
    public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
79
    {
80
        if (!class_exists('PhpConsole\Connector')) {
81
            throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
82
        }
83
        parent::__construct($level, $bubble);
84
        $this->options = $this->initOptions($options);
85
        $this->connector = $this->initConnector($connector);
86
    }
87
 
88
    /**
89
     * @param array<string, mixed> $options
90
     *
91
     * @return array<string, mixed>
92
     */
93
    private function initOptions(array $options): array
94
    {
95
        $wrongOptions = array_diff(array_keys($options), array_keys($this->options));
96
        if ($wrongOptions) {
97
            throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions));
98
        }
99
 
100
        return array_replace($this->options, $options);
101
    }
102
 
103
    private function initConnector(?Connector $connector = null): Connector
104
    {
105
        if (!$connector) {
106
            if ($this->options['dataStorage']) {
107
                Connector::setPostponeStorage($this->options['dataStorage']);
108
            }
109
            $connector = Connector::getInstance();
110
        }
111
 
112
        if ($this->options['registerHelper'] && !Helper::isRegistered()) {
113
            Helper::register();
114
        }
115
 
116
        if ($this->options['enabled'] && $connector->isActiveClient()) {
117
            if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) {
118
                $handler = VendorPhpConsoleHandler::getInstance();
119
                $handler->setHandleErrors($this->options['useOwnErrorsHandler']);
120
                $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
121
                $handler->start();
122
            }
123
            if ($this->options['sourcesBasePath']) {
124
                $connector->setSourcesBasePath($this->options['sourcesBasePath']);
125
            }
126
            if ($this->options['serverEncoding']) {
127
                $connector->setServerEncoding($this->options['serverEncoding']);
128
            }
129
            if ($this->options['password']) {
130
                $connector->setPassword($this->options['password']);
131
            }
132
            if ($this->options['enableSslOnlyMode']) {
133
                $connector->enableSslOnlyMode();
134
            }
135
            if ($this->options['ipMasks']) {
136
                $connector->setAllowedIpMasks($this->options['ipMasks']);
137
            }
138
            if ($this->options['headersLimit']) {
139
                $connector->setHeadersLimit($this->options['headersLimit']);
140
            }
141
            if ($this->options['detectDumpTraceAndSource']) {
142
                $connector->getDebugDispatcher()->detectTraceAndSource = true;
143
            }
144
            $dumper = $connector->getDumper();
145
            $dumper->levelLimit = $this->options['dumperLevelLimit'];
146
            $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit'];
147
            $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit'];
148
            $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit'];
149
            $dumper->detectCallbacks = $this->options['dumperDetectCallbacks'];
150
            if ($this->options['enableEvalListener']) {
151
                $connector->startEvalRequestsListener();
152
            }
153
        }
154
 
155
        return $connector;
156
    }
157
 
158
    public function getConnector(): Connector
159
    {
160
        return $this->connector;
161
    }
162
 
163
    /**
164
     * @return array<string, mixed>
165
     */
166
    public function getOptions(): array
167
    {
168
        return $this->options;
169
    }
170
 
171
    public function handle(array $record): bool
172
    {
173
        if ($this->options['enabled'] && $this->connector->isActiveClient()) {
174
            return parent::handle($record);
175
        }
176
 
177
        return !$this->bubble;
178
    }
179
 
180
    /**
181
     * Writes the record down to the log of the implementing handler
182
     */
183
    protected function write(array $record): void
184
    {
185
        if ($record['level'] < Logger::NOTICE) {
186
            $this->handleDebugRecord($record);
187
        } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
188
            $this->handleExceptionRecord($record);
189
        } else {
190
            $this->handleErrorRecord($record);
191
        }
192
    }
193
 
194
    /**
195
     * @phpstan-param Record $record
196
     */
197
    private function handleDebugRecord(array $record): void
198
    {
199
        $tags = $this->getRecordTags($record);
200
        $message = $record['message'];
201
        if ($record['context']) {
202
            $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true);
203
        }
204
        $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
205
    }
206
 
207
    /**
208
     * @phpstan-param Record $record
209
     */
210
    private function handleExceptionRecord(array $record): void
211
    {
212
        $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
213
    }
214
 
215
    /**
216
     * @phpstan-param Record $record
217
     */
218
    private function handleErrorRecord(array $record): void
219
    {
220
        $context = $record['context'];
221
 
222
        $this->connector->getErrorsDispatcher()->dispatchError(
223
            $context['code'] ?? null,
224
            $context['message'] ?? $record['message'],
225
            $context['file'] ?? null,
226
            $context['line'] ?? null,
227
            $this->options['classesPartialsTraceIgnore']
228
        );
229
    }
230
 
231
    /**
232
     * @phpstan-param Record $record
233
     * @return string
234
     */
235
    private function getRecordTags(array &$record)
236
    {
237
        $tags = null;
238
        if (!empty($record['context'])) {
239
            $context = & $record['context'];
240
            foreach ($this->options['debugTagsKeysInContext'] as $key) {
241
                if (!empty($context[$key])) {
242
                    $tags = $context[$key];
243
                    if ($key === 0) {
244
                        array_shift($context);
245
                    } else {
246
                        unset($context[$key]);
247
                    }
248
                    break;
249
                }
250
            }
251
        }
252
 
253
        return $tags ?: strtolower($record['level_name']);
254
    }
255
 
256
    /**
257
     * {@inheritDoc}
258
     */
259
    protected function getDefaultFormatter(): FormatterInterface
260
    {
261
        return new LineFormatter('%message%');
262
    }
263
}