Kaynağa Gözat

Handle __toString to serialize objects which are not json-serializable in JsonFormatter, fixes #1733

Jordi Boggiano 3 yıl önce
ebeveyn
işleme
cf0f4b3814

+ 18 - 5
src/Monolog/Formatter/JsonFormatter.php

@@ -178,12 +178,25 @@ class JsonFormatter extends NormalizerFormatter
             return $normalized;
         }
 
-        if ($data instanceof \DateTimeInterface) {
-            return $this->formatDate($data);
-        }
+        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 (method_exists($data, '__toString')) {
+                return $data->__toString();
+            }
 
-        if ($data instanceof Throwable) {
-            return $this->normalizeException($data, $depth);
+            return $data;
         }
 
         if (is_resource($data)) {

+ 56 - 0
tests/Monolog/Formatter/JsonFormatterTest.php

@@ -11,6 +11,7 @@
 
 namespace Monolog\Formatter;
 
+use JsonSerializable;
 use Monolog\Logger;
 use Monolog\Test\TestCase;
 
@@ -314,4 +315,59 @@ class JsonFormatterTest extends TestCase
             $record
         );
     }
+
+    public function testFormatObjects()
+    {
+        $formatter = new JsonFormatter();
+
+        $record = $formatter->format(array(
+            'level' => 100,
+            'level_name' => 'DEBUG',
+            'channel' => 'test',
+            'message' => 'Testing',
+            'context' => array(
+                'public' => new TestJsonNormPublic,
+                'private' => new TestJsonNormPrivate,
+                'withToStringAndJson' => new TestJsonNormWithToStringAndJson,
+                'withToString' => new TestJsonNormWithToString,
+            ),
+            'extra' => array(),
+        ));
+
+        $this->assertSame(
+            '{"level":100,"level_name":"DEBUG","channel":"test","message":"Testing","context":{"public":{"foo":"fooValue"},"private":{},"withToStringAndJson":["json serialized"],"withToString":"stringified"},"extra":{}}'."\n",
+            $record
+        );
+    }
+}
+
+class TestJsonNormPublic
+{
+    public $foo = 'fooValue';
+}
+
+class TestJsonNormPrivate
+{
+    private $foo = 'fooValue';
+}
+
+class TestJsonNormWithToStringAndJson implements JsonSerializable
+{
+    public function jsonSerialize()
+    {
+        return ['json serialized'];
+    }
+
+    public function __toString()
+    {
+        return 'SHOULD NOT SHOW UP';
+    }
+}
+
+class TestJsonNormWithToString
+{
+    public function __toString()
+    {
+        return 'stringified';
+    }
 }