config->version = 2; ?>'; private $baseurl; public function _before() { \Rhymix\Framework\Debug::disable(); $this->baseurl = '/' . basename(dirname(dirname(dirname(dirname(__DIR__))))) . '/'; } public function testVersionDetection() { // Extension is .html and config is explicitly declared $source = '' . "\n" . '
{{ RX_VERSION|noescape }}
'; $target = '
'; $this->assertEquals("\n" . $target, $this->_parse($source), false); $source = '@version(2)' . "\n" . '
@php func_get_args(); @endphp
'; $target = '
'; $this->assertEquals("\n" . $target, $this->_parse($source), false); // Extension is .blade.php and config is not declared $source = ''; $target = ' disabled="disabled">'; $this->assertEquals($target, $this->_parse($source)); // Extension is .blade.php but version is incorrectly declared: will be parsed as v1 $source = '@version(1)' . "\n" . ''; $target = ''; $this->assertStringContainsString($target, $this->_parse($source)); } public function testClassAliases() { // XE-style $source = '' . "\n" . '{@ $foo = TemplateHandler::getInstance()}'; $target = "\n" . 'foo = Rhymix\Framework\Template::getInstance() ?>'; $this->assertEquals($target, $this->_parse($source)); // Blade-style $source = "@use('Rhymix\Framework\Template', 'TemplateHandler')" . "\n" . '{@ $foo = new TemplateHandler()}'; $target = "\n" . 'foo = new Rhymix\Framework\Template() ?>'; $this->assertEquals($target, $this->_parse($source)); } public function testInclude() { // Basic usage $source = ''; $target = 'relative_dirname, "foobar", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Legacy 'target' attribute $source = ''; $target = 'normalizePath($this->relative_dirname . "subdir"), "foobar", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Conditional include $source = ''; $target = 'normalizePath($this->relative_dirname . "../up"), "foobar", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Conditional include with legacy 'cond' attribute $source = ''; $target = 'normalizePath($this->relative_dirname . "legacy"), "cond.statement.html", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Path relative to Rhymix installation directory $source = ''; $target = 'setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Unless $source = ''; $target = 'setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // With variables $source = ''; $target = 'relative_dirname, "foobar", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; $__tpl->addVars($__Context->vars); echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // With array literal passed as variables $source = ''; $target = 'relative_dirname, "foobar", "html"); $__tpl->setParent($this); if ($this->vars): $__tpl->setVars($this->vars); endif; $__tpl->addVars([\'foo\' => \'bar\']); echo $__tpl->compile(); ?>'; $this->assertEquals($target, $this->_parse($source)); // Blade-style @include $source = "@include ('foobar')"; $target = "_v2_include('include', 'foobar'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @include with variable in filename $source = "@include(\$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 = '_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 = "_v2_include('includeIf', 'dir/foobar', \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @includeWhen $source = "@includeWhen(\$foo->isBar(), '../../foobar.html', \$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 = "_v2_include('includeUnless', false, '^common/tpl/foobar.html', \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style @each $source = "@each('incl/eachtest', \$jobs, 'job')"; $target = 'foreach ($__vars as $__var):'; $this->assertStringContainsString($target, $this->_parse($source)); // Blade-style @each with fallback template $source = "@each('incl/eachtest', \$jobs, 'job', 'incl/empty')"; $target = 'echo $this->_v2_include("include"'; $this->assertStringContainsString($target, $this->_parse($source)); } public function testResourceLoading() { // CSS, SCSS, LESS with media and variables $source = ''; $target = "_v2_loadResource('assets/hello.scss', 'print', '', \$__Context->foo); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = ''; $target = "_v2_loadResource('../hello.css', 'screen and (max-width: 800px)', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // JS with type and index $source = ''; $target = "_v2_loadResource('assets/hello.js', 'head', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = ''; $target = "_v2_loadResource('assets/../otherdir/hello.js', 'body', '20', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // External script $source = ''; $target = "_v2_loadResource('//cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // External webfont $source = ''; $target = "_v2_loadResource('https://fonts.googleapis.com/css2?family=Roboto&display=swap', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // Path relative to Rhymix installation directory $source = ''; $target = "_v2_loadResource('^/common/js/foobar.js', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // JS plugin $source = ''; $target = "_v2_loadResource('^/common/js/plugins/ckeditor/', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // Lang file $source = ''; $target = "_v2_loadResource('^/modules/member/lang', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = ''; $target = "_v2_loadResource('^/modules/legacy_module/lang/lang.xml', '', '', []); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style SCSS with media and variables $source = "@load('assets/hello.scss', 'print', 0, \$vars)"; $target = "_v2_loadResource('assets/hello.scss', 'print', 0, \$__Context->vars); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = "@load ('../hello.css', 'screen')"; $target = "_v2_loadResource('../hello.css', 'screen'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style JS with type and index $source = "@load('assets/hello.js', 'body', 10)"; $target = "_v2_loadResource('assets/hello.js', 'body', 10); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = "@load ('assets/hello.js', 'head')"; $target = "_v2_loadResource('assets/hello.js', 'head'); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = "@load ('assets/hello.js')"; $target = "_v2_loadResource('assets/hello.js'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style external script $source = "@load ('//cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js')"; $target = "_v2_loadResource('//cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style external webfont $source = "@load('https://fonts.googleapis.com/css2?family=Roboto&display=swap')"; $target = "_v2_loadResource('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style path relative to Rhymix installation directory $source = '@load ("^/common/js/foobar.js")'; $target = '_v2_loadResource("^/common/js/foobar.js"); ?>'; $this->assertEquals($target, $this->_parse($source)); // Blade-style JS plugin $source = "@load('^/common/js/plugins/ckeditor/')"; $target = "_v2_loadResource('^/common/js/plugins/ckeditor/'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Blade-style lang file $source = "@load('^/modules/member/lang')"; $target = "_v2_loadResource('^/modules/member/lang'); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = '@load("^/modules/legacy_module/lang/lang.xml")'; $target = '_v2_loadResource("^/modules/legacy_module/lang/lang.xml"); ?>'; $this->assertEquals($target, $this->_parse($source)); // XE-style unload $source = ''; $target = ""; $this->assertEquals($target, $this->_parse($source)); $source = ''; $target = ""; $this->assertEquals($target, $this->_parse($source)); // Blade-style unload $source = "@unload('../script.js')"; $target = "convertPath('../script.js')); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = "@unload('^/common/js/jquery.js')"; $target = "convertPath('^/common/js/jquery.js')); ?>"; $this->assertEquals($target, $this->_parse($source)); } public function testContextSwitches() { // '; $target = ''; $this->assertEquals($target, $this->_parse($source, true, false)); // Inline script in link href $source = 'Hello'; $target = 'Hello'; $this->assertEquals($target, $this->_parse($source, true, false)); // Inline script in event handler $source = '
Hello
'; $target = '
Hello
'; $this->assertEquals($target, $this->_parse($source, true, false)); // pattern attribute in tag $source = ''; $target = ''; $this->assertEquals($target, $this->_parse($source, true, false)); $source = ''; $target = ''; $this->assertEquals($target, $this->_parse($source, true, false)); // '; $target = 'config->context = \'CSS\'; ?>> body { font-size: 16px; } config->context = \'HTML\'; ?>'; $this->assertEquals($target, $this->_parse($source, true, false)); // Inline style $source = '
'; $target = '
'; $this->assertEquals($target, $this->_parse($source, true, false)); } public function testEchoStatements() { // Basic usage of XE-style single braces $source = '{$var}'; $target = "config->context === 'HTML' ? htmlspecialchars(\$__Context->var ?? '', \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(\$__Context->var ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Single braces with space at beginning will not be parsed $source = '{ $var}'; $target = '{ $var}'; $this->assertEquals($target, $this->_parse($source)); // Single braces with space at end are OK $source = '{$var }'; $target = "config->context === 'HTML' ? htmlspecialchars(\$__Context->var ?? '', \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(\$__Context->var ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Correct handling of object property and array access $source = '{Context::getRequestVars()->$foo[$bar]}'; $target = "config->context === 'HTML' ? htmlspecialchars(Context::getRequestVars()->{\$__Context->foo}[\$__Context->bar], \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(Context::getRequestVars()->{\$__Context->foo}[\$__Context->bar]); ?>"; $this->assertEquals($target, $this->_parse($source)); // Basic usage of Blade-style double braces $source = '{{ $var }}'; $target = "config->context === 'HTML' ? htmlspecialchars(\$__Context->var ?? '', \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(\$__Context->var ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Double braces without spaces are OK $source = '{{$var}}'; $target = "config->context === 'HTML' ? htmlspecialchars(\$__Context->var ?? '', \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(\$__Context->var ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Literal double braces $source = '@{{ $var }}'; $target = '{{ $var }}'; $this->assertEquals($target, $this->_parse($source)); // Blade-style shortcut for unescaped output $source = '{!! Context::getInstance()->get($var) !!}'; $target = "get(\$__Context->var); ?>"; $this->assertEquals($target, $this->_parse($source)); // Callback function inside echo statement $source = '{{ implode("|", array_map(function(\$i) { return \$i + 1; }, $list) | noescape }}'; $target = "list); ?>"; $this->assertEquals($target, $this->_parse($source)); // Multiline echo statement $source = '{{ $foo ?' . "\n" . ' date($foo) :' . "\n" . ' toBool($bar) }}'; $target = "config->context === 'HTML' ? htmlspecialchars(\$__Context->foo ?\n date(\$__Context->foo) :\n toBool(\$__Context->bar), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(\$__Context->foo ? date(\$__Context->foo) : toBool(\$__Context->bar)); ?>"; $this->assertEquals($target, $this->_parse($source)); } public function testOutputFilters() { // Filters with no whitespace $source = '{$foo|upper|noescape}'; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Randomly distributed whitespace $source = '{$foo | upper |noescape }'; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Pipe character in filter option $source = "{\$foo|join:'|'|noescape}"; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Pipe character in filter option, escaped $source = "{\$foo|join:'foo\|bar'|noescape}"; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Pipe character in OR operator $source = '{$foo || $bar | noescape}'; $target = "foo || \$__Context->bar; ?>"; $this->assertEquals($target, $this->_parse($source)); // Autoescape $source = '{{ $foo|autoescape }}'; $target = "foo ?? '', \ENT_QUOTES, 'UTF-8', false); ?>"; $this->assertEquals($target, $this->_parse($source)); // Autolang (lang codes are not escaped, but escape_js() is applied in JS context) $source = '{{ $foo|autolang }}'; $target = "\w+$/', \$__Context->foo ?? '') ? (\$__Context->foo ?? '') : htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = '{{ $lang->cmd_hello_world }}'; $target = "config->context === 'HTML' ? (\$__Context->lang->cmd_hello_world) : \$this->_v2_escape(\$__Context->lang->cmd_hello_world); ?>"; $this->assertEquals($target, $this->_parse($source)); $source = '{{ $user_lang->user_lang_1234567890 }}'; $target = "config->context === 'HTML' ? (\$__Context->user_lang->user_lang_1234567890 ?? '') : \$this->_v2_escape(\$__Context->user_lang->user_lang_1234567890 ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Escape $source = '{{ $foo|escape }}'; $target = "foo ?? '', \ENT_QUOTES, 'UTF-8', true); ?>"; $this->assertEquals($target, $this->_parse($source)); // Noescape $source = '{{ $foo|escape|noescape }}'; $target = "foo ?? ''; ?>"; $this->assertEquals($target, $this->_parse($source)); // Escape for Javascript $source = '{{ $foo|js }}'; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Escape for Javascript (alternate name) $source = '{{ $foo|escapejs }}'; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // JSON using context-aware escape $source = '{{ $foo|json }}'; $target = implode('', [ "config->context === 'JS' ? ", "json_encode(\$__Context->foo ?? '', self::\$_json_options) : ", "htmlspecialchars(json_encode(\$__Context->foo ?? '', self::\$_json_options), \ENT_QUOTES, 'UTF-8', false); ?>", ]); $this->assertEquals($target, $this->_parse($source)); // strip_tags $source = '{{ $foo|strip }}'; $target = "config->context === 'HTML' ? htmlspecialchars(strip_tags(\$__Context->foo ?? ''), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(strip_tags(\$__Context->foo ?? '')); ?>"; $this->assertEquals($target, $this->_parse($source)); // strip_tags (alternate name) $source = '{{ $foo|upper|strip_tags }}'; $target = "config->context === 'HTML' ? htmlspecialchars(strip_tags(strtoupper(\$__Context->foo ?? '')), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(strip_tags(strtoupper(\$__Context->foo ?? ''))); ?>"; $this->assertEquals($target, $this->_parse($source)); // Trim $source = '{{ $foo|trim|noescape }}'; $target = "foo ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // URL encode $source = '{{ $foo|trim|urlencode }}'; $target = "config->context === 'HTML' ? htmlspecialchars(rawurlencode(trim(\$__Context->foo ?? '')), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(rawurlencode(trim(\$__Context->foo ?? ''))); ?>"; $this->assertEquals($target, $this->_parse($source)); // Lowercase $source = '{{ $foo|trim|lower }}'; $target = "config->context === 'HTML' ? htmlspecialchars(strtolower(trim(\$__Context->foo ?? '')), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(strtolower(trim(\$__Context->foo ?? ''))); ?>"; $this->assertEquals($target, $this->_parse($source)); // Uppercase $source = '{{ $foo|upper|escape }}'; $target = "foo ?? ''), \ENT_QUOTES, 'UTF-8', true); ?>"; $this->assertEquals($target, $this->_parse($source)); // nl2br() $source = '{{ $foo|nl2br }}'; $target = "foo ?? '', \ENT_QUOTES, 'UTF-8', false)); ?>"; $this->assertEquals($target, $this->_parse($source)); // nl2br() with gratuitous escape $source = '{{ $foo|nl2br|escape }}'; $target = "foo ?? '', \ENT_QUOTES, 'UTF-8', false)), \ENT_QUOTES, 'UTF-8', true); ?>"; $this->assertEquals($target, $this->_parse($source)); // Array join (default joiner is comma) $source = '{{ $foo|join }}'; $target = "config->context === 'HTML' ? htmlspecialchars(implode(', ', \$__Context->foo ?? ''), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(implode(', ', \$__Context->foo ?? '')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Array join (custom joiner) $source = '{{ $foo|join:"!@!" }}'; $target = "config->context === 'HTML' ? htmlspecialchars(implode(\"!@!\", \$__Context->foo ?? ''), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(implode(\"!@!\", \$__Context->foo ?? '')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Date conversion (default format) $source = '{{ $item->regdate | date }}'; $target = "config->context === 'HTML' ? htmlspecialchars(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), 'Y-m-d H:i:s'), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), 'Y-m-d H:i:s')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Date conversion (custom format) $source = "{{ \$item->regdate | date:'n/j H:i' }}"; $target = "config->context === 'HTML' ? htmlspecialchars(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), 'n/j H:i'), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), 'n/j H:i')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Date conversion (custom format in variable) $source = "{{ \$item->regdate | date:\$format }}"; $target = "config->context === 'HTML' ? htmlspecialchars(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), \$__Context->format), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(getDisplayDateTime(ztime(\$__Context->item->regdate ?? ''), \$__Context->format)); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number format $source = '{{ $num | format }}'; $target = "config->context === 'HTML' ? htmlspecialchars(number_format(\$__Context->num ?? ''), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(number_format(\$__Context->num ?? '')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number format (alternate name) $source = '{{ $num | number_format }}'; $target = "config->context === 'HTML' ? htmlspecialchars(number_format(\$__Context->num ?? ''), \ENT_QUOTES, 'UTF-8', false) : \$this->_v2_escape(number_format(\$__Context->num ?? '')); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number format (custom format) $source = '{{ $num | number_format:6 | noescape }}'; $target = "num ?? '', '6'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number format (custom format in variable) $source = '{{ $num | number_format:$digits | noescape }}'; $target = "num ?? '', \$__Context->digits); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number shorten $source = '{{ $num | shorten | noescape }}'; $target = "num ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number shorten (alternate name) $source = '{{ $num | number_shorten | noescape }}'; $target = "num ?? ''); ?>"; $this->assertEquals($target, $this->_parse($source)); // Number shorten (custom format) $source = '{{ $num | number_shorten:1 | noescape }}'; $target = "num ?? '', '1'); ?>"; $this->assertEquals($target, $this->_parse($source)); // Link $source = '{{ $foo|link }}'; $target = "foo ?? '', \ENT_QUOTES, 'UTF-8', false)) . '\">' . (htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)) . ''; ?>"; $this->assertEquals($target, $this->_parse($source)); // Link (custom link text) $source = '{{ $foo|link:"Hello World" }}'; $target = "' . (htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)) . ''; ?>"; $this->assertEquals($target, $this->_parse($source)); // Link (custom link text in variable) $source = '{{ $foo|link:$bar->baz[0] }}'; $target = "bar->baz[0], \ENT_QUOTES, 'UTF-8', false)) . '\">' . (htmlspecialchars(\$__Context->foo ?? '', \ENT_QUOTES, 'UTF-8', false)) . ''; ?>"; $this->assertEquals($target, $this->_parse($source)); } public function testVariableScopeConversion() { // Local variable $source = '{$foo|noescape}'; $target = "foo ?? ''; ?>"; $this->assertEquals($target, $this->_parse($source)); // Class and array keys $source = '{!! ClassName::getInstance()->$foo[$bar] !!}'; $target = "{\$__Context->foo}[\$__Context->bar]; ?>"; $this->assertEquals($target, $this->_parse($source)); // Superglobals $source = "{!! \$_SERVER['HTTP_USER_AGENT'] . \$GLOBALS[\$_GET['foo']] !!}"; $target = ""; $this->assertEquals($target, $this->_parse($source)); // $this $source = "{!! \$this->func(\$args) !!}"; $target = "func(\$__Context->args); ?>"; $this->assertEquals($target, $this->_parse($source)); // $lang $source = "{!! \$lang->cmd_yes !!}"; $target = "lang->cmd_yes; ?>"; $this->assertEquals($target, $this->_parse($source)); // $loop $source = "{!! \$loop->first !!}"; $target = "first ?? ''; ?>"; $this->assertEquals($target, $this->_parse($source)); // Escaped dollar sign $source = "{!! \\\$escaped !!}"; $target = ""; $this->assertEquals($target, $this->_parse($source)); // Escaped and unescaped variables used together in closure $source = "{!! (function(\\\$i) use(\$__Context) { return \\\$i * \$j; })(\$k); !!}"; $target = "j; })(\$__Context->k);; ?>"; $this->assertEquals($target, $this->_parse($source)); } public function testPathConversion() { // Image $source = 'foo'; $target = 'foo'; $this->assertEquals($target, $this->_parse($source)); //