From 782a9ab9cf0e78dc6575cc53dc00766500cc2d92 Mon Sep 17 00:00:00 2001 From: John Rayes Date: Thu, 9 May 2024 23:42:02 -0700 Subject: [PATCH] Improve performance I have identified three main bottlenecks when profiling the code with xdebug: 1. `array_shift()`: The array of tokens ended up being renumbered on each iteration. So we instead set consumed tokens to `null`. 2. `mb_substr()`: Using mutltibyte here is unnecessary and overkill, when we can just operate directly on the string. 3. `Liquid::get()`: This one surprised me, but due to the number of calls, here it is. Shave off more processing time by accessing the config array directly. --- src/Liquid/AbstractBlock.php | 20 ++++++++++++-------- src/Liquid/Tag/TagRaw.php | 8 ++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Liquid/AbstractBlock.php b/src/Liquid/AbstractBlock.php index 6e0820b2..b454392d 100644 --- a/src/Liquid/AbstractBlock.php +++ b/src/Liquid/AbstractBlock.php @@ -51,16 +51,20 @@ public function getNodelist() */ public function parse(array &$tokens) { - $startRegexp = new Regexp('/^' . Liquid::get('TAG_START') . '/'); - $tagRegexp = new Regexp('/^' . Liquid::get('TAG_START') . Liquid::get('WHITESPACE_CONTROL') . '?\s*(\w+)\s*(.*?)' . Liquid::get('WHITESPACE_CONTROL') . '?' . Liquid::get('TAG_END') . '$/s'); - $variableStartRegexp = new Regexp('/^' . Liquid::get('VARIABLE_START') . '/'); + $startRegexp = new Regexp('/^' . Liquid::$config['TAG_START'] . '/'); + $tagRegexp = new Regexp('/^' . Liquid::$config['TAG_START'] . Liquid::$config['WHITESPACE_CONTROL'] . '?\s*(\w+)\s*(.*?)' . Liquid::$config['WHITESPACE_CONTROL'] . '?' . Liquid::$config['TAG_END'] . '$/s'); + $variableStartRegexp = new Regexp('/^' . Liquid::$config['VARIABLE_START'] . '/'); $this->nodelist = array(); $tags = Template::getTags(); - while (count($tokens)) { - $token = array_shift($tokens); + for ($i = 0, $n = count($tokens); $i < $n; $i++) { + if ($tokens[$i] === null) { + continue; + } + $token = $tokens[$i]; + $tokens[$i] = null; if ($startRegexp->match($token)) { $this->whitespaceHandler($token); @@ -118,7 +122,7 @@ protected function whitespaceHandler($token) * This assumes that TAG_START is always '{%', and a whitespace control indicator * is exactly one character long, on a third position. */ - if (mb_substr($token, 2, 1) === Liquid::get('WHITESPACE_CONTROL')) { + if ($token[2] === Liquid::$config['WHITESPACE_CONTROL']) { $previousToken = end($this->nodelist); if (is_string($previousToken)) { // this can also be a tag or a variable $this->nodelist[key($this->nodelist)] = rtrim($previousToken); @@ -129,7 +133,7 @@ protected function whitespaceHandler($token) * This assumes that TAG_END is always '%}', and a whitespace control indicator * is exactly one character long, on a third position from the end. */ - self::$trimWhitespace = mb_substr($token, -3, 1) === Liquid::get('WHITESPACE_CONTROL'); + self::$trimWhitespace = $token[-3] === Liquid::$config['WHITESPACE_CONTROL']; } /** @@ -254,7 +258,7 @@ private function blockName() */ private function createVariable($token) { - $variableRegexp = new Regexp('/^' . Liquid::get('VARIABLE_START') . Liquid::get('WHITESPACE_CONTROL') . '?(.*?)' . Liquid::get('WHITESPACE_CONTROL') . '?' . Liquid::get('VARIABLE_END') . '$/s'); + $variableRegexp = new Regexp('/^' . Liquid::$config['VARIABLE_START'] . Liquid::$config['WHITESPACE_CONTROL'] . '?(.*?)' . Liquid::$config['WHITESPACE_CONTROL'] . '?' . Liquid::$config['VARIABLE_END'] . '$/s'); if ($variableRegexp->match($token)) { return new Variable($variableRegexp->matches[1]); } diff --git a/src/Liquid/Tag/TagRaw.php b/src/Liquid/Tag/TagRaw.php index 36310d73..f785c101 100644 --- a/src/Liquid/Tag/TagRaw.php +++ b/src/Liquid/Tag/TagRaw.php @@ -36,8 +36,12 @@ public function parse(array &$tokens) $this->nodelist = array(); - while (count($tokens)) { - $token = array_shift($tokens); + for ($i = 0, $n = count($tokens); $i < $n; $i++) { + if ($tokens[$i] === null) { + continue; + } + $token = $tokens[$i]; + $tokens[$i] = null; if ($tagRegexp->match($token)) { // If we found the proper block delimiter just end parsing here and let the outer block proceed