From e044e11c5f081479e3eed378c73f017395f56d44 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 18 Oct 2023 00:01:11 +0900 Subject: [PATCH] Convert include code into a method of Template class --- common/framework/Template.php | 57 ++++++++++++++ .../parsers/template/TemplateParser_v2.php | 75 ++----------------- tests/_data/template/v2example.compiled.html | 10 +-- .../parsers/TemplateParserV2Test.php | 49 ++---------- 4 files changed, 77 insertions(+), 114 deletions(-) diff --git a/common/framework/Template.php b/common/framework/Template.php index bc16be971..02a0e1357 100644 --- a/common/framework/Template.php +++ b/common/framework/Template.php @@ -457,6 +457,63 @@ class Template * =================== HELPER FUNCTIONS FOR TEMPLATE v2 =================== */ + /** + * Include another template from v2 @include directive. + * + * Blade has several variations of the @include directive, and we need + * access to the actual PHP args in order to process them accurately. + * So we do this in the Template class, not in the converter. + * + * @param ...$args + * @return string + */ + protected function _v2_include(...$args): string + { + // Set some basic information. + $directive = $args[0]; + $extension = $this->extension === 'blade.php' ? 'blade.php' : null; + $isConditional = in_array($directive, ['includeWhen', 'includeUnless']); + $basedir = $this->relative_dirname; + $cond = $isConditional ? $args[1] : null; + $path = $isConditional ? $args[2] : $args[1]; + $vars = $isConditional ? ($args[3] ?? null) : ($args[2] ?? null); + + // Handle paths relative to the Rhymix installation directory. + if (preg_match('#^\^/?(\w.+)$#s', $path, $match)) + { + $basedir = str_contains($match[1], '/') ? dirname($match[1]) : \RX_BASEDIR; + $path = basename($match[1]); + } + + // If the conditions are not met, return. + if ($isConditional && $directive === 'includeWhen' && !$cond) + { + return ''; + } + if ($isConditional && $directive === 'includeUnless' && $cond) + { + return ''; + } + + // Create a new instance of TemplateHandler. + $template = new self($basedir, $path, $extension); + + // If the directive is @includeIf and the template file does not exist, return. + if ($directive === 'includeIf' && !$template->exists()) + { + return ''; + } + + // Set variables. + if ($vars !== null) + { + $template->setVars($vars); + } + + // Compile and return. + return $template->compile(); + } + /** * Load a resource from v2 @load directive. * diff --git a/common/framework/parsers/template/TemplateParser_v2.php b/common/framework/parsers/template/TemplateParser_v2.php index 441ad113b..f47162f28 100644 --- a/common/framework/parsers/template/TemplateParser_v2.php +++ b/common/framework/parsers/template/TemplateParser_v2.php @@ -374,58 +374,9 @@ class TemplateParser_v2 $parentheses = self::_getRegexpForParentheses(2); $regexp = '#(?template->extension === 'blade.php' ? 'blade.php' : 'html'; - $dir = '$this->relative_dirname'; - if ($match[1] === 'include' || $match[1] === 'includeIf') - { - $path = preg_match('#^([\'"])([^\'"]+)\1#', $match[2], $m) ? $m[2] : ''; - if (preg_match('#^\^/?(\w.+)$#s', $path, $mm)) - { - $dir = '"' . escape_dqstr(str_contains($mm[1], '/') ? dirname($mm[1]) : '') . '"'; - $filename = basename($mm[1]); - $match[2] = preg_replace('#^([\'"])([^\'"]+)\1#', '$1' . $filename . '$1', $match[2]); - } - } - else - { - $path = preg_match('#^([^,]+,\s*)([\'"])([^\'"]+)\2#', $match[2], $m) ? $m[3] : ''; - if (preg_match('#^\^/?(\w.+)$#s', $path, $mm)) - { - $dir = '"' . escape_dqstr(str_contains($mm[1], '/') ? dirname($mm[1]) : '') . '"'; - $filename = basename($mm[1]); - $match[2] = preg_replace('#^([^,]+,\s*)([\'"])([^\'"]+)\2#', '$1$2' . $filename . '$2', $match[2]); - } - } - - // Generate an IIFE to create a new Template object and compile it. - if ($match[1] === 'include') - { - $tpl = 'setVars($__vars); ' ; - $tpl .= 'echo $__tpl->compile(); })(' . $dir . ', ' . $match[2] . '); ?>'; - } - elseif ($match[1] === 'includeIf') - { - $tpl = 'exists()) return; '; - $tpl .= 'if ($__vars) $__tpl->setVars($__vars); ' ; - $tpl .= 'echo $__tpl->compile(); })(' . $dir . ', ' . $match[2] . '); ?>'; - } - else - { - $tpl = 'setVars($__vars); ' ; - $tpl .= 'echo $__tpl->compile(); })("' . $match[1] . '", ' . $dir . ', ' . $match[2] . '); ?>'; - } - return self::_escapeVars($tpl); + $directive = trim($match[1]); + $args = self::_convertVariableScope(substr($match[2], 1, strlen($match[2]) - 2)); + return sprintf("_v2_include('%s', %s); ?>", $directive, $args); }, $content); // Handle the @each directive. @@ -435,23 +386,13 @@ class TemplateParser_v2 // Convert the path if necessary. $args = self::_convertVariableScope(substr($match[1], 1, strlen($match[1]) - 2)); - $extension = $this->template->extension === 'blade.php' ? 'blade.php' : 'html'; - $dir = '$this->relative_dirname'; - $path = preg_match('#^([\'"])([^\'"]+)\1#', $args, $m) ? $m[2] : ''; - if (preg_match('#^\^/?(\w.+)$#s', $path, $mm)) - { - $dir = '"' . escape_dqstr(str_contains($mm[1], '/') ? dirname($mm[1]) : '') . '"'; - $filename = basename($mm[1]); - $args = preg_replace('#^([\'"])([^\'"]+)\1#', '$1' . $filename . '$1', $args); - } - // Generate the IIFE code. - $tpl = 'setVars([(string)$__varname => $__var]); ' ; - $tpl .= 'echo $__tpl->compile(); endforeach; })(' . $dir . ', ' . $args . '); ?>'; + $tpl .= 'echo $this->_v2_include("include", $__filename, [(string)$__varname => $__var]); '; + $tpl .= 'endforeach; })(' . $args . '); ?>'; return self::_escapeVars($tpl); }, $content); diff --git a/tests/_data/template/v2example.compiled.html b/tests/_data/template/v2example.compiled.html index 303b7e715..28d4b82eb 100644 --- a/tests/_data/template/v2example.compiled.html +++ b/tests/_data/template/v2example.compiled.html @@ -1,7 +1,7 @@ config->version = 2; ?> -
setVars($__vars); echo $__tpl->compile(); })("common/tpl", 'refresh.html'); ?>
+
_v2_include('include', '^/common/tpl/refresh.html'); ?>
_v2_loadResource('^/common/js/plugins/ckeditor/'); ?>
_v2_loadResource('css/style.scss', 'print', '', []); ?> @@ -29,18 +29,18 @@ -_v2_initLoopVar("64b3371f38fea1", $__tmp_64b3371f38fea1); foreach ($__tmp_64b3371f38fea1 as $__Context->k => $__Context->val): ?> +_v2_initLoopVar("RANDOM_LOOP_ID", $__tmp_RANDOM_LOOP_ID); foreach ($__tmp_RANDOM_LOOP_ID as $__Context->k => $__Context->val): ?>
nosuchvar)): ?> unit tests are cool k >= 2): ?>class="val ?? '', \ENT_QUOTES, 'UTF-8', false); ?>">
-_v2_incrLoopVar($__loop_64b3371f38fea1); endforeach; $this->_v2_removeLoopVar($__loop_64b3371f38fea1); unset($__loop_64b3371f38fea1); else: ?>
Nothing here...
+_v2_incrLoopVar($__loop_RANDOM_LOOP_ID); endforeach; $this->_v2_removeLoopVar($__loop_RANDOM_LOOP_ID); unset($__loop_RANDOM_LOOP_ID); else: ?>
Nothing here...
_fragments[$__last_fragment_name] = ob_get_flush(); ?> -setVars([(string)$__varname => $__var]); echo $__tpl->compile(); endforeach; })($this->relative_dirname, 'incl/eachtest', $__Context->bar, 'var'); ?> -setVars([(string)$__varname => $__var]); echo $__tpl->compile(); endforeach; })($this->relative_dirname, 'incl/eachtest', [], 'anything', 'incl/empty'); ?> +_v2_include("include", $__filename, [(string)$__varname => $__var]); endforeach; })('incl/eachtest', $__Context->bar, 'var'); ?> +_v2_include("include", $__filename, [(string)$__varname => $__var]); endforeach; })('incl/eachtest', [], 'anything', 'incl/empty'); ?> m): ?>

The full class name is , really.

diff --git a/tests/unit/framework/parsers/TemplateParserV2Test.php b/tests/unit/framework/parsers/TemplateParserV2Test.php index f48e6416d..f0f6c6ae9 100644 --- a/tests/unit/framework/parsers/TemplateParserV2Test.php +++ b/tests/unit/framework/parsers/TemplateParserV2Test.php @@ -90,67 +90,32 @@ class TemplateParserV2Test extends \Codeception\Test\Unit // Blade-style @include $source = "@include ('foobar')"; - $target = implode(' ', [ - 'setVars($__vars);', - 'echo $__tpl->compile(); })($this->relative_dirname, \'foobar\'); ?>' - ]); + $target = "_v2_include('include', 'foobar'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @include with variable in filename $source = "@include(\$var)"; - $target = implode(' ', [ - 'setVars($__vars);', - 'echo $__tpl->compile(); })($this->relative_dirname, $__Context->var); ?>' - ]); + $target = "_v2_include('include', \$__Context->var); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @include with path relative to Rhymix installation directory $source = '@include ("^/common/js/plugins/foobar/baz.blade.php")'; - $target = implode(' ', [ - 'setVars($__vars);', - 'echo $__tpl->compile(); })("common/js/plugins/foobar", "baz.blade.php"); ?>' - ]); + $target = '_v2_include(\'include\', "^/common/js/plugins/foobar/baz.blade.php"); ?>'; $this->assertEquals($target, $this->_parse($source)); // Blade-style @includeIf with variables $source = "@includeIf('dir/foobar', \$vars)"; - $target = implode(' ', [ - 'exists()) return;', - 'if ($__vars) $__tpl->setVars($__vars);', - 'echo $__tpl->compile(); })($this->relative_dirname, \'dir/foobar\', $__Context->vars); ?>' - ]); + $target = "_v2_include('includeIf', 'dir/foobar', \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @includeWhen $source = "@includeWhen(\$foo->isBar(), '../../foobar.html', \$vars)"; - $target = implode(' ', [ - 'setVars($__vars);', - 'echo $__tpl->compile(); })("includeWhen", $this->relative_dirname, $__Context->foo->isBar(), \'../../foobar.html\', $__Context->vars); ?>' - ]); + $target = "_v2_include('includeWhen', \$__Context->foo->isBar(), '../../foobar.html', \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @includeUnless with path relative to Rhymix installation directory $source = "@includeUnless (false, '^common/tpl/foobar.html', \$vars)"; - $target = implode(' ', [ - 'setVars($__vars);', - 'echo $__tpl->compile(); })("includeUnless", "common/tpl", false, \'foobar.html\', $__Context->vars); ?>' - ]); + $target = "_v2_include('includeUnless', false, '^common/tpl/foobar.html', \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @each @@ -160,7 +125,7 @@ class TemplateParserV2Test extends \Codeception\Test\Unit // Blade-style @each with fallback template $source = "@each('incl/eachtest', \$jobs, 'job', 'incl/empty')"; - $target = 'if ($__empty): $__path = $__empty;'; + $target = 'echo $this->_v2_include("include"'; $this->assertStringContainsString($target, $this->_parse($source)); }