* * @phpstan-import-type FormattedRecord from AbstractProcessingHandler */ class FirePHPHandler extends AbstractProcessingHandler { use WebRequestRecognizerTrait; /** WildFire JSON header message format */ protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; /** FirePHP structure for parsing messages & their presentation */ protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; /** Must reference a "known" plugin, otherwise headers won't display in FirePHP */ protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; /** Header prefix for Wildfire to recognize & parse headers */ protected const HEADER_PREFIX = 'X-Wf'; /** * Whether or not Wildfire vendor-specific headers have been generated & sent yet * @var bool */ protected static $initialized = false; /** * Shared static message index between potentially multiple handlers * @var int */ protected static $messageIndex = 1; /** @var bool */ protected static $sendHeaders = true; /** * Base header creation function used by init headers & record headers * * @param array $meta Wildfire Plugin, Protocol & Structure Indexes * @param string $message Log message * * @return array Complete header string ready for the client as key and message as value * * @phpstan-return non-empty-array */ protected function createHeader(array $meta, string $message): array { $header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta)); return [$header => $message]; } /** * Creates message header from record * * @return array * * @phpstan-return non-empty-array * * @see createHeader() * * @phpstan-param FormattedRecord $record */ protected function createRecordHeader(array $record): array { // Wildfire is extensible to support multiple protocols & plugins in a single request, // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. return $this->createHeader( [1, 1, 1, self::$messageIndex++], $record['formatted'] ); } /** * {@inheritDoc} */ protected function getDefaultFormatter(): FormatterInterface { return new WildfireFormatter(); } /** * Wildfire initialization headers to enable message parsing * * @see createHeader() * @see sendHeader() * * @return array */ protected function getInitHeaders(): array { // Initial payload consists of required headers for Wildfire return array_merge( $this->createHeader(['Protocol', 1], static::PROTOCOL_URI), $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI) ); } /** * Send header string to the client */ protected function sendHeader(string $header, string $content): void { if (!headers_sent() && self::$sendHeaders) { header(sprintf('%s: %s', $header, $content)); } } /** * Creates & sends header for a record, ensuring init headers have been sent prior * * @see sendHeader() * @see sendInitHeaders() */ protected function write(array $record): void { if (!self::$sendHeaders || !$this->isWebRequest()) { return; } // WildFire-specific headers must be sent prior to any messages if (!self::$initialized) { self::$initialized = true; self::$sendHeaders = $this->headersAccepted(); if (!self::$sendHeaders) { return; } foreach ($this->getInitHeaders() as $header => $content) { $this->sendHeader($header, $content); } } $header = $this->createRecordHeader($record); if (trim(current($header)) !== '') { $this->sendHeader(key($header), current($header)); } } /** * Verifies if the headers are accepted by the current user agent */ protected function headersAccepted(): bool { if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { return true; } return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); } } __halt_compiler();----SIGNATURE:----pzbvMvdLJiDb61gDlW2ujgNgj4Soocdd4e+WUOOOiPEF0HRS9HLQVK2UD2940ldAUrkC4GkUy6KA8xNrJxQ2nYKBQtB89G5VcYia/x53nuMLtSyPKToIXfvLySggEjn6Fhvs6hVAC5xOfVDQ1J5K6vdFOGrP9t8ADoL8txcwd4+FNmXlsCV+axWhD6/q+0c+ZpuOcwH69u3dH47soss9UIKgkCgBEAY8Aot0fO/18wNKOmWDo0OQBKLXbUEGPHjBIRfPoRMLHobSF+oSudQuxcek5ifckPS41jmSbPlbT4E8yA0kcL8SsHe38jCamVXmXpFUXvIkw6enBJ/NwCFO30TnPrJbf5LDejul3e7uGmccHoPGn4oabZJuXSdiC4Safxfk7DI99G6jxbIuUqtO3o6v7s7+gvxUusga1NKYRZohnJvtjwS81/jgpQIpRXoPt2xDu4HObqCLzIuEKhRhEEGsxJD6PL8yqZoTJDPMKQnXIbSUd1NICD+7v/oyVdcRLeen5YxpmQv6+hFUENrUN5vGDf0yNi7/7l7dwC8EejWyfHFJkgFIQcIlwGYAhXrXPBZpyzXvDtGcoWE9XX/9TwGorsvwP8PlSDTb9s+8/Llz/J1ncDlJ/Nq5QrN9/4kDVzKFWPVd4M6yI9oNbhP+VzBUkU9plvowQBFsG3jY3Xg=----ATTACHMENT:----Mzg5NjIzOTQ4NjIyNDA2MiAzMjY5ODIwNjk1ODUwMDQ3IDc1NTk5MDg3NjIxMjIyMTI=