['if (%s):', 'endif;'],
'unless' => ['if (!(%s)):', 'endif;'],
'for' => ['for (%s):', 'endfor;'],
'while' => ['while (%s):', 'endwhile;'],
'switch' => ['switch (%s):', 'endswitch;'],
'foreach' => [
'$__tmp_%uniq = %array ?? []; foreach ($__tmp_%uniq as %remainder):',
'endforeach;',
],
'forelse' => [
'$__tmp_%uniq = %array ?? []; if($__tmp_%uniq): foreach ($__tmp_%uniq as %remainder):',
'endforeach; else:',
'endif;',
],
'once' => [
"if (!isset(\$GLOBALS['tplv2_once']['%uniq'])):",
"endif; \$GLOBALS['tplv2_once']['%uniq'] = true;",
],
'isset' => ['if (isset(%s)):', 'endif;'],
'unset' => ['if (!isset(%s)):', 'endif;'],
'empty' => ['if (empty(%s)):', 'endif;'],
'admin' => ['if ($this->user->isAdmin()):', 'endif;'],
'auth' => ['if ($this->user->isMember()):', 'endif;'],
'member' => ['if ($this->user->isMember()):', 'endif;'],
'guest' => ['if (!$this->user->isMember()):', 'endif;'],
'desktop' => ['if (!$__Context->m):', 'endif;'],
'mobile' => ['if ($__Context->m):', 'endif;'],
'else' => ['else:'],
'elseif' => ['elseif (%s):'],
'case' => ['case %s:'],
'default' => ['default:'],
'continue' => ['continue;'],
'break' => ['break;'],
];
/**
* Convert template code into PHP.
*
* @param string $content
* @param Template $template
* @return string
*/
public function convert(string $content, Template $template): string
{
// Store template info in instance property.
$this->template = $template;
$this->source_type = preg_match('!^((?:m\.)?[a-z]+)/!', $template->relative_dirname, $match) ? $match[1] : null;
// Preprocessing.
$content = $this->_preprocess($content);
// Apply conversions.
$content = $this->_addContextSwitches($content);
$content = $this->_removeComments($content);
$content = $this->_convertRelativePaths($content);
$content = $this->_convertPHPSections($content);
$content = $this->_convertVerbatimSections($content);
$content = $this->_convertClassAliases($content);
$content = $this->_convertIncludes($content);
$content = $this->_convertAssets($content);
$content = $this->_convertLoopDirectives($content);
$content = $this->_convertInlineDirectives($content);
$content = $this->_convertMiscDirectives($content);
$content = $this->_convertEchoStatements($content);
// Postprocessing.
$content = $this->_postprocess($content);
return $content;
}
/**
* Preprocessing.
*
* @param string $content
* @return string
*/
protected function _preprocess(string $content): string
{
// Remove trailing whitespace.
$content = preg_replace('#[\x20\x09]+$#m', '', $content);
return $content;
}
/**
* Insert context switch points (HTML <-> JS).
*
* @param string $content
* @return string
*/
protected function _addContextSwitches(string $content): string
{
return preg_replace_callback('#(config->context = "HTML"; ?>' . $match[1];
}
else
{
return $match[1] . 'config->context = "JS"; ?>';
}
}, $content);
}
/**
* Remove comments that should not be visible in the output.
*
*
* {{-- Blade-style Comment --}}
*
* @param string $content
* @return string
*/
protected function _removeComments(string $content): string
{
return preg_replace([
'##',
'#\{\{--[^\n]+?--\}\}#',
], '', $content);
}
/**
* Convert relative paths to absolute paths.
*
* @param string $content
* @return string
*/
protected function _convertRelativePaths(string $content): string
{
// Get the base path for this template.
$basepath = \RX_BASEURL . $this->template->relative_dirname;
// Convert all src and srcset attributes.
$regexp = '#(<(?:img|audio|video|script|input|source|link)\s[^>]*)(src|srcset)="([^"]+)"#';
return preg_replace_callback($regexp, function($match) use ($basepath) {
if ($match[2] === 'src')
{
$src = trim($match[3]);
return $match[1] . sprintf('src="%s"', self::_isRelativePath($src) ? self::_convertRelativePath($src, $basepath) : $src);
}
else
{
$srcset = array_map('trim', explode(',', $match[3]));
$result = array_map(function($src) use($basepath) {
return self::_isRelativePath($src) ? self::_convertRelativePath($src, $basepath) : $src;
}, array_filter($srcset, function($src) {
return !empty($src);
}));
return $match[1] . sprintf('srcset="%s"', implode(', ', $result));
}
}, $content);
}
/**
* Convert PHP sections.
*
* Unlike v1, all variables in all PHP code belong to the same scope.
*
* Supported syntaxes:
*
* ... ?>
* {@ ... }
* @php ... @endphp
*
* @param string $content
* @return string
*/
protected function _convertPHPSections(string $content): string
{
$callback = function($match) {
$open = '';
return $open . self::_convertVariableScope($match[2]) . $close;
};
$content = preg_replace_callback('#(<\?php|<\?(?!=))(.+?)(\?>)#s', $callback, $content);
$content = preg_replace_callback('#(\{@)(.+?)(\})#s', $callback, $content);
$content = preg_replace_callback('#(?
*
* Blade-style syntax:
* @use('Rhymix\Modules\Foobar\Models\HelloWorld', 'HelloWorldModel')
*
* @param string $content
* @return string
*/
protected function _convertClassAliases(string $content): string
{
// Find all alias directives.
$regexp = '#^[\x09\x20]*(?: