mirror of
https://github.com/Seldaek/monolog.git
synced 2025-07-31 18:30:15 +02:00
Merge branch '1.x'
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Monolog\Formatter;
|
||||
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Class FluentdFormatter
|
||||
*
|
||||
@@ -71,7 +73,7 @@ class FluentdFormatter implements FormatterInterface
|
||||
$message['level_name'] = $record['level_name'];
|
||||
}
|
||||
|
||||
return json_encode([$tag, $record['datetime']->getTimestamp(), $message]);
|
||||
return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]);
|
||||
}
|
||||
|
||||
public function formatBatch(array $records): string
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Formatter;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Formats incoming records into an HTML table
|
||||
@@ -133,6 +134,6 @@ class HtmlFormatter extends NormalizerFormatter
|
||||
|
||||
$data = $this->normalize($data);
|
||||
|
||||
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true);
|
||||
}
|
||||
}
|
||||
|
@@ -152,7 +152,7 @@ class LineFormatter extends NormalizerFormatter
|
||||
return (string) $data;
|
||||
}
|
||||
|
||||
return (string) $this->toJson($data, true);
|
||||
return $this->toJson($data, true);
|
||||
}
|
||||
|
||||
protected function replaceNewlines(string $str): string
|
||||
|
@@ -94,7 +94,7 @@ class MongoDBFormatter implements FormatterInterface
|
||||
$formattedException = [
|
||||
'class' => Utils::getClass($exception),
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode(),
|
||||
'code' => (int) $exception->getCode(),
|
||||
'file' => $exception->getFile() . ':' . $exception->getLine(),
|
||||
];
|
||||
|
||||
|
@@ -28,7 +28,7 @@ class NormalizerFormatter implements FormatterInterface
|
||||
protected $maxNormalizeDepth = 9;
|
||||
protected $maxNormalizeItemCount = 1000;
|
||||
|
||||
private $jsonEncodeOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION;
|
||||
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
|
||||
|
||||
/**
|
||||
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||
@@ -189,7 +189,7 @@ class NormalizerFormatter implements FormatterInterface
|
||||
$data = [
|
||||
'class' => Utils::getClass($e),
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
'code' => (int) $e->getCode(),
|
||||
'file' => $e->getFile().':'.$e->getLine(),
|
||||
];
|
||||
|
||||
@@ -202,8 +202,8 @@ class NormalizerFormatter implements FormatterInterface
|
||||
$data['faultactor'] = $e->faultactor;
|
||||
}
|
||||
|
||||
if (isset($e->detail)) {
|
||||
$data['detail'] = $e->detail;
|
||||
if (isset($e->detail) && (is_string($e->detail) || is_object($e->detail) || is_array($e->detail))) {
|
||||
$data['detail'] = is_string($e->detail) ? $e->detail : reset($e->detail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,130 +226,11 @@ class NormalizerFormatter implements FormatterInterface
|
||||
*
|
||||
* @param mixed $data
|
||||
* @throws \RuntimeException if encoding fails and errors are not ignored
|
||||
* @return string|bool
|
||||
* @return string if encoding fails and ignoreErrors is true 'null' is returned
|
||||
*/
|
||||
protected function toJson($data, bool $ignoreErrors = false)
|
||||
protected function toJson($data, bool $ignoreErrors = false): string
|
||||
{
|
||||
// suppress json_encode errors since it's twitchy with some inputs
|
||||
if ($ignoreErrors) {
|
||||
return @$this->jsonEncode($data);
|
||||
}
|
||||
|
||||
$json = $this->jsonEncode($data);
|
||||
|
||||
if ($json === false) {
|
||||
$json = $this->handleJsonError(json_last_error(), $data);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @return string|bool JSON encoded data or false on failure
|
||||
*/
|
||||
private function jsonEncode($data)
|
||||
{
|
||||
return json_encode($data, $this->jsonEncodeOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a json_encode failure.
|
||||
*
|
||||
* If the failure is due to invalid string encoding, try to clean the
|
||||
* input and encode again. If the second encoding attempt fails, the
|
||||
* initial error is not encoding related or the input can't be cleaned then
|
||||
* raise a descriptive exception.
|
||||
*
|
||||
* @param int $code return code of json_last_error function
|
||||
* @param mixed $data data that was meant to be encoded
|
||||
* @throws \RuntimeException if failure can't be corrected
|
||||
* @return string JSON encoded data after error correction
|
||||
*/
|
||||
private function handleJsonError(int $code, $data): string
|
||||
{
|
||||
if ($code !== JSON_ERROR_UTF8) {
|
||||
$this->throwEncodeError($code, $data);
|
||||
}
|
||||
|
||||
if (is_string($data)) {
|
||||
$this->detectAndCleanUtf8($data);
|
||||
} elseif (is_array($data)) {
|
||||
array_walk_recursive($data, [$this, 'detectAndCleanUtf8']);
|
||||
} else {
|
||||
$this->throwEncodeError($code, $data);
|
||||
}
|
||||
|
||||
$json = $this->jsonEncode($data);
|
||||
|
||||
if ($json === false) {
|
||||
$this->throwEncodeError(json_last_error(), $data);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception according to a given code with a customized message
|
||||
*
|
||||
* @param int $code return code of json_last_error function
|
||||
* @param mixed $data data that was meant to be encoded
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function throwEncodeError(int $code, $data)
|
||||
{
|
||||
switch ($code) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
$msg = 'Maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$msg = 'Underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$msg = 'Unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$msg = 'Unknown error';
|
||||
}
|
||||
|
||||
throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect invalid UTF-8 string characters and convert to valid UTF-8.
|
||||
*
|
||||
* Valid UTF-8 input will be left unmodified, but strings containing
|
||||
* invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
|
||||
* original encoding of ISO-8859-15. This conversion may result in
|
||||
* incorrect output if the actual encoding was not ISO-8859-15, but it
|
||||
* will be clean UTF-8 output and will not rely on expensive and fragile
|
||||
* detection algorithms.
|
||||
*
|
||||
* Function converts the input in place in the passed variable so that it
|
||||
* can be used as a callback for array_walk_recursive.
|
||||
*
|
||||
* @param mixed &$data Input to check and convert if needed
|
||||
* @private
|
||||
*/
|
||||
public function detectAndCleanUtf8(&$data)
|
||||
{
|
||||
if (is_string($data) && !preg_match('//u', $data)) {
|
||||
$data = preg_replace_callback(
|
||||
'/[\x80-\xFF]+/',
|
||||
function ($m) {
|
||||
return utf8_encode($m[0]);
|
||||
},
|
||||
$data
|
||||
);
|
||||
$data = str_replace(
|
||||
['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'],
|
||||
['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'],
|
||||
$data
|
||||
);
|
||||
}
|
||||
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
|
||||
}
|
||||
|
||||
protected function formatDate(\DateTimeInterface $date)
|
||||
|
@@ -167,21 +167,22 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||
|
||||
private static function handleStyles(string $formatted): array
|
||||
{
|
||||
$args = [static::quote('font-weight: normal')];
|
||||
$args = [];
|
||||
$format = '%c' . $formatted;
|
||||
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
|
||||
foreach (array_reverse($matches) as $match) {
|
||||
$args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
|
||||
$args[] = '"font-weight: normal"';
|
||||
$args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
|
||||
|
||||
$pos = $match[0][1];
|
||||
$format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0]));
|
||||
}
|
||||
|
||||
array_unshift($args, static::quote($format));
|
||||
$args[] = static::quote('font-weight: normal');
|
||||
$args[] = static::quote($format);
|
||||
|
||||
return $args;
|
||||
return array_reverse($args);
|
||||
}
|
||||
|
||||
private static function handleCustomStyles(string $style, string $string): string
|
||||
|
@@ -14,6 +14,7 @@ namespace Monolog\Handler;
|
||||
use Monolog\Formatter\ChromePHPFormatter;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
|
||||
@@ -144,7 +145,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||
self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? '';
|
||||
}
|
||||
|
||||
$json = @json_encode(self::$json);
|
||||
$json = Utils::jsonEncode(self::$json, null, true);
|
||||
$data = base64_encode(utf8_encode($json));
|
||||
if (strlen($data) > 3 * 1024) {
|
||||
self::$overflowed = true;
|
||||
@@ -159,7 +160,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||
'extra' => [],
|
||||
];
|
||||
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
|
||||
$json = @json_encode(self::$json);
|
||||
$json = Utils::jsonEncode(self::$json, null, true);
|
||||
$data = base64_encode(utf8_encode($json));
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* Logs to Cube.
|
||||
@@ -122,9 +123,9 @@ class CubeHandler extends AbstractProcessingHandler
|
||||
$data['data']['level'] = $record['level'];
|
||||
|
||||
if ($this->scheme === 'http') {
|
||||
$this->writeHttp(json_encode($data));
|
||||
$this->writeHttp(Utils::jsonEncode($data));
|
||||
} else {
|
||||
$this->writeUdp(json_encode($data));
|
||||
$this->writeUdp(Utils::jsonEncode($data));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use Monolog\Formatter\FlowdockFormatter;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
|
||||
@@ -96,7 +97,7 @@ class FlowdockHandler extends SocketHandler
|
||||
*/
|
||||
private function buildContent(array $record): string
|
||||
{
|
||||
return json_encode($record['formatted']['flowdock']);
|
||||
return Utils::jsonEncode($record['formatted']['flowdock']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
|
||||
/**
|
||||
* IFTTTHandler uses cURL to trigger IFTTT Maker actions
|
||||
@@ -53,7 +54,7 @@ class IFTTTHandler extends AbstractProcessingHandler
|
||||
"value2" => $record["level_name"],
|
||||
"value3" => $record["message"],
|
||||
];
|
||||
$postString = json_encode($postData);
|
||||
$postString = Utils::jsonEncode($postData);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey);
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use Monolog\Formatter\NormalizerFormatter;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
|
||||
@@ -182,7 +183,7 @@ class NewRelicHandler extends AbstractProcessingHandler
|
||||
if (null === $value || is_scalar($value)) {
|
||||
newrelic_add_custom_parameter($key, $value);
|
||||
} else {
|
||||
newrelic_add_custom_parameter($key, @json_encode($value));
|
||||
newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ namespace Monolog\Handler;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use PhpConsole\Connector;
|
||||
use PhpConsole\Handler as VendorPhpConsoleHandler;
|
||||
use PhpConsole\Helper;
|
||||
@@ -188,7 +189,7 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||
$tags = $this->getRecordTags($record);
|
||||
$message = $record['message'];
|
||||
if ($record['context']) {
|
||||
$message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context'])));
|
||||
$message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true);
|
||||
}
|
||||
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Monolog\Handler\Slack;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use Monolog\Formatter\NormalizerFormatter;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
|
||||
@@ -211,14 +212,13 @@ class SlackRecord
|
||||
public function stringify(array $fields): string
|
||||
{
|
||||
$normalized = $this->normalizerFormatter->format($fields);
|
||||
$prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
|
||||
|
||||
$hasSecondDimension = count(array_filter($normalized, 'is_array'));
|
||||
$hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
|
||||
|
||||
return $hasSecondDimension || $hasNonNumericKeys
|
||||
? json_encode($normalized, $prettyPrintFlag|JSON_UNESCAPED_UNICODE)
|
||||
: json_encode($normalized, JSON_UNESCAPED_UNICODE);
|
||||
? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)
|
||||
: Utils::jsonEncode($normalized, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use Monolog\Handler\Slack\SlackRecord;
|
||||
|
||||
/**
|
||||
@@ -115,7 +116,7 @@ class SlackHandler extends SocketHandler
|
||||
$dataArray['token'] = $this->token;
|
||||
|
||||
if (!empty($dataArray['attachments'])) {
|
||||
$dataArray['attachments'] = json_encode($dataArray['attachments']);
|
||||
$dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
|
||||
}
|
||||
|
||||
return $dataArray;
|
||||
|
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Utils;
|
||||
use Monolog\Handler\Slack\SlackRecord;
|
||||
|
||||
/**
|
||||
@@ -92,7 +93,7 @@ class SlackWebhookHandler extends AbstractProcessingHandler
|
||||
protected function write(array $record): void
|
||||
{
|
||||
$postData = $this->slackRecord->getSlackData($record);
|
||||
$postString = json_encode($postData);
|
||||
$postString = Utils::jsonEncode($postData);
|
||||
|
||||
$ch = curl_init();
|
||||
$options = array(
|
||||
|
@@ -13,6 +13,8 @@ namespace Monolog;
|
||||
|
||||
final class Utils
|
||||
{
|
||||
const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -31,4 +33,135 @@ final class Utils
|
||||
|
||||
return substr($string, $start, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON representation of a value
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
|
||||
* @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null
|
||||
* @throws \RuntimeException if encoding fails and errors are not ignored
|
||||
* @return string when errors are ignored and the encoding fails, "null" is returned which is valid json for null
|
||||
*/
|
||||
public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = false): string
|
||||
{
|
||||
if (null === $encodeFlags) {
|
||||
$encodeFlags = self::DEFAULT_JSON_FLAGS;
|
||||
}
|
||||
|
||||
$json = json_encode($data, $encodeFlags);
|
||||
|
||||
if (false === $json) {
|
||||
if ($ignoreErrors) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
$json = self::handleJsonError(json_last_error(), $data);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a json_encode failure.
|
||||
*
|
||||
* If the failure is due to invalid string encoding, try to clean the
|
||||
* input and encode again. If the second encoding attempt fails, the
|
||||
* inital error is not encoding related or the input can't be cleaned then
|
||||
* raise a descriptive exception.
|
||||
*
|
||||
* @param int $code return code of json_last_error function
|
||||
* @param mixed $data data that was meant to be encoded
|
||||
* @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
|
||||
* @throws \RuntimeException if failure can't be corrected
|
||||
* @return string JSON encoded data after error correction
|
||||
*/
|
||||
public static function handleJsonError(int $code, $data, ?int $encodeFlags = null): string
|
||||
{
|
||||
if ($code !== JSON_ERROR_UTF8) {
|
||||
self::throwEncodeError($code, $data);
|
||||
}
|
||||
|
||||
if (is_string($data)) {
|
||||
self::detectAndCleanUtf8($data);
|
||||
} elseif (is_array($data)) {
|
||||
array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8'));
|
||||
} else {
|
||||
self::throwEncodeError($code, $data);
|
||||
}
|
||||
|
||||
if (null === $encodeFlags) {
|
||||
$encodeFlags = self::DEFAULT_JSON_FLAGS;
|
||||
}
|
||||
|
||||
$json = json_encode($data, $encodeFlags);
|
||||
|
||||
if ($json === false) {
|
||||
self::throwEncodeError(json_last_error(), $data);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception according to a given code with a customized message
|
||||
*
|
||||
* @param int $code return code of json_last_error function
|
||||
* @param mixed $data data that was meant to be encoded
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private static function throwEncodeError(int $code, $data)
|
||||
{
|
||||
switch ($code) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
$msg = 'Maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$msg = 'Underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$msg = 'Unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$msg = 'Unknown error';
|
||||
}
|
||||
|
||||
throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect invalid UTF-8 string characters and convert to valid UTF-8.
|
||||
*
|
||||
* Valid UTF-8 input will be left unmodified, but strings containing
|
||||
* invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
|
||||
* original encoding of ISO-8859-15. This conversion may result in
|
||||
* incorrect output if the actual encoding was not ISO-8859-15, but it
|
||||
* will be clean UTF-8 output and will not rely on expensive and fragile
|
||||
* detection algorithms.
|
||||
*
|
||||
* Function converts the input in place in the passed variable so that it
|
||||
* can be used as a callback for array_walk_recursive.
|
||||
*
|
||||
* @param mixed &$data Input to check and convert if needed
|
||||
*/
|
||||
private static function detectAndCleanUtf8(&$data)
|
||||
{
|
||||
if (is_string($data) && !preg_match('//u', $data)) {
|
||||
$data = preg_replace_callback(
|
||||
'/[\x80-\xFF]+/',
|
||||
function ($m) {
|
||||
return utf8_encode($m[0]);
|
||||
},
|
||||
$data
|
||||
);
|
||||
$data = str_replace(
|
||||
['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'],
|
||||
['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'],
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -87,7 +87,7 @@ class NormalizerFormatterTest extends TestCase
|
||||
}
|
||||
|
||||
$formatter = new NormalizerFormatter('Y-m-d');
|
||||
$e = new \SoapFault('foo', 'bar', 'hello', 'world');
|
||||
$e = new \SoapFault('foo', 'bar', 'hello', (object) ['foo' => 'world']);
|
||||
$formatted = $formatter->format([
|
||||
'exception' => $e,
|
||||
]);
|
||||
@@ -188,7 +188,7 @@ class NormalizerFormatterTest extends TestCase
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertEquals(@json_encode([$foo, $bar]), $res);
|
||||
$this->assertEquals('null', $res);
|
||||
}
|
||||
|
||||
public function testCanNormalizeReferences()
|
||||
@@ -223,7 +223,7 @@ class NormalizerFormatterTest extends TestCase
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertEquals(@json_encode([$resource]), $res);
|
||||
$this->assertEquals('null', $res);
|
||||
}
|
||||
|
||||
public function testNormalizeHandleLargeArraysWithExactly1000Items()
|
||||
@@ -330,66 +330,6 @@ class NormalizerFormatterTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $in Input
|
||||
* @param mixed $expect Expected output
|
||||
* @covers Monolog\Formatter\NormalizerFormatter::detectAndCleanUtf8
|
||||
* @dataProvider providesDetectAndCleanUtf8
|
||||
*/
|
||||
public function testDetectAndCleanUtf8($in, $expect)
|
||||
{
|
||||
$formatter = new NormalizerFormatter();
|
||||
$formatter->detectAndCleanUtf8($in);
|
||||
$this->assertSame($expect, $in);
|
||||
}
|
||||
|
||||
public function providesDetectAndCleanUtf8()
|
||||
{
|
||||
$obj = new \stdClass;
|
||||
|
||||
return [
|
||||
'null' => [null, null],
|
||||
'int' => [123, 123],
|
||||
'float' => [123.45, 123.45],
|
||||
'bool false' => [false, false],
|
||||
'bool true' => [true, true],
|
||||
'ascii string' => ['abcdef', 'abcdef'],
|
||||
'latin9 string' => ["\xB1\x31\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xFF", '±1€ŠšŽžŒœŸÿ'],
|
||||
'unicode string' => ['¤¦¨´¸¼½¾€ŠšŽžŒœŸ', '¤¦¨´¸¼½¾€ŠšŽžŒœŸ'],
|
||||
'empty array' => [[], []],
|
||||
'array' => [['abcdef'], ['abcdef']],
|
||||
'object' => [$obj, $obj],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @dataProvider providesHandleJsonErrorFailure
|
||||
*/
|
||||
public function testHandleJsonErrorFailure($code, $msg)
|
||||
{
|
||||
$formatter = new NormalizerFormatter();
|
||||
$reflMethod = new \ReflectionMethod($formatter, 'handleJsonError');
|
||||
$reflMethod->setAccessible(true);
|
||||
|
||||
$this->expectException('RuntimeException');
|
||||
$this->expectExceptionMessage($msg);
|
||||
$reflMethod->invoke($formatter, $code, 'faked');
|
||||
}
|
||||
|
||||
public function providesHandleJsonErrorFailure()
|
||||
{
|
||||
return [
|
||||
'depth' => [JSON_ERROR_DEPTH, 'Maximum stack depth exceeded'],
|
||||
'state' => [JSON_ERROR_STATE_MISMATCH, 'Underflow or the modes mismatch'],
|
||||
'ctrl' => [JSON_ERROR_CTRL_CHAR, 'Unexpected control character found'],
|
||||
'default' => [-1, 'Unknown error'],
|
||||
];
|
||||
}
|
||||
|
||||
// This happens i.e. in React promises or Guzzle streams where stream wrappers are registered
|
||||
// and no file or line are included in the trace because it's treated as internal function
|
||||
public function testExceptionTraceWithArgs()
|
||||
{
|
||||
try {
|
||||
|
@@ -48,6 +48,22 @@ EOF;
|
||||
$this->assertEquals($expected, $this->generateScript());
|
||||
}
|
||||
|
||||
public function testStylingMultiple()
|
||||
{
|
||||
$handler = new BrowserConsoleHandler();
|
||||
$handler->setFormatter($this->getIdentityFormatter());
|
||||
|
||||
$handler->handle($this->getRecord(Logger::DEBUG, 'foo[[bar]]{color: red}[[baz]]{color: blue}'));
|
||||
|
||||
$expected = <<<EOF
|
||||
(function (c) {if (c && c.groupCollapsed) {
|
||||
c.log("%cfoo%cbar%c%cbaz%c", "font-weight: normal", "color: red", "font-weight: normal", "color: blue", "font-weight: normal");
|
||||
}})(console);
|
||||
EOF;
|
||||
|
||||
$this->assertEquals($expected, $this->generateScript());
|
||||
}
|
||||
|
||||
public function testEscaping()
|
||||
{
|
||||
$handler = new BrowserConsoleHandler();
|
||||
|
70
tests/Monolog/UtilsTest.php
Normal file
70
tests/Monolog/UtilsTest.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog;
|
||||
|
||||
class UtilsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @dataProvider providesHandleJsonErrorFailure
|
||||
*/
|
||||
public function testHandleJsonErrorFailure($code, $msg)
|
||||
{
|
||||
$this->expectException('RuntimeException', $msg);
|
||||
Utils::handleJsonError($code, 'faked');
|
||||
}
|
||||
|
||||
public function providesHandleJsonErrorFailure()
|
||||
{
|
||||
return [
|
||||
'depth' => [JSON_ERROR_DEPTH, 'Maximum stack depth exceeded'],
|
||||
'state' => [JSON_ERROR_STATE_MISMATCH, 'Underflow or the modes mismatch'],
|
||||
'ctrl' => [JSON_ERROR_CTRL_CHAR, 'Unexpected control character found'],
|
||||
'default' => [-1, 'Unknown error'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $in Input
|
||||
* @param mixed $expect Expected output
|
||||
* @covers Monolog\Formatter\NormalizerFormatter::detectAndCleanUtf8
|
||||
* @dataProvider providesDetectAndCleanUtf8
|
||||
*/
|
||||
public function testDetectAndCleanUtf8($in, $expect)
|
||||
{
|
||||
$reflMethod = new \ReflectionMethod(Utils::class, 'detectAndCleanUtf8');
|
||||
$reflMethod->setAccessible(true);
|
||||
$args = [&$in];
|
||||
$reflMethod->invokeArgs(null, $args);
|
||||
$this->assertSame($expect, $in);
|
||||
}
|
||||
|
||||
public function providesDetectAndCleanUtf8()
|
||||
{
|
||||
$obj = new \stdClass;
|
||||
|
||||
return [
|
||||
'null' => [null, null],
|
||||
'int' => [123, 123],
|
||||
'float' => [123.45, 123.45],
|
||||
'bool false' => [false, false],
|
||||
'bool true' => [true, true],
|
||||
'ascii string' => ['abcdef', 'abcdef'],
|
||||
'latin9 string' => ["\xB1\x31\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xFF", '±1€ŠšŽžŒœŸÿ'],
|
||||
'unicode string' => ['¤¦¨´¸¼½¾€ŠšŽžŒœŸ', '¤¦¨´¸¼½¾€ŠšŽžŒœŸ'],
|
||||
'empty array' => [[], []],
|
||||
'array' => [['abcdef'], ['abcdef']],
|
||||
'object' => [$obj, $obj],
|
||||
];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user