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 = '';
$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 = '
';
$target = '
';
$this->assertEquals($target, $this->_parse($source));
//