From 21bea751d0108e48de35408ecdfe189ea6ed0539 Mon Sep 17 00:00:00 2001
From: Filip Halaxa <filip@halaxa.cz>
Date: Sat, 23 Nov 2024 22:18:44 +0100
Subject: [PATCH] Recursive focused performace optimizations. Ops outside the
 main foreach in Parser now matter as new Parser is created for each level

---
 src/JsonDecoder/ExtJsonDecoder.php    | 14 ++++++++++++++
 src/JsonDecoder/StringOnlyDecoder.php | 14 ++++++++++++++
 src/Parser.php                        | 22 ++++++++++++----------
 3 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/src/JsonDecoder/ExtJsonDecoder.php b/src/JsonDecoder/ExtJsonDecoder.php
index 7a3df20..31ec068 100644
--- a/src/JsonDecoder/ExtJsonDecoder.php
+++ b/src/JsonDecoder/ExtJsonDecoder.php
@@ -21,6 +21,11 @@ class ExtJsonDecoder implements ItemDecoder
      */
     private $options;
 
+    /**
+     * @var self
+     */
+    private static $instance;
+
     public function __construct($assoc = false, $depth = 512, $options = 0)
     {
         $this->assoc = $assoc;
@@ -37,4 +42,13 @@ class ExtJsonDecoder implements ItemDecoder
 
         return new ValidResult($decoded);
     }
+
+    public static function instance(): self
+    {
+        if ( ! self::$instance) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
 }
diff --git a/src/JsonDecoder/StringOnlyDecoder.php b/src/JsonDecoder/StringOnlyDecoder.php
index ebdf544..2858379 100644
--- a/src/JsonDecoder/StringOnlyDecoder.php
+++ b/src/JsonDecoder/StringOnlyDecoder.php
@@ -9,6 +9,11 @@ class StringOnlyDecoder implements ItemDecoder
     /** @var ItemDecoder */
     private $innerDecoder;
 
+    /**
+     * @var self
+     */
+    private static $instance;
+
     public function __construct(ItemDecoder $innerDecoder)
     {
         $this->innerDecoder = $innerDecoder;
@@ -22,4 +27,13 @@ class StringOnlyDecoder implements ItemDecoder
 
         return new ValidResult($jsonValue);
     }
+
+    public static function instance(ItemDecoder $innerDecoder): self
+    {
+        if ( ! self::$instance) {
+            self::$instance = new self($innerDecoder);
+        }
+
+        return self::$instance;
+    }
 }
diff --git a/src/Parser.php b/src/Parser.php
index 57b6ac4..d6f62e5 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -67,7 +67,7 @@ class Parser implements \IteratorAggregate, PositionAware
     private $recursive;
 
     /** @var array */
-    private static $allBytes;
+    private static $tokenTypes;
 
     /**
      * @param array|string $jsonPointer Follows json pointer RFC https://tools.ietf.org/html/rfc6901
@@ -97,10 +97,15 @@ class Parser implements \IteratorAggregate, PositionAware
             throw new InvalidArgumentException('$tokens must be either an instance of Iterator or IteratorAggregate.');
         }
 
-        $this->jsonDecoder = $jsonDecoder ?: new ExtJsonDecoder();
-        if ($recursive) {
-            $this->jsonDecoder = new StringOnlyDecoder($this->jsonDecoder);
+        if ($jsonDecoder instanceof StringOnlyDecoder) {
+            $this->jsonDecoder = $jsonDecoder;
+        } else {
+            $this->jsonDecoder = $jsonDecoder ?: new ExtJsonDecoder();
+            if ($recursive) {
+                $this->jsonDecoder = new StringOnlyDecoder($this->jsonDecoder);
+            }
         }
+
         $this->recursive = $recursive;
     }
 
@@ -127,11 +132,11 @@ class Parser implements \IteratorAggregate, PositionAware
      */
     private function createGenerator(): Generator
     {
-        if ( ! self::$allBytes) {
-            self::$allBytes = $this->tokenTypes();
+        if ( ! self::$tokenTypes) {
+            self::$tokenTypes = $this->tokenTypes();
         }
 
-        $tokenTypes = self::$allBytes;
+        $tokenTypes = self::$tokenTypes;
 
         $iteratorStruct = null;
         $currentPath = &$this->currentPath;
@@ -333,9 +338,6 @@ class Parser implements \IteratorAggregate, PositionAware
         $generator = $this->getIterator();
 
         while ($generator->valid()) {
-//            var_dump(is_object($generator->current()) ? get_class($generator->current()) : $generator->current());
-//            $generator->key();
-//            $generator->current();
             $generator->next();
         }
     }