PK ZdOutputFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; use Symfony\Component\Console\Exception\InvalidArgumentException; /** * Formatter class for console output. * * @author Konstantin Kudryashov * @author Roland Franssen */ class OutputFormatter implements WrappableOutputFormatterInterface { private bool $decorated; private array $styles = []; private OutputFormatterStyleStack $styleStack; public function __clone() { $this->styleStack = clone $this->styleStack; foreach ($this->styles as $key => $value) { $this->styles[$key] = clone $value; } } /** * Escapes "<" and ">" special chars in given text. */ public static function escape(string $text): string { $text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text); return self::escapeTrailingBackslash($text); } /** * Escapes trailing "\" in given text. * * @internal */ public static function escapeTrailingBackslash(string $text): string { if (str_ends_with($text, '\\')) { $len = \strlen($text); $text = rtrim($text, '\\'); $text = str_replace("\0", '', $text); $text .= str_repeat("\0", $len - \strlen($text)); } return $text; } /** * Initializes console output formatter. * * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances */ public function __construct(bool $decorated = false, array $styles = []) { $this->decorated = $decorated; $this->setStyle('error', new OutputFormatterStyle('white', 'red')); $this->setStyle('info', new OutputFormatterStyle('green')); $this->setStyle('comment', new OutputFormatterStyle('yellow')); $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); foreach ($styles as $name => $style) { $this->setStyle($name, $style); } $this->styleStack = new OutputFormatterStyleStack(); } /** * @return void */ public function setDecorated(bool $decorated) { $this->decorated = $decorated; } public function isDecorated(): bool { return $this->decorated; } /** * @return void */ public function setStyle(string $name, OutputFormatterStyleInterface $style) { $this->styles[strtolower($name)] = $style; } public function hasStyle(string $name): bool { return isset($this->styles[strtolower($name)]); } public function getStyle(string $name): OutputFormatterStyleInterface { if (!$this->hasStyle($name)) { throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); } return $this->styles[strtolower($name)]; } public function format(?string $message): ?string { return $this->formatAndWrap($message, 0); } /** * @return string */ public function formatAndWrap(?string $message, int $width) { if (null === $message) { return ''; } $offset = 0; $output = ''; $openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; $closeTagRegex = '[a-z][^<>]*+'; $currentLineLength = 0; preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE); foreach ($matches[0] as $i => $match) { $pos = $match[1]; $text = $match[0]; if (0 != $pos && '\\' == $message[$pos - 1]) { continue; } // add the text up to the next tag $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength); $offset = $pos + \strlen($text); // opening tag? if ($open = '/' !== $text[1]) { $tag = $matches[1][$i][0]; } else { $tag = $matches[3][$i][0] ?? ''; } if (!$open && !$tag) { // $this->styleStack->pop(); } elseif (null === $style = $this->createStyleFromString($tag)) { $output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength); } elseif ($open) { $this->styleStack->push($style); } else { $this->styleStack->pop($style); } } $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength); return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']); } public function getStyleStack(): OutputFormatterStyleStack { return $this->styleStack; } /** * Tries to create new style instance from string. */ private function createStyleFromString(string $string): ?OutputFormatterStyleInterface { if (isset($this->styles[$string])) { return $this->styles[$string]; } if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) { return null; } $style = new OutputFormatterStyle(); foreach ($matches as $match) { array_shift($match); $match[0] = strtolower($match[0]); if ('fg' == $match[0]) { $style->setForeground(strtolower($match[1])); } elseif ('bg' == $match[0]) { $style->setBackground(strtolower($match[1])); } elseif ('href' === $match[0]) { $url = preg_replace('{\\\\([<>])}', '$1', $match[1]); $style->setHref($url); } elseif ('options' === $match[0]) { preg_match_all('([^,;]+)', strtolower($match[1]), $options); $options = array_shift($options); foreach ($options as $option) { $style->setOption($option); } } else { return null; } } return $style; } /** * Applies current style from stack to text, if must be applied. */ private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string { if ('' === $text) { return ''; } if (!$width) { return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text; } if (!$currentLineLength && '' !== $current) { $text = ltrim($text); } if ($currentLineLength) { $prefix = substr($text, 0, $i = $width - $currentLineLength)."\n"; $text = substr($text, $i); } else { $prefix = ''; } preg_match('~(\\n)$~', $text, $matches); $text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text); $text = rtrim($text, "\n").($matches[1] ?? ''); if (!$currentLineLength && '' !== $current && !str_ends_with($current, "\n")) { $text = "\n".$text; } $lines = explode("\n", $text); foreach ($lines as $line) { $currentLineLength += \strlen($line); if ($width <= $currentLineLength) { $currentLineLength = 0; } } if ($this->isDecorated()) { foreach ($lines as $i => $line) { $lines[$i] = $this->styleStack->getCurrent()->apply($line); } } return implode("\n", $lines); } } PK Z`)OutputFormatterInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; /** * Formatter interface for console output. * * @author Konstantin Kudryashov */ interface OutputFormatterInterface { /** * Sets the decorated flag. * * @return void */ public function setDecorated(bool $decorated); /** * Whether the output will decorate messages. */ public function isDecorated(): bool; /** * Sets a new style. * * @return void */ public function setStyle(string $name, OutputFormatterStyleInterface $style); /** * Checks if output formatter has style with specified name. */ public function hasStyle(string $name): bool; /** * Gets style options from style with specified name. * * @throws \InvalidArgumentException When style isn't defined */ public function getStyle(string $name): OutputFormatterStyleInterface; /** * Formats a message according to the given styles. */ public function format(?string $message): ?string; } PK Z%NullOutputFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; /** * @author Tien Xuan Vo */ final class NullOutputFormatter implements OutputFormatterInterface { private NullOutputFormatterStyle $style; public function format(?string $message): ?string { return null; } public function getStyle(string $name): OutputFormatterStyleInterface { // to comply with the interface we must return a OutputFormatterStyleInterface return $this->style ??= new NullOutputFormatterStyle(); } public function hasStyle(string $name): bool { return false; } public function isDecorated(): bool { return false; } public function setDecorated(bool $decorated): void { // do nothing } public function setStyle(string $name, OutputFormatterStyleInterface $style): void { // do nothing } } PK Z#ó%WrappableOutputFormatterInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; /** * Formatter interface for console output that supports word wrapping. * * @author Roland Franssen */ interface WrappableOutputFormatterInterface extends OutputFormatterInterface { /** * Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping). * * @return string */ public function formatAndWrap(?string $message, int $width); } PK Zls OutputFormatterStyle.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; use Symfony\Component\Console\Color; /** * Formatter style class for defining styles. * * @author Konstantin Kudryashov */ class OutputFormatterStyle implements OutputFormatterStyleInterface { private Color $color; private string $foreground; private string $background; private array $options; private ?string $href = null; private bool $handlesHrefGracefully; /** * Initializes output formatter style. * * @param string|null $foreground The style foreground color name * @param string|null $background The style background color name */ public function __construct(string $foreground = null, string $background = null, array $options = []) { $this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options); } /** * @return void */ public function setForeground(string $color = null) { if (1 > \func_num_args()) { trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options); } /** * @return void */ public function setBackground(string $color = null) { if (1 > \func_num_args()) { trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options); } public function setHref(string $url): void { $this->href = $url; } /** * @return void */ public function setOption(string $option) { $this->options[] = $option; $this->color = new Color($this->foreground, $this->background, $this->options); } /** * @return void */ public function unsetOption(string $option) { $pos = array_search($option, $this->options); if (false !== $pos) { unset($this->options[$pos]); } $this->color = new Color($this->foreground, $this->background, $this->options); } /** * @return void */ public function setOptions(array $options) { $this->color = new Color($this->foreground, $this->background, $this->options = $options); } public function apply(string $text): string { $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); if (null !== $this->href && $this->handlesHrefGracefully) { $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\"; } return $this->color->apply($text); } } PK Z&!OutputFormatterStyleInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; /** * Formatter style interface for defining styles. * * @author Konstantin Kudryashov */ interface OutputFormatterStyleInterface { /** * Sets style foreground color. * * @return void */ public function setForeground(?string $color); /** * Sets style background color. * * @return void */ public function setBackground(?string $color); /** * Sets some specific style option. * * @return void */ public function setOption(string $option); /** * Unsets some specific style option. * * @return void */ public function unsetOption(string $option); /** * Sets multiple style options at once. * * @return void */ public function setOptions(array $options); /** * Applies the style to a given text. */ public function apply(string $text): string; } PK Zq/0YYNullOutputFormatterStyle.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; /** * @author Tien Xuan Vo */ final class NullOutputFormatterStyle implements OutputFormatterStyleInterface { public function apply(string $text): string { return $text; } public function setBackground(string $color = null): void { if (1 > \func_num_args()) { trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } // do nothing } public function setForeground(string $color = null): void { if (1 > \func_num_args()) { trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } // do nothing } public function setOption(string $option): void { // do nothing } public function setOptions(array $options): void { // do nothing } public function unsetOption(string $option): void { // do nothing } } PK ZXǿ  OutputFormatterStyleStack.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Formatter; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Contracts\Service\ResetInterface; /** * @author Jean-François Simon */ class OutputFormatterStyleStack implements ResetInterface { /** * @var OutputFormatterStyleInterface[] */ private array $styles = []; private OutputFormatterStyleInterface $emptyStyle; public function __construct(OutputFormatterStyleInterface $emptyStyle = null) { $this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle(); $this->reset(); } /** * Resets stack (ie. empty internal arrays). * * @return void */ public function reset() { $this->styles = []; } /** * Pushes a style in the stack. * * @return void */ public function push(OutputFormatterStyleInterface $style) { $this->styles[] = $style; } /** * Pops a style from the stack. * * @throws InvalidArgumentException When style tags incorrectly nested */ public function pop(OutputFormatterStyleInterface $style = null): OutputFormatterStyleInterface { if (!$this->styles) { return $this->emptyStyle; } if (null === $style) { return array_pop($this->styles); } foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { if ($style->apply('') === $stackedStyle->apply('')) { $this->styles = \array_slice($this->styles, 0, $index); return $stackedStyle; } } throw new InvalidArgumentException('Incorrectly nested style tag found.'); } /** * Computes current style with stacks top codes. */ public function getCurrent(): OutputFormatterStyleInterface { if (!$this->styles) { return $this->emptyStyle; } return $this->styles[\count($this->styles) - 1]; } /** * @return $this */ public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle): static { $this->emptyStyle = $emptyStyle; return $this; } public function getEmptyStyle(): OutputFormatterStyleInterface { return $this->emptyStyle; } } PKZJ J LogstashFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * Serializes a log message to Logstash Event Format * * @see https://www.elastic.co/products/logstash * @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java * * @author Tim Mower */ class LogstashFormatter extends NormalizerFormatter { /** * @var string the name of the system for the Logstash log message, used to fill the @source field */ protected $systemName; /** * @var string an application name for the Logstash log message, used to fill the @type field */ protected $applicationName; /** * @var string the key for 'extra' fields from the Monolog record */ protected $extraKey; /** * @var string the key for 'context' fields from the Monolog record */ protected $contextKey; /** * @param string $applicationName The application that sends the data, used as the "type" field of logstash * @param string|null $systemName The system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine * @param string $extraKey The key for extra keys inside logstash "fields", defaults to extra * @param string $contextKey The key for context keys inside logstash "fields", defaults to context */ public function __construct(string $applicationName, ?string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context') { // logstash requires a ISO 8601 format date with optional millisecond precision. parent::__construct('Y-m-d\TH:i:s.uP'); $this->systemName = $systemName === null ? (string) gethostname() : $systemName; $this->applicationName = $applicationName; $this->extraKey = $extraKey; $this->contextKey = $contextKey; } /** * {@inheritDoc} */ public function format(array $record): string { $record = parent::format($record); if (empty($record['datetime'])) { $record['datetime'] = gmdate('c'); } $message = [ '@timestamp' => $record['datetime'], '@version' => 1, 'host' => $this->systemName, ]; if (isset($record['message'])) { $message['message'] = $record['message']; } if (isset($record['channel'])) { $message['type'] = $record['channel']; $message['channel'] = $record['channel']; } if (isset($record['level_name'])) { $message['level'] = $record['level_name']; } if (isset($record['level'])) { $message['monolog_level'] = $record['level']; } if ($this->applicationName) { $message['type'] = $this->applicationName; } if (!empty($record['extra'])) { $message[$this->extraKey] = $record['extra']; } if (!empty($record['context'])) { $message[$this->contextKey] = $record['context']; } return $this->toJson($message) . "\n"; } } PKZEEElasticsearchFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use DateTimeInterface; /** * Format a log message into an Elasticsearch record * * @author Avtandil Kikabidze */ class ElasticsearchFormatter extends NormalizerFormatter { /** * @var string Elasticsearch index name */ protected $index; /** * @var string Elasticsearch record type */ protected $type; /** * @param string $index Elasticsearch index name * @param string $type Elasticsearch record type */ public function __construct(string $index, string $type) { // Elasticsearch requires an ISO 8601 format date with optional millisecond precision. parent::__construct(DateTimeInterface::ISO8601); $this->index = $index; $this->type = $type; } /** * {@inheritDoc} */ public function format(array $record) { $record = parent::format($record); return $this->getDocument($record); } /** * Getter index * * @return string */ public function getIndex(): string { return $this->index; } /** * Getter type * * @return string */ public function getType(): string { return $this->type; } /** * Convert a log message into an Elasticsearch record * * @param mixed[] $record Log message * @return mixed[] */ protected function getDocument(array $record): array { $record['_index'] = $this->index; $record['_type'] = $this->type; return $record; } } PKZ!te5 5 FlowdockFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * formats the record to be used in the FlowdockHandler * * @author Dominik Liebler * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4 */ class FlowdockFormatter implements FormatterInterface { /** * @var string */ private $source; /** * @var string */ private $sourceEmail; public function __construct(string $source, string $sourceEmail) { $this->source = $source; $this->sourceEmail = $sourceEmail; } /** * {@inheritDoc} * * @return mixed[] */ public function format(array $record): array { $tags = [ '#logs', '#' . strtolower($record['level_name']), '#' . $record['channel'], ]; foreach ($record['extra'] as $value) { $tags[] = '#' . $value; } $subject = sprintf( 'in %s: %s - %s', $this->source, $record['level_name'], $this->getShortMessage($record['message']) ); $record['flowdock'] = [ 'source' => $this->source, 'from_address' => $this->sourceEmail, 'subject' => $subject, 'content' => $record['message'], 'tags' => $tags, 'project' => $this->source, ]; return $record; } /** * {@inheritDoc} * * @return mixed[][] */ public function formatBatch(array $records): array { $formatted = []; foreach ($records as $record) { $formatted[] = $this->format($record); } return $formatted; } public function getShortMessage(string $message): string { static $hasMbString; if (null === $hasMbString) { $hasMbString = function_exists('mb_strlen'); } $maxLength = 45; if ($hasMbString) { if (mb_strlen($message, 'UTF-8') > $maxLength) { $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; } } else { if (strlen($message) > $maxLength) { $message = substr($message, 0, $maxLength - 4) . ' ...'; } } return $message; } } PKZqRLogmaticFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * Encodes message information into JSON in a format compatible with Logmatic. * * @author Julien Breux */ class LogmaticFormatter extends JsonFormatter { protected const MARKERS = ["sourcecode", "php"]; /** * @var string */ protected $hostname = ''; /** * @var string */ protected $appname = ''; public function setHostname(string $hostname): self { $this->hostname = $hostname; return $this; } public function setAppname(string $appname): self { $this->appname = $appname; return $this; } /** * Appends the 'hostname' and 'appname' parameter for indexing by Logmatic. * * @see http://doc.logmatic.io/docs/basics-to-send-data * @see \Monolog\Formatter\JsonFormatter::format() */ public function format(array $record): string { if (!empty($this->hostname)) { $record["hostname"] = $this->hostname; } if (!empty($this->appname)) { $record["appname"] = $this->appname; } $record["@marker"] = static::MARKERS; return parent::format($record); } } PKZXFluentdFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Utils; /** * Class FluentdFormatter * * Serializes a log message to Fluentd unix socket protocol * * Fluentd config: * * * type unix * path /var/run/td-agent/td-agent.sock * * * Monolog setup: * * $logger = new Monolog\Logger('fluent.tag'); * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock'); * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter()); * $logger->pushHandler($fluentHandler); * * @author Andrius Putna */ class FluentdFormatter implements FormatterInterface { /** * @var bool $levelTag should message level be a part of the fluentd tag */ protected $levelTag = false; public function __construct(bool $levelTag = false) { if (!function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); } $this->levelTag = $levelTag; } public function isUsingLevelsInTag(): bool { return $this->levelTag; } public function format(array $record): string { $tag = $record['channel']; if ($this->levelTag) { $tag .= '.' . strtolower($record['level_name']); } $message = [ 'message' => $record['message'], 'context' => $record['context'], 'extra' => $record['extra'], ]; if (!$this->levelTag) { $message['level'] = $record['level']; $message['level_name'] = $record['level_name']; } return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]); } public function formatBatch(array $records): string { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } } PKZ1eElasticaFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Elastica\Document; /** * Format a log message into an Elastica Document * * @author Jelle Vink * * @phpstan-import-type Record from \Monolog\Logger */ class ElasticaFormatter extends NormalizerFormatter { /** * @var string Elastic search index name */ protected $index; /** * @var ?string Elastic search document type */ protected $type; /** * @param string $index Elastic Search index name * @param ?string $type Elastic Search document type, deprecated as of Elastica 7 */ public function __construct(string $index, ?string $type) { // elasticsearch requires a ISO 8601 format date with optional millisecond precision. parent::__construct('Y-m-d\TH:i:s.uP'); $this->index = $index; $this->type = $type; } /** * {@inheritDoc} */ public function format(array $record) { $record = parent::format($record); return $this->getDocument($record); } public function getIndex(): string { return $this->index; } /** * @deprecated since Elastica 7 type has no effect */ public function getType(): string { /** @phpstan-ignore-next-line */ return $this->type; } /** * Convert a log message into an Elastica Document * * @phpstan-param Record $record */ protected function getDocument(array $record): Document { $document = new Document(); $document->setData($record); if (method_exists($document, 'setType')) { /** @phpstan-ignore-next-line */ $document->setType($this->type); } $document->setIndex($this->index); return $document; } } PKZjjGoogleCloudLoggingFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use DateTimeInterface; use Monolog\LogRecord; /** * Encodes message information into JSON in a format compatible with Cloud logging. * * @see https://cloud.google.com/logging/docs/structured-logging * @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry * * @author Luís Cobucci */ final class GoogleCloudLoggingFormatter extends JsonFormatter { /** {@inheritdoc} **/ public function format(array $record): string { // Re-key level for GCP logging $record['severity'] = $record['level_name']; $record['time'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED); // Remove keys that are not used by GCP unset($record['level'], $record['level_name'], $record['datetime']); return parent::format($record); } } PKZ4zFormatterInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * Interface for formatters * * @author Jordi Boggiano * * @phpstan-import-type Record from \Monolog\Logger */ interface FormatterInterface { /** * Formats a log record. * * @param array $record A record to format * @return mixed The formatted record * * @phpstan-param Record $record */ public function format(array $record); /** * Formats a set of log records. * * @param array $records A set of records to format * @return mixed The formatted set of records * * @phpstan-param Record[] $records */ public function formatBatch(array $records); } PKZFScalarFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * Formats data into an associative array of scalar values. * Objects and arrays will be JSON encoded. * * @author Andrew Lawson */ class ScalarFormatter extends NormalizerFormatter { /** * {@inheritDoc} * * @phpstan-return array $record */ public function format(array $record): array { $result = []; foreach ($record as $key => $value) { $result[$key] = $this->normalizeValue($value); } return $result; } /** * @param mixed $value * @return scalar|null */ protected function normalizeValue($value) { $normalized = $this->normalize($value); if (is_array($normalized)) { return $this->toJson($normalized, true); } return $normalized; } } PKZ ؑLogglyFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; /** * Encodes message information into JSON in a format compatible with Loggly. * * @author Adam Pancutt */ class LogglyFormatter extends JsonFormatter { /** * Overrides the default batch mode to new lines for compatibility with the * Loggly bulk API. */ public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = false) { parent::__construct($batchMode, $appendNewline); } /** * Appends the 'timestamp' parameter for indexing by Loggly. * * @see https://www.loggly.com/docs/automated-parsing/#json * @see \Monolog\Formatter\JsonFormatter::format() */ public function format(array $record): string { if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) { $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); unset($record["datetime"]); } return parent::format($record); } } PKZ& HtmlFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Logger; use Monolog\Utils; /** * Formats incoming records into an HTML table * * This is especially useful for html email logging * * @author Tiago Brito */ class HtmlFormatter extends NormalizerFormatter { /** * Translates Monolog log levels to html color priorities. * * @var array */ protected $logLevels = [ Logger::DEBUG => '#CCCCCC', Logger::INFO => '#28A745', Logger::NOTICE => '#17A2B8', Logger::WARNING => '#FFC107', Logger::ERROR => '#FD7E14', Logger::CRITICAL => '#DC3545', Logger::ALERT => '#821722', Logger::EMERGENCY => '#000000', ]; /** * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct(?string $dateFormat = null) { parent::__construct($dateFormat); } /** * Creates an HTML table row * * @param string $th Row header content * @param string $td Row standard cell content * @param bool $escapeTd false if td content must not be html escaped */ protected function addRow(string $th, string $td = ' ', bool $escapeTd = true): string { $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); if ($escapeTd) { $td = '
'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
'; } return "\n$th:\n".$td."\n"; } /** * Create a HTML h1 tag * * @param string $title Text to be in the h1 * @param int $level Error level * @return string */ protected function addTitle(string $title, int $level): string { $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); return '

'.$title.'

'; } /** * Formats a log record. * * @return string The formatted record */ public function format(array $record): string { $output = $this->addTitle($record['level_name'], $record['level']); $output .= ''; $output .= $this->addRow('Message', (string) $record['message']); $output .= $this->addRow('Time', $this->formatDate($record['datetime'])); $output .= $this->addRow('Channel', $record['channel']); if ($record['context']) { $embeddedTable = '
'; foreach ($record['context'] as $key => $value) { $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); } $embeddedTable .= '
'; $output .= $this->addRow('Context', $embeddedTable, false); } if ($record['extra']) { $embeddedTable = ''; foreach ($record['extra'] as $key => $value) { $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); } $embeddedTable .= '
'; $output .= $this->addRow('Extra', $embeddedTable, false); } return $output.''; } /** * Formats a set of log records. * * @return string The formatted set of records */ public function formatBatch(array $records): string { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } /** * @param mixed $data */ protected function convertToString($data): string { if (null === $data || is_scalar($data)) { return (string) $data; } $data = $this->normalize($data); return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true); } } PKZ݃GelfMessageFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Logger; use Gelf\Message; use Monolog\Utils; /** * Serializes a log message to GELF * @see http://docs.graylog.org/en/latest/pages/gelf.html * * @author Matt Lehner * * @phpstan-import-type Level from \Monolog\Logger */ class GelfMessageFormatter extends NormalizerFormatter { protected const DEFAULT_MAX_LENGTH = 32766; /** * @var string the name of the system for the Gelf log message */ protected $systemName; /** * @var string a prefix for 'extra' fields from the Monolog record (optional) */ protected $extraPrefix; /** * @var string a prefix for 'context' fields from the Monolog record (optional) */ protected $contextPrefix; /** * @var int max length per field */ protected $maxLength; /** * @var int */ private $gelfVersion = 2; /** * Translates Monolog log levels to Graylog2 log priorities. * * @var array * * @phpstan-var array */ private $logLevels = [ Logger::DEBUG => 7, Logger::INFO => 6, Logger::NOTICE => 5, Logger::WARNING => 4, Logger::ERROR => 3, Logger::CRITICAL => 2, Logger::ALERT => 1, Logger::EMERGENCY => 0, ]; public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null) { if (!class_exists(Message::class)) { throw new \RuntimeException('Composer package graylog2/gelf-php is required to use Monolog\'s GelfMessageFormatter'); } parent::__construct('U.u'); $this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName; $this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix; $this->contextPrefix = $contextPrefix; $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; if (method_exists(Message::class, 'setFacility')) { $this->gelfVersion = 1; } } /** * {@inheritDoc} */ public function format(array $record): Message { $context = $extra = []; if (isset($record['context'])) { /** @var mixed[] $context */ $context = parent::normalize($record['context']); } if (isset($record['extra'])) { /** @var mixed[] $extra */ $extra = parent::normalize($record['extra']); } if (!isset($record['datetime'], $record['message'], $record['level'])) { throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); } $message = new Message(); $message ->setTimestamp($record['datetime']) ->setShortMessage((string) $record['message']) ->setHost($this->systemName) ->setLevel($this->logLevels[$record['level']]); // message length + system name length + 200 for padding / metadata $len = 200 + strlen((string) $record['message']) + strlen($this->systemName); if ($len > $this->maxLength) { $message->setShortMessage(Utils::substr($record['message'], 0, $this->maxLength)); } if ($this->gelfVersion === 1) { if (isset($record['channel'])) { $message->setFacility($record['channel']); } if (isset($extra['line'])) { $message->setLine($extra['line']); unset($extra['line']); } if (isset($extra['file'])) { $message->setFile($extra['file']); unset($extra['file']); } } else { $message->setAdditional('facility', $record['channel']); } foreach ($extra as $key => $val) { $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); $len = strlen($this->extraPrefix . $key . $val); if ($len > $this->maxLength) { $message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); continue; } $message->setAdditional($this->extraPrefix . $key, $val); } foreach ($context as $key => $val) { $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); $len = strlen($this->contextPrefix . $key . $val); if ($len > $this->maxLength) { $message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); continue; } $message->setAdditional($this->contextPrefix . $key, $val); } if ($this->gelfVersion === 1) { /** @phpstan-ignore-next-line */ if (null === $message->getFile() && isset($context['exception']['file'])) { if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) { $message->setFile($matches[1]); $message->setLine($matches[2]); } } } return $message; } } PKZ ,qqJsonFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Throwable; /** * Encodes whatever record data is passed to it as json * * This can be useful to log to databases or remote APIs * * @author Jordi Boggiano * * @phpstan-import-type Record from \Monolog\Logger */ class JsonFormatter extends NormalizerFormatter { public const BATCH_MODE_JSON = 1; public const BATCH_MODE_NEWLINES = 2; /** @var self::BATCH_MODE_* */ protected $batchMode; /** @var bool */ protected $appendNewline; /** @var bool */ protected $ignoreEmptyContextAndExtra; /** @var bool */ protected $includeStacktraces = false; /** * @param self::BATCH_MODE_* $batchMode */ public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) { $this->batchMode = $batchMode; $this->appendNewline = $appendNewline; $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; $this->includeStacktraces = $includeStacktraces; parent::__construct(); } /** * The batch mode option configures the formatting style for * multiple records. By default, multiple records will be * formatted as a JSON-encoded array. However, for * compatibility with some API endpoints, alternative styles * are available. */ public function getBatchMode(): int { return $this->batchMode; } /** * True if newlines are appended to every formatted record */ public function isAppendingNewlines(): bool { return $this->appendNewline; } /** * {@inheritDoc} */ public function format(array $record): string { $normalized = $this->normalize($record); if (isset($normalized['context']) && $normalized['context'] === []) { if ($this->ignoreEmptyContextAndExtra) { unset($normalized['context']); } else { $normalized['context'] = new \stdClass; } } if (isset($normalized['extra']) && $normalized['extra'] === []) { if ($this->ignoreEmptyContextAndExtra) { unset($normalized['extra']); } else { $normalized['extra'] = new \stdClass; } } return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : ''); } /** * {@inheritDoc} */ public function formatBatch(array $records): string { switch ($this->batchMode) { case static::BATCH_MODE_NEWLINES: return $this->formatBatchNewlines($records); case static::BATCH_MODE_JSON: default: return $this->formatBatchJson($records); } } /** * @return self */ public function includeStacktraces(bool $include = true): self { $this->includeStacktraces = $include; return $this; } /** * Return a JSON-encoded array of records. * * @phpstan-param Record[] $records */ protected function formatBatchJson(array $records): string { return $this->toJson($this->normalize($records), true); } /** * Use new lines to separate records instead of a * JSON-encoded array. * * @phpstan-param Record[] $records */ protected function formatBatchNewlines(array $records): string { $instance = $this; $oldNewline = $this->appendNewline; $this->appendNewline = false; array_walk($records, function (&$value, $key) use ($instance) { $value = $instance->format($value); }); $this->appendNewline = $oldNewline; return implode("\n", $records); } /** * Normalizes given $data. * * @param mixed $data * * @return mixed */ protected function normalize($data, int $depth = 0) { if ($depth > $this->maxNormalizeDepth) { return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization'; } if (is_array($data)) { $normalized = []; $count = 1; foreach ($data as $key => $value) { if ($count++ > $this->maxNormalizeItemCount) { $normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization'; break; } $normalized[$key] = $this->normalize($value, $depth + 1); } return $normalized; } if (is_object($data)) { if ($data instanceof \DateTimeInterface) { return $this->formatDate($data); } if ($data instanceof Throwable) { return $this->normalizeException($data, $depth); } // if the object has specific json serializability we want to make sure we skip the __toString treatment below if ($data instanceof \JsonSerializable) { return $data; } if (\get_class($data) === '__PHP_Incomplete_Class') { return new \ArrayObject($data); } if (method_exists($data, '__toString')) { return $data->__toString(); } return $data; } if (is_resource($data)) { return parent::normalize($data); } return $data; } /** * Normalizes given exception with or without its own stack trace based on * `includeStacktraces` property. * * {@inheritDoc} */ protected function normalizeException(Throwable $e, int $depth = 0): array { $data = parent::normalizeException($e, $depth); if (!$this->includeStacktraces) { unset($data['trace']); } return $data; } } PKZ13caaWildfireFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Logger; /** * Serializes a log message according to Wildfire's header requirements * * @author Eric Clemmons (@ericclemmons) * @author Christophe Coevoet * @author Kirill chEbba Chebunin * * @phpstan-import-type Level from \Monolog\Logger */ class WildfireFormatter extends NormalizerFormatter { /** * Translates Monolog log levels to Wildfire levels. * * @var array */ private $logLevels = [ Logger::DEBUG => 'LOG', Logger::INFO => 'INFO', Logger::NOTICE => 'INFO', Logger::WARNING => 'WARN', Logger::ERROR => 'ERROR', Logger::CRITICAL => 'ERROR', Logger::ALERT => 'ERROR', Logger::EMERGENCY => 'ERROR', ]; /** * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct(?string $dateFormat = null) { parent::__construct($dateFormat); // http headers do not like non-ISO-8559-1 characters $this->removeJsonEncodeOption(JSON_UNESCAPED_UNICODE); } /** * {@inheritDoc} * * @return string */ public function format(array $record): string { // Retrieve the line and file if set and remove them from the formatted extra $file = $line = ''; if (isset($record['extra']['file'])) { $file = $record['extra']['file']; unset($record['extra']['file']); } if (isset($record['extra']['line'])) { $line = $record['extra']['line']; unset($record['extra']['line']); } /** @var mixed[] $record */ $record = $this->normalize($record); $message = ['message' => $record['message']]; $handleError = false; if ($record['context']) { $message['context'] = $record['context']; $handleError = true; } if ($record['extra']) { $message['extra'] = $record['extra']; $handleError = true; } if (count($message) === 1) { $message = reset($message); } if (isset($record['context']['table'])) { $type = 'TABLE'; $label = $record['channel'] .': '. $record['message']; $message = $record['context']['table']; } else { $type = $this->logLevels[$record['level']]; $label = $record['channel']; } // Create JSON object describing the appearance of the message in the console $json = $this->toJson([ [ 'Type' => $type, 'File' => $file, 'Line' => $line, 'Label' => $label, ], $message, ], $handleError); // The message itself is a serialization of the above JSON object + it's length return sprintf( '%d|%s|', strlen($json), $json ); } /** * {@inheritDoc} * * @phpstan-return never */ public function formatBatch(array $records) { throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); } /** * {@inheritDoc} * * @return null|scalar|array|object */ protected function normalize($data, int $depth = 0) { if (is_object($data) && !$data instanceof \DateTimeInterface) { return $data; } return parent::normalize($data, $depth); } } PKZllMongoDBFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use MongoDB\BSON\Type; use MongoDB\BSON\UTCDateTime; use Monolog\Utils; /** * Formats a record for use with the MongoDBHandler. * * @author Florian Plattner */ class MongoDBFormatter implements FormatterInterface { /** @var bool */ private $exceptionTraceAsString; /** @var int */ private $maxNestingLevel; /** @var bool */ private $isLegacyMongoExt; /** * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings */ public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true) { $this->maxNestingLevel = max($maxNestingLevel, 0); $this->exceptionTraceAsString = $exceptionTraceAsString; $this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<='); } /** * {@inheritDoc} * * @return mixed[] */ public function format(array $record): array { /** @var mixed[] $res */ $res = $this->formatArray($record); return $res; } /** * {@inheritDoc} * * @return array */ public function formatBatch(array $records): array { $formatted = []; foreach ($records as $key => $record) { $formatted[$key] = $this->format($record); } return $formatted; } /** * @param mixed[] $array * @return mixed[]|string Array except when max nesting level is reached then a string "[...]" */ protected function formatArray(array $array, int $nestingLevel = 0) { if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) { return '[...]'; } foreach ($array as $name => $value) { if ($value instanceof \DateTimeInterface) { $array[$name] = $this->formatDate($value, $nestingLevel + 1); } elseif ($value instanceof \Throwable) { $array[$name] = $this->formatException($value, $nestingLevel + 1); } elseif (is_array($value)) { $array[$name] = $this->formatArray($value, $nestingLevel + 1); } elseif (is_object($value) && !$value instanceof Type) { $array[$name] = $this->formatObject($value, $nestingLevel + 1); } } return $array; } /** * @param mixed $value * @return mixed[]|string */ protected function formatObject($value, int $nestingLevel) { $objectVars = get_object_vars($value); $objectVars['class'] = Utils::getClass($value); return $this->formatArray($objectVars, $nestingLevel); } /** * @return mixed[]|string */ protected function formatException(\Throwable $exception, int $nestingLevel) { $formattedException = [ 'class' => Utils::getClass($exception), 'message' => $exception->getMessage(), 'code' => (int) $exception->getCode(), 'file' => $exception->getFile() . ':' . $exception->getLine(), ]; if ($this->exceptionTraceAsString === true) { $formattedException['trace'] = $exception->getTraceAsString(); } else { $formattedException['trace'] = $exception->getTrace(); } return $this->formatArray($formattedException, $nestingLevel); } protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime { if ($this->isLegacyMongoExt) { return $this->legacyGetMongoDbDateTime($value); } return $this->getMongoDbDateTime($value); } private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime { return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000)); } /** * This is needed to support MongoDB Driver v1.19 and below * * See https://github.com/mongodb/mongo-php-driver/issues/426 * * It can probably be removed in 2.1 or later once MongoDB's 1.2 is released and widely adopted */ private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime { $milliseconds = floor(((float) $value->format('U.u')) * 1000); $milliseconds = (PHP_INT_SIZE == 8) //64-bit OS? ? (int) $milliseconds : (string) $milliseconds; // @phpstan-ignore-next-line return new UTCDateTime($milliseconds); } } PKZ](M``ChromePHPFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Logger; /** * Formats a log message according to the ChromePHP array format * * @author Christophe Coevoet */ class ChromePHPFormatter implements FormatterInterface { /** * Translates Monolog log levels to Wildfire levels. * * @var array */ private $logLevels = [ Logger::DEBUG => 'log', Logger::INFO => 'info', Logger::NOTICE => 'info', Logger::WARNING => 'warn', Logger::ERROR => 'error', Logger::CRITICAL => 'error', Logger::ALERT => 'error', Logger::EMERGENCY => 'error', ]; /** * {@inheritDoc} */ public function format(array $record) { // Retrieve the line and file if set and remove them from the formatted extra $backtrace = 'unknown'; if (isset($record['extra']['file'], $record['extra']['line'])) { $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; unset($record['extra']['file'], $record['extra']['line']); } $message = ['message' => $record['message']]; if ($record['context']) { $message['context'] = $record['context']; } if ($record['extra']) { $message['extra'] = $record['extra']; } if (count($message) === 1) { $message = reset($message); } return [ $record['channel'], $message, $backtrace, $this->logLevels[$record['level']], ]; } /** * {@inheritDoc} */ public function formatBatch(array $records) { $formatted = []; foreach ($records as $record) { $formatted[] = $this->format($record); } return $formatted; } } PKZ>/ / NormalizerFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\DateTimeImmutable; use Monolog\Utils; use Throwable; /** * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets * * @author Jordi Boggiano */ class NormalizerFormatter implements FormatterInterface { public const SIMPLE_DATE = "Y-m-d\TH:i:sP"; /** @var string */ protected $dateFormat; /** @var int */ protected $maxNormalizeDepth = 9; /** @var int */ protected $maxNormalizeItemCount = 1000; /** @var int */ private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS; /** * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct(?string $dateFormat = null) { $this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat; if (!function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); } } /** * {@inheritDoc} * * @param mixed[] $record */ public function format(array $record) { return $this->normalize($record); } /** * {@inheritDoc} */ public function formatBatch(array $records) { foreach ($records as $key => $record) { $records[$key] = $this->format($record); } return $records; } public function getDateFormat(): string { return $this->dateFormat; } public function setDateFormat(string $dateFormat): self { $this->dateFormat = $dateFormat; return $this; } /** * The maximum number of normalization levels to go through */ public function getMaxNormalizeDepth(): int { return $this->maxNormalizeDepth; } public function setMaxNormalizeDepth(int $maxNormalizeDepth): self { $this->maxNormalizeDepth = $maxNormalizeDepth; return $this; } /** * The maximum number of items to normalize per level */ public function getMaxNormalizeItemCount(): int { return $this->maxNormalizeItemCount; } public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): self { $this->maxNormalizeItemCount = $maxNormalizeItemCount; return $this; } /** * Enables `json_encode` pretty print. */ public function setJsonPrettyPrint(bool $enable): self { if ($enable) { $this->jsonEncodeOptions |= JSON_PRETTY_PRINT; } else { $this->jsonEncodeOptions &= ~JSON_PRETTY_PRINT; } return $this; } /** * @param mixed $data * @return null|scalar|array */ protected function normalize($data, int $depth = 0) { if ($depth > $this->maxNormalizeDepth) { return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; } if (null === $data || is_scalar($data)) { if (is_float($data)) { if (is_infinite($data)) { return ($data > 0 ? '' : '-') . 'INF'; } if (is_nan($data)) { return 'NaN'; } } return $data; } if (is_array($data)) { $normalized = []; $count = 1; foreach ($data as $key => $value) { if ($count++ > $this->maxNormalizeItemCount) { $normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.count($data).' total), aborting normalization'; break; } $normalized[$key] = $this->normalize($value, $depth + 1); } return $normalized; } if ($data instanceof \DateTimeInterface) { return $this->formatDate($data); } if (is_object($data)) { if ($data instanceof Throwable) { return $this->normalizeException($data, $depth); } if ($data instanceof \JsonSerializable) { /** @var null|scalar|array $value */ $value = $data->jsonSerialize(); } elseif (\get_class($data) === '__PHP_Incomplete_Class') { $accessor = new \ArrayObject($data); $value = (string) $accessor['__PHP_Incomplete_Class_Name']; } elseif (method_exists($data, '__toString')) { /** @var string $value */ $value = $data->__toString(); } else { // the rest is normalized by json encoding and decoding it /** @var null|scalar|array $value */ $value = json_decode($this->toJson($data, true), true); } return [Utils::getClass($data) => $value]; } if (is_resource($data)) { return sprintf('[resource(%s)]', get_resource_type($data)); } return '[unknown('.gettype($data).')]'; } /** * @return mixed[] */ protected function normalizeException(Throwable $e, int $depth = 0) { if ($depth > $this->maxNormalizeDepth) { return ['Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization']; } if ($e instanceof \JsonSerializable) { return (array) $e->jsonSerialize(); } $data = [ 'class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => (int) $e->getCode(), 'file' => $e->getFile().':'.$e->getLine(), ]; if ($e instanceof \SoapFault) { if (isset($e->faultcode)) { $data['faultcode'] = $e->faultcode; } if (isset($e->faultactor)) { $data['faultactor'] = $e->faultactor; } if (isset($e->detail)) { if (is_string($e->detail)) { $data['detail'] = $e->detail; } elseif (is_object($e->detail) || is_array($e->detail)) { $data['detail'] = $this->toJson($e->detail, true); } } } $trace = $e->getTrace(); foreach ($trace as $frame) { if (isset($frame['file'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } } if ($previous = $e->getPrevious()) { $data['previous'] = $this->normalizeException($previous, $depth + 1); } return $data; } /** * Return the JSON representation of a value * * @param mixed $data * @throws \RuntimeException if encoding fails and errors are not ignored * @return string if encoding fails and ignoreErrors is true 'null' is returned */ protected function toJson($data, bool $ignoreErrors = false): string { return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors); } /** * @return string */ protected function formatDate(\DateTimeInterface $date) { // in case the date format isn't custom then we defer to the custom DateTimeImmutable // formatting logic, which will pick the right format based on whether useMicroseconds is on if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof DateTimeImmutable) { return (string) $date; } return $date->format($this->dateFormat); } public function addJsonEncodeOption(int $option): self { $this->jsonEncodeOptions |= $option; return $this; } public function removeJsonEncodeOption(int $option): self { $this->jsonEncodeOptions &= ~$option; return $this; } } PKZk  LineFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Monolog\Utils; /** * Formats incoming records into a one-line string * * This is especially useful for logging to files * * @author Jordi Boggiano * @author Christophe Coevoet */ class LineFormatter extends NormalizerFormatter { public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; /** @var string */ protected $format; /** @var bool */ protected $allowInlineLineBreaks; /** @var bool */ protected $ignoreEmptyContextAndExtra; /** @var bool */ protected $includeStacktraces; /** @var ?callable */ protected $stacktracesParser; /** * @param string|null $format The format of the message * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries * @param bool $ignoreEmptyContextAndExtra */ public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) { $this->format = $format === null ? static::SIMPLE_FORMAT : $format; $this->allowInlineLineBreaks = $allowInlineLineBreaks; $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; $this->includeStacktraces($includeStacktraces); parent::__construct($dateFormat); } public function includeStacktraces(bool $include = true, ?callable $parser = null): self { $this->includeStacktraces = $include; if ($this->includeStacktraces) { $this->allowInlineLineBreaks = true; $this->stacktracesParser = $parser; } return $this; } public function allowInlineLineBreaks(bool $allow = true): self { $this->allowInlineLineBreaks = $allow; return $this; } public function ignoreEmptyContextAndExtra(bool $ignore = true): self { $this->ignoreEmptyContextAndExtra = $ignore; return $this; } /** * {@inheritDoc} */ public function format(array $record): string { $vars = parent::format($record); $output = $this->format; foreach ($vars['extra'] as $var => $val) { if (false !== strpos($output, '%extra.'.$var.'%')) { $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); unset($vars['extra'][$var]); } } foreach ($vars['context'] as $var => $val) { if (false !== strpos($output, '%context.'.$var.'%')) { $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); unset($vars['context'][$var]); } } if ($this->ignoreEmptyContextAndExtra) { if (empty($vars['context'])) { unset($vars['context']); $output = str_replace('%context%', '', $output); } if (empty($vars['extra'])) { unset($vars['extra']); $output = str_replace('%extra%', '', $output); } } foreach ($vars as $var => $val) { if (false !== strpos($output, '%'.$var.'%')) { $output = str_replace('%'.$var.'%', $this->stringify($val), $output); } } // remove leftover %extra.xxx% and %context.xxx% if any if (false !== strpos($output, '%')) { $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); if (null === $output) { $pcreErrorCode = preg_last_error(); throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); } } return $output; } public function formatBatch(array $records): string { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } /** * @param mixed $value */ public function stringify($value): string { return $this->replaceNewlines($this->convertToString($value)); } protected function normalizeException(\Throwable $e, int $depth = 0): string { $str = $this->formatException($e); if ($previous = $e->getPrevious()) { do { $depth++; if ($depth > $this->maxNormalizeDepth) { $str .= "\n[previous exception] Over " . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; break; } $str .= "\n[previous exception] " . $this->formatException($previous); } while ($previous = $previous->getPrevious()); } return $str; } /** * @param mixed $data */ protected function convertToString($data): string { if (null === $data || is_bool($data)) { return var_export($data, true); } if (is_scalar($data)) { return (string) $data; } return $this->toJson($data, true); } protected function replaceNewlines(string $str): string { if ($this->allowInlineLineBreaks) { if (0 === strpos($str, '{')) { $str = preg_replace('/(?getCode(); if ($e instanceof \SoapFault) { if (isset($e->faultcode)) { $str .= ' faultcode: ' . $e->faultcode; } if (isset($e->faultactor)) { $str .= ' faultactor: ' . $e->faultactor; } if (isset($e->detail)) { if (is_string($e->detail)) { $str .= ' detail: ' . $e->detail; } elseif (is_object($e->detail) || is_array($e->detail)) { $str .= ' detail: ' . $this->toJson($e->detail, true); } } } $str .= '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')'; if ($this->includeStacktraces) { $str .= $this->stacktracesParser($e); } return $str; } private function stacktracesParser(\Throwable $e): string { $trace = $e->getTraceAsString(); if ($this->stacktracesParser) { $trace = $this->stacktracesParserCustom($trace); } return "\n[stacktrace]\n" . $trace . "\n"; } private function stacktracesParserCustom(string $trace): string { return implode("\n", array_filter(array_map($this->stacktracesParser, explode("\n", $trace)))); } } PKZʤSc33DocblockFormatter.phpnuW+A 'info', 'var' => 'strong', ]; /** * Format a docblock. * * @param \Reflector $reflector * * @return string Formatted docblock */ public static function format(\Reflector $reflector): string { $docblock = new Docblock($reflector); $chunks = []; if (!empty($docblock->desc)) { $chunks[] = 'Description:'; $chunks[] = self::indent(OutputFormatter::escape($docblock->desc), ' '); $chunks[] = ''; } if (!empty($docblock->tags)) { foreach ($docblock::$vectors as $name => $vector) { if (isset($docblock->tags[$name])) { $chunks[] = \sprintf('%s:', self::inflect($name)); $chunks[] = self::formatVector($vector, $docblock->tags[$name]); $chunks[] = ''; } } $tags = self::formatTags(\array_keys($docblock::$vectors), $docblock->tags); if (!empty($tags)) { $chunks[] = $tags; $chunks[] = ''; } } return \rtrim(\implode("\n", $chunks)); } /** * Format a docblock vector, for example, `@throws`, `@param`, or `@return`. * * @see DocBlock::$vectors * * @param array $vector * @param array $lines */ private static function formatVector(array $vector, array $lines): string { $template = [' ']; foreach ($vector as $type) { $max = 0; foreach ($lines as $line) { $chunk = $line[$type]; $cur = empty($chunk) ? 0 : \strlen($chunk) + 1; if ($cur > $max) { $max = $cur; } } $template[] = self::getVectorParamTemplate($type, $max); } $template = \implode(' ', $template); return \implode("\n", \array_map(function ($line) use ($template) { $escaped = \array_map(function ($l) { if ($l === null) { return ''; } return OutputFormatter::escape($l); }, $line); return \rtrim(\vsprintf($template, $escaped)); }, $lines)); } /** * Format docblock tags. * * @param array $skip Tags to exclude * @param array $tags Tags to format * * @return string formatted tags */ private static function formatTags(array $skip, array $tags): string { $chunks = []; foreach ($tags as $name => $values) { if (\in_array($name, $skip)) { continue; } foreach ($values as $value) { $chunks[] = \sprintf('%s%s %s', self::inflect($name), empty($value) ? '' : ':', OutputFormatter::escape($value)); } $chunks[] = ''; } return \implode("\n", $chunks); } /** * Get a docblock vector template. * * @param string $type Vector type * @param int $max Pad width */ private static function getVectorParamTemplate(string $type, int $max): string { if (!isset(self::VECTOR_PARAM_TEMPLATES[$type])) { return \sprintf('%%-%ds', $max); } return \sprintf('<%s>%%-%ds', self::VECTOR_PARAM_TEMPLATES[$type], $max, self::VECTOR_PARAM_TEMPLATES[$type]); } /** * Indent a string. * * @param string $text String to indent * @param string $indent (default: ' ') */ private static function indent(string $text, string $indent = ' '): string { return $indent.\str_replace("\n", "\n".$indent, $text); } /** * Convert underscored or whitespace separated words into sentence case. * * @param string $text */ private static function inflect(string $text): string { $words = \trim(\preg_replace('/[\s_-]+/', ' ', \preg_replace('/([a-z])([A-Z])/', '$1 $2', $text))); return \implode(' ', \array_map('ucfirst', \explode(' ', $words))); } } PKZ~ ' 'CodeFormatter.phpnuW+A> '; const NO_LINE_MARKER = ' '; const HIGHLIGHT_DEFAULT = 'default'; const HIGHLIGHT_KEYWORD = 'keyword'; const HIGHLIGHT_PUBLIC = 'public'; const HIGHLIGHT_PROTECTED = 'protected'; const HIGHLIGHT_PRIVATE = 'private'; const HIGHLIGHT_CONST = 'const'; const HIGHLIGHT_NUMBER = 'number'; const HIGHLIGHT_STRING = 'string'; const HIGHLIGHT_COMMENT = 'code_comment'; const HIGHLIGHT_INLINE_HTML = 'inline_html'; private const TOKEN_MAP = [ // Not highlighted \T_OPEN_TAG => self::HIGHLIGHT_DEFAULT, \T_OPEN_TAG_WITH_ECHO => self::HIGHLIGHT_DEFAULT, \T_CLOSE_TAG => self::HIGHLIGHT_DEFAULT, \T_STRING => self::HIGHLIGHT_DEFAULT, \T_VARIABLE => self::HIGHLIGHT_DEFAULT, \T_NS_SEPARATOR => self::HIGHLIGHT_DEFAULT, // Visibility \T_PUBLIC => self::HIGHLIGHT_PUBLIC, \T_PROTECTED => self::HIGHLIGHT_PROTECTED, \T_PRIVATE => self::HIGHLIGHT_PRIVATE, // Constants \T_DIR => self::HIGHLIGHT_CONST, \T_FILE => self::HIGHLIGHT_CONST, \T_METHOD_C => self::HIGHLIGHT_CONST, \T_NS_C => self::HIGHLIGHT_CONST, \T_LINE => self::HIGHLIGHT_CONST, \T_CLASS_C => self::HIGHLIGHT_CONST, \T_FUNC_C => self::HIGHLIGHT_CONST, \T_TRAIT_C => self::HIGHLIGHT_CONST, // Types \T_DNUMBER => self::HIGHLIGHT_NUMBER, \T_LNUMBER => self::HIGHLIGHT_NUMBER, \T_ENCAPSED_AND_WHITESPACE => self::HIGHLIGHT_STRING, \T_CONSTANT_ENCAPSED_STRING => self::HIGHLIGHT_STRING, // Comments \T_COMMENT => self::HIGHLIGHT_COMMENT, \T_DOC_COMMENT => self::HIGHLIGHT_COMMENT, // @todo something better here? \T_INLINE_HTML => self::HIGHLIGHT_INLINE_HTML, ]; /** * Format the code represented by $reflector for shell output. * * @param \Reflector $reflector * * @return string formatted code */ public static function format(\Reflector $reflector): string { if (self::isReflectable($reflector)) { if ($code = @\file_get_contents($reflector->getFileName())) { return self::formatCode($code, self::getStartLine($reflector), $reflector->getEndLine()); } } throw new RuntimeException('Source code unavailable'); } /** * Format code for shell output. * * Optionally, restrict by $startLine and $endLine line numbers, or pass $markLine to add a line marker. * * @param string $code * @param int $startLine * @param int|null $endLine * @param int|null $markLine * * @return string formatted code */ public static function formatCode(string $code, int $startLine = 1, ?int $endLine = null, ?int $markLine = null): string { $spans = self::tokenizeSpans($code); $lines = self::splitLines($spans, $startLine, $endLine); $lines = self::formatLines($lines); $lines = self::numberLines($lines, $markLine); return \implode('', \iterator_to_array($lines)); } /** * Get the start line for a given Reflector. * * Tries to incorporate doc comments if possible. * * This is typehinted as \Reflector but we've narrowed the input via self::isReflectable already. * * @param \ReflectionClass|\ReflectionFunctionAbstract $reflector */ private static function getStartLine(\Reflector $reflector): int { $startLine = $reflector->getStartLine(); if ($docComment = $reflector->getDocComment()) { $startLine -= \preg_match_all('/(\r\n?|\n)/', $docComment) + 1; } return \max($startLine, 1); } /** * Split code into highlight spans. * * Tokenize via \token_get_all, then map these tokens to internal highlight types, combining * adjacent spans of the same highlight type. * * @todo consider switching \token_get_all() out for PHP-Parser-based formatting at some point. * * @param string $code * * @return \Generator [$spanType, $spanText] highlight spans */ private static function tokenizeSpans(string $code): \Generator { $spanType = null; $buffer = ''; foreach (\token_get_all($code) as $token) { $nextType = self::nextHighlightType($token, $spanType); $spanType = $spanType ?: $nextType; if ($spanType !== $nextType) { yield [$spanType, $buffer]; $spanType = $nextType; $buffer = ''; } $buffer .= \is_array($token) ? $token[1] : $token; } if ($spanType !== null && $buffer !== '') { yield [$spanType, $buffer]; } } /** * Given a token and the current highlight span type, compute the next type. * * @param array|string $token \token_get_all token * @param string|null $currentType * * @return string|null */ private static function nextHighlightType($token, $currentType) { if ($token === '"') { return self::HIGHLIGHT_STRING; } if (\is_array($token)) { if ($token[0] === \T_WHITESPACE) { return $currentType; } if (\array_key_exists($token[0], self::TOKEN_MAP)) { return self::TOKEN_MAP[$token[0]]; } } return self::HIGHLIGHT_KEYWORD; } /** * Group highlight spans into an array of lines. * * Optionally, restrict by start and end line numbers. * * @param \Generator $spans as [$spanType, $spanText] pairs * @param int $startLine * @param int|null $endLine * * @return \Generator lines, each an array of [$spanType, $spanText] pairs */ private static function splitLines(\Generator $spans, int $startLine = 1, ?int $endLine = null): \Generator { $lineNum = 1; $buffer = []; foreach ($spans as list($spanType, $spanText)) { foreach (\preg_split('/(\r\n?|\n)/', $spanText) as $index => $spanLine) { if ($index > 0) { if ($lineNum >= $startLine) { yield $lineNum => $buffer; } $lineNum++; $buffer = []; if ($endLine !== null && $lineNum > $endLine) { return; } } if ($spanLine !== '') { $buffer[] = [$spanType, $spanLine]; } } } if (!empty($buffer)) { yield $lineNum => $buffer; } } /** * Format lines of highlight spans for shell output. * * @param \Generator $spanLines lines, each an array of [$spanType, $spanText] pairs * * @return \Generator Formatted lines */ private static function formatLines(\Generator $spanLines): \Generator { foreach ($spanLines as $lineNum => $spanLine) { $line = ''; foreach ($spanLine as list($spanType, $spanText)) { if ($spanType === self::HIGHLIGHT_DEFAULT) { $line .= OutputFormatter::escape($spanText); } else { $line .= \sprintf('<%s>%s', $spanType, OutputFormatter::escape($spanText), $spanType); } } yield $lineNum => $line.\PHP_EOL; } } /** * Prepend line numbers to formatted lines. * * Lines must be in an associative array with the correct keys in order to be numbered properly. * * Optionally, pass $markLine to add a line marker. * * @param \Generator $lines Formatted lines * @param int|null $markLine * * @return \Generator Numbered, formatted lines */ private static function numberLines(\Generator $lines, ?int $markLine = null): \Generator { $lines = \iterator_to_array($lines); // Figure out how much space to reserve for line numbers. \end($lines); $pad = \strlen(\key($lines)); // If $markLine is before or after our line range, don't bother reserving space for the marker. if ($markLine !== null) { if ($markLine > \key($lines)) { $markLine = null; } \reset($lines); if ($markLine < \key($lines)) { $markLine = null; } } foreach ($lines as $lineNum => $line) { $mark = ''; if ($markLine !== null) { $mark = ($markLine === $lineNum) ? self::LINE_MARKER : self::NO_LINE_MARKER; } yield \sprintf("%s: %s", $mark, $lineNum, $line); } } /** * Check whether a Reflector instance is reflectable by this formatter. * * @phpstan-assert-if-true \ReflectionClass|\ReflectionFunctionAbstract $reflector * * @param \Reflector $reflector */ private static function isReflectable(\Reflector $reflector): bool { return ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionFunctionAbstract) && \is_file($reflector->getFileName()); } } PKZ^ۦ Formatter.phpnuW+AgetName(); } /** * Print the method, property or class modifiers. * * @param \ReflectionMethod|\ReflectionProperty|\ReflectionClass $reflector * * @return string Formatted modifiers */ private static function formatModifiers(\Reflector $reflector): string { return \implode(' ', \array_map(function ($modifier) { return \sprintf('%s', $modifier); }, \Reflection::getModifierNames($reflector->getModifiers()))); } /** * Format a class signature. * * @param \ReflectionClass $reflector * * @return string Formatted signature */ private static function formatClass(\ReflectionClass $reflector): string { $chunks = []; if ($modifiers = self::formatModifiers($reflector)) { $chunks[] = $modifiers; } if ($reflector->isTrait()) { $chunks[] = 'trait'; } else { $chunks[] = $reflector->isInterface() ? 'interface' : 'class'; } $chunks[] = \sprintf('%s', self::formatName($reflector)); if ($parent = $reflector->getParentClass()) { $chunks[] = 'extends'; $chunks[] = \sprintf('%s', $parent->getName()); } $interfaces = $reflector->getInterfaceNames(); if (!empty($interfaces)) { \sort($interfaces); $chunks[] = $reflector->isInterface() ? 'extends' : 'implements'; $chunks[] = \implode(', ', \array_map(function ($name) { return \sprintf('%s', $name); }, $interfaces)); } return \implode(' ', $chunks); } /** * Format a constant signature. * * @param \ReflectionClassConstant $reflector * * @return string Formatted signature */ private static function formatClassConstant($reflector): string { $value = $reflector->getValue(); $style = self::getTypeStyle($value); return \sprintf( 'const %s = <%s>%s', self::formatName($reflector), $style, OutputFormatter::escape(Json::encode($value)), $style ); } /** * Format a constant signature. * * @param ReflectionConstant $reflector * * @return string Formatted signature */ private static function formatConstant(ReflectionConstant $reflector): string { $value = $reflector->getValue(); $style = self::getTypeStyle($value); return \sprintf( 'define(%s, <%s>%s)', OutputFormatter::escape(Json::encode($reflector->getName())), $style, OutputFormatter::escape(Json::encode($value)), $style ); } /** * Helper for getting output style for a given value's type. * * @param mixed $value */ private static function getTypeStyle($value): string { if (\is_int($value) || \is_float($value)) { return 'number'; } elseif (\is_string($value)) { return 'string'; } elseif (\is_bool($value) || $value === null) { return 'bool'; } else { return 'strong'; // @codeCoverageIgnore } } /** * Format a property signature. * * @param \ReflectionProperty $reflector * * @return string Formatted signature */ private static function formatProperty(\ReflectionProperty $reflector): string { return \sprintf( '%s $%s', self::formatModifiers($reflector), $reflector->getName() ); } /** * Format a function signature. * * @param \ReflectionFunction $reflector * * @return string Formatted signature */ private static function formatFunction(\ReflectionFunctionAbstract $reflector): string { return \sprintf( 'function %s%s(%s)%s', $reflector->returnsReference() ? '&' : '', self::formatName($reflector), \implode(', ', self::formatFunctionParams($reflector)), self::formatFunctionReturnType($reflector) ); } /** * Format a function signature's return type (if available). * * @param \ReflectionFunctionAbstract $reflector * * @return string Formatted return type */ private static function formatFunctionReturnType(\ReflectionFunctionAbstract $reflector): string { if (!\method_exists($reflector, 'hasReturnType') || !$reflector->hasReturnType()) { return ''; } return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true)); } /** * Format a method signature. * * @param \ReflectionMethod $reflector * * @return string Formatted signature */ private static function formatMethod(\ReflectionMethod $reflector): string { return \sprintf( '%s %s', self::formatModifiers($reflector), self::formatFunction($reflector) ); } /** * Print the function params. * * @param \ReflectionFunctionAbstract $reflector * * @return array */ private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector): array { $params = []; foreach ($reflector->getParameters() as $param) { $hint = ''; try { if (\method_exists($param, 'getType')) { // Only include the inquisitive nullable type iff param default value is not null. $defaultIsNull = $param->isOptional() && $param->isDefaultValueAvailable() && $param->getDefaultValue() === null; $hint = self::formatReflectionType($param->getType(), !$defaultIsNull); } else { if ($param->isArray()) { $hint = 'array'; } elseif ($class = $param->getClass()) { $hint = \sprintf('%s', $class->getName()); } } } catch (\Throwable $e) { // sometimes we just don't know... // bad class names, or autoloaded classes that haven't been loaded yet, or whathaveyou. // come to think of it, the only time I've seen this is with the intl extension. // Hax: we'll try to extract it :P // @codeCoverageIgnoreStart $chunks = \explode('$'.$param->getName(), (string) $param); $chunks = \explode(' ', \trim($chunks[0])); $guess = \end($chunks); $hint = \sprintf('%s', OutputFormatter::escape($guess)); // @codeCoverageIgnoreEnd } if ($param->isOptional()) { if (!$param->isDefaultValueAvailable()) { $value = 'unknown'; $typeStyle = 'urgent'; } else { $value = $param->getDefaultValue(); $typeStyle = self::getTypeStyle($value); $value = \is_array($value) ? '[]' : ($value === null ? 'null' : \var_export($value, true)); } $default = \sprintf(' = <%s>%s', $typeStyle, OutputFormatter::escape($value), $typeStyle); } else { $default = ''; } $params[] = \sprintf( '%s%s%s$%s%s', $param->isPassedByReference() ? '&' : '', $hint, $hint !== '' ? ' ' : '', $param->getName(), $default ); } return $params; } /** * Print function param or return type(s). * * @param \ReflectionType $type */ private static function formatReflectionType(?\ReflectionType $type, bool $indicateNullable): string { if ($type === null) { return ''; } if ($type instanceof \ReflectionUnionType) { $delimeter = '|'; } elseif ($type instanceof \ReflectionIntersectionType) { $delimeter = '&'; } else { return self::formatReflectionNamedType($type, $indicateNullable); } $formattedTypes = []; foreach ($type->getTypes() as $namedType) { $formattedTypes[] = self::formatReflectionNamedType($namedType, $indicateNullable); } return \implode($delimeter, $formattedTypes); } /** * Print a single named type. */ private static function formatReflectionNamedType(\ReflectionNamedType $type, bool $indicateNullable): string { $typeStyle = $type->isBuiltin() ? 'keyword' : 'class'; $nullable = $indicateNullable && $type->allowsNull() ? '?' : ''; return \sprintf('<%s>%s%s', $typeStyle, $nullable, OutputFormatter::escape($type->getName()), $typeStyle); } } PKZqy]ReflectorFormatter.phpnuW+AgetTrace(); \array_unshift($trace, [ 'function' => '', 'file' => $throwable->getFile() !== null ? $throwable->getFile() : 'n/a', 'line' => $throwable->getLine() !== null ? $throwable->getLine() : 'n/a', 'args' => [], ]); if (!$includePsy) { for ($i = \count($trace) - 1; $i >= 0; $i--) { $thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function']; if (\preg_match('/\\\\?Psy\\\\/', $thing)) { $trace = \array_slice($trace, $i + 1); break; } } } for ($i = 0, $count = \min($count, \count($trace)); $i < $count; $i++) { $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; $function = $trace[$i]['function']; $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; // Make file paths relative to cwd if ($cwd !== false) { $file = \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file); } // Leave execution loop out of the `eval()'d code` lines if (\preg_match("#/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code$#", \str_replace('\\', '/', $file))) { $file = "eval()'d code"; } // Skip any lines that don't match our filter options if ($filter !== null && !$filter->match(\sprintf('%s%s%s() at %s:%s', $class, $type, $function, $file, $line))) { continue; } $lines[] = \sprintf( ' %s%s%s() at %s:%s', OutputFormatter::escape($class), OutputFormatter::escape($type), OutputFormatter::escape($function), OutputFormatter::escape($file), OutputFormatter::escape($line) ); } return $lines; } } PKvZϲIntlFormatterInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; /** * Formats ICU message patterns. * * @author Nicolas Grekas */ interface IntlFormatterInterface { /** * Formats a localized message using rules defined by ICU MessageFormat. * * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details */ public function formatIntl(string $message, string $locale, array $parameters = []): string; } PKvZ]sVVMessageFormatterInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; /** * @author Guilherme Blanco * @author Abdellatif Ait boudad */ interface MessageFormatterInterface { /** * Formats a localized message pattern with given arguments. * * @param string $message The message (may also be an object that can be cast to string) * @param string $locale The message locale * @param array $parameters An array of parameters for the message */ public function format(string $message, string $locale, array $parameters = []): string; } PKvZBBIntlFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\LogicException; /** * @author Guilherme Blanco * @author Abdellatif Ait boudad */ class IntlFormatter implements IntlFormatterInterface { private $hasMessageFormatter; private $cache = []; public function formatIntl(string $message, string $locale, array $parameters = []): string { // MessageFormatter constructor throws an exception if the message is empty if ('' === $message) { return ''; } if (!$formatter = $this->cache[$locale][$message] ?? null) { if (!$this->hasMessageFormatter ??= class_exists(\MessageFormatter::class)) { throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.'); } try { $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message); } catch (\IntlException $e) { throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): ', intl_get_error_code()).intl_get_error_message(), 0, $e); } } foreach ($parameters as $key => $value) { if (\in_array($key[0] ?? null, ['%', '{'], true)) { unset($parameters[$key]); $parameters[trim($key, '%{ }')] = $value; } } if (false === $message = $formatter->format($parameters)) { throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()).$formatter->getErrorMessage()); } return $message; } } PKvZ<+ddMessageFormatter.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; use Symfony\Component\Translation\IdentityTranslator; use Symfony\Contracts\Translation\TranslatorInterface; // Help opcache.preload discover always-needed symbols class_exists(IntlFormatter::class); /** * @author Abdellatif Ait boudad */ class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterface { private TranslatorInterface $translator; private IntlFormatterInterface $intlFormatter; /** * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization */ public function __construct(TranslatorInterface $translator = null, IntlFormatterInterface $intlFormatter = null) { $this->translator = $translator ?? new IdentityTranslator(); $this->intlFormatter = $intlFormatter ?? new IntlFormatter(); } public function format(string $message, string $locale, array $parameters = []): string { if ($this->translator instanceof TranslatorInterface) { return $this->translator->trans($message, $parameters, null, $locale); } return strtr($message, $parameters); } public function formatIntl(string $message, string $locale, array $parameters = []): string { return $this->intlFormatter->formatIntl($message, $locale, $parameters); } } PK ZdOutputFormatter.phpnuW+APK Z`)6OutputFormatterInterface.phpnuW+APK Z%$NullOutputFormatter.phpnuW+APK Z#ó%d)WrappableOutputFormatterInterface.phpnuW+APK Zls ,OutputFormatterStyle.phpnuW+APK Z&!9OutputFormatterStyleInterface.phpnuW+APK Zq/0YY>NullOutputFormatterStyle.phpnuW+APK ZXǿ  fDOutputFormatterStyleStack.phpnuW+APKZJ J NLogstashFormatter.phpnuW+APKZEE[\ElasticsearchFormatter.phpnuW+APKZ!te5 5 cFlowdockFormatter.phpnuW+APKZqRdnLogmaticFormatter.phpnuW+APKZXutFluentdFormatter.phpnuW+APKZ1ei}ElasticaFormatter.phpnuW+APKZjjƅGoogleCloudLoggingFormatter.phpnuW+APKZ4zFormatterInterface.phpnuW+APKZFScalarFormatter.phpnuW+APKZ ؑNLogglyFormatter.phpnuW+APKZ& HtmlFormatter.phpnuW+APKZ݃GelfMessageFormatter.phpnuW+APKZ ,qqJsonFormatter.phpnuW+APKZ13caawWildfireFormatter.phpnuW+APKZllMongoDBFormatter.phpnuW+APKZ](M``ChromePHPFormatter.phpnuW+APKZ>/ / sNormalizerFormatter.phpnuW+APKZk  %LineFormatter.phpnuW+APKZʤSc334DDocblockFormatter.phpnuW+APKZ~ ' 'VCodeFormatter.phpnuW+APKZ^ۦ }Formatter.phpnuW+APKZȑd,,SignatureFormatter.phpnuW+APKZqy]̬ReflectorFormatter.phpnuW+APKZ  ĮTraceFormatter.phpnuW+APKvZϲ!IntlFormatterInterface.phpnuW+APKvZ]sVVMessageFormatterInterface.phpnuW+APKvZBBIntlFormatter.phpnuW+APKvZ<+ddCMessageFormatter.phpnuW+APK$$(