From c6ba92c43d664430d2471a0ce7ad727126199d80 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 18 May 2020 01:18:41 -0400 Subject: [PATCH] Refactor template engine code - Convert ITemplateEngine interface to abstract class - Remove dependency to App in FriendicaSmarty and FriendicaSmartyEngine - Normalize replaceMacro parameter behavior using Smarty template string prefixes - Remove obsolete FriendicaSmarty->parsed method - Update unused Smarty directory paths --- src/Core/Renderer.php | 33 ++++----- src/Render/FriendicaSmarty.php | 25 ++----- src/Render/FriendicaSmartyEngine.php | 74 +++++++++++-------- ...ITemplateEngine.php => TemplateEngine.php} | 35 ++++++++- tests/Util/AppMockTrait.php | 2 +- 5 files changed, 97 insertions(+), 72 deletions(-) rename src/Render/{ITemplateEngine.php => TemplateEngine.php} (50%) diff --git a/src/Core/Renderer.php b/src/Core/Renderer.php index 5ce47ad931..4dab3184c7 100644 --- a/src/Core/Renderer.php +++ b/src/Core/Renderer.php @@ -23,8 +23,7 @@ namespace Friendica\Core; use Exception; use Friendica\DI; -use Friendica\Render\FriendicaSmarty; -use Friendica\Render\ITemplateEngine; +use Friendica\Render\TemplateEngine; /** * This class handles Renderer related functions. @@ -66,25 +65,23 @@ class Renderer ]; /** - * This is our template processor + * Returns the rendered template output from the template string and variables * - * @param string|FriendicaSmarty $s The string requiring macro substitution or an instance of FriendicaSmarty - * @param array $vars Key value pairs (search => replace) - * - * @return string substituted string - * @throws Exception + * @param string $template + * @param array $vars + * @return string */ - public static function replaceMacros($s, array $vars = []) + public static function replaceMacros(string $template, array $vars) { $stamp1 = microtime(true); // pass $baseurl to all templates if it isn't set - $vars = array_merge(['$baseurl' => DI::baseUrl()->get()], $vars); + $vars = array_merge(['$baseurl' => DI::baseUrl()->get(), '$APP' => DI::app()], $vars); $t = self::getTemplateEngine(); try { - $output = $t->replaceMacros($s, $vars); + $output = $t->replaceMacros($template, $vars); } catch (Exception $e) { echo "
" . __FUNCTION__ . ": " . $e->getMessage() . "
"; exit(); @@ -98,19 +95,19 @@ class Renderer /** * Load a given template $s * - * @param string $s Template to load. + * @param string $file Template to load. * @param string $subDir Subdirectory (Optional) * * @return string template. * @throws Exception */ - public static function getMarkupTemplate($s, $subDir = '') + public static function getMarkupTemplate($file, $subDir = '') { $stamp1 = microtime(true); $t = self::getTemplateEngine(); try { - $template = $t->getTemplateFile($s, $subDir); + $template = $t->getTemplateFile($file, $subDir); } catch (Exception $e) { echo "
" . __FUNCTION__ . ": " . $e->getMessage() . "
"; exit(); @@ -130,8 +127,7 @@ class Renderer { $v = get_class_vars($class); - if (!empty($v['name'])) - { + if (!empty($v['name'])) { $name = $v['name']; self::$template_engines[$name] = $class; } else { @@ -146,7 +142,7 @@ class Renderer * If $name is not defined, return engine defined by theme, * or default * - * @return ITemplateEngine Template Engine instance + * @return TemplateEngine Template Engine instance */ public static function getTemplateEngine() { @@ -156,8 +152,9 @@ class Renderer if (isset(self::$template_engine_instance[$template_engine])) { return self::$template_engine_instance[$template_engine]; } else { + $a = DI::app(); $class = self::$template_engines[$template_engine]; - $obj = new $class; + $obj = new $class($a->getCurrentTheme(), $a->theme_info); self::$template_engine_instance[$template_engine] = $obj; return $obj; } diff --git a/src/Render/FriendicaSmarty.php b/src/Render/FriendicaSmarty.php index 2b06c88c91..5a1e7ed100 100644 --- a/src/Render/FriendicaSmarty.php +++ b/src/Render/FriendicaSmarty.php @@ -21,7 +21,6 @@ namespace Friendica\Render; -use Friendica\DI; use Smarty; use Friendica\Core\Renderer; @@ -34,26 +33,23 @@ class FriendicaSmarty extends Smarty public $filename; - function __construct() + function __construct(string $theme, array $theme_info) { parent::__construct(); - $a = DI::app(); - $theme = $a->getCurrentTheme(); - // setTemplateDir can be set to an array, which Smarty will parse in order. // The order is thus very important here $template_dirs = ['theme' => "view/theme/$theme/" . self::SMARTY3_TEMPLATE_FOLDER . "/"]; - if (!empty($a->theme_info['extends'])) { - $template_dirs = $template_dirs + ['extends' => "view/theme/" . $a->theme_info["extends"] . "/" . self::SMARTY3_TEMPLATE_FOLDER . "/"]; + if (!empty($theme_info['extends'])) { + $template_dirs = $template_dirs + ['extends' => "view/theme/" . $theme_info["extends"] . "/" . self::SMARTY3_TEMPLATE_FOLDER . "/"]; } $template_dirs = $template_dirs + ['base' => "view/" . self::SMARTY3_TEMPLATE_FOLDER . "/"]; $this->setTemplateDir($template_dirs); $this->setCompileDir('view/smarty3/compiled/'); - $this->setConfigDir('view/smarty3/config/'); - $this->setCacheDir('view/smarty3/cache/'); + $this->setConfigDir('view/smarty3/'); + $this->setCacheDir('view/smarty3/'); $this->left_delimiter = Renderer::getTemplateLeftDelimiter('smarty3'); $this->right_delimiter = Renderer::getTemplateRightDelimiter('smarty3'); @@ -63,13 +59,4 @@ class FriendicaSmarty extends Smarty // Don't report errors so verbosely $this->error_reporting = E_ALL & ~E_NOTICE; } - - function parsed($template = '') - { - if ($template) { - return $this->fetch('string:' . $template); - } - return $this->fetch('file:' . $this->filename); - } - -} \ No newline at end of file +} diff --git a/src/Render/FriendicaSmartyEngine.php b/src/Render/FriendicaSmartyEngine.php index 6984daa158..668b91ea5b 100644 --- a/src/Render/FriendicaSmartyEngine.php +++ b/src/Render/FriendicaSmartyEngine.php @@ -23,56 +23,69 @@ namespace Friendica\Render; use Friendica\Core\Hook; use Friendica\DI; +use Friendica\Util\Strings; /** - * Smarty implementation of the Friendica template engine interface + * Smarty implementation of the Friendica template abstraction */ -class FriendicaSmartyEngine implements ITemplateEngine +final class FriendicaSmartyEngine extends TemplateEngine { static $name = "smarty3"; - public function __construct() + const FILE_PREFIX = 'file:'; + const STRING_PREFIX = 'string:'; + + /** @var FriendicaSmarty */ + private $smarty; + + /** + * @inheritDoc + */ + public function __construct(string $theme, array $theme_info) { - if (!is_writable(__DIR__ . '/../../view/smarty3/')) { + $this->theme = $theme; + $this->theme_info = $theme_info; + $this->smarty = new FriendicaSmarty($this->theme, $this->theme_info); + + if (!is_writable(DI::basePath() . '/view/smarty3')) { echo "ERROR: folder view/smarty3/ must be writable by webserver."; exit(); } } - // ITemplateEngine interface - public function replaceMacros($s, $r) + /** + * @inheritDoc + */ + public function replaceMacros(string $template, array $vars) { - $template = ''; - if (gettype($s) === 'string') { - $template = $s; - $s = new FriendicaSmarty(); + if (!Strings::startsWith($template, self::FILE_PREFIX)) { + $template = self::STRING_PREFIX . $template; } - $r['$APP'] = DI::app(); - // "middleware": inject variables into templates $arr = [ - "template" => basename($s->filename), - "vars" => $r + 'template' => basename($this->smarty->filename), + 'vars' => $vars ]; - Hook::callAll("template_vars", $arr); - $r = $arr['vars']; + Hook::callAll('template_vars', $arr); + $vars = $arr['vars']; - foreach ($r as $key => $value) { + foreach ($vars as $key => $value) { if ($key[0] === '$') { $key = substr($key, 1); } - $s->assign($key, $value); + $this->smarty->assign($key, $value); } - return $s->parsed($template); + + return $this->smarty->fetch($template); } - public function getTemplateFile($file, $subDir = '') + /** + * @inheritDoc + */ + public function getTemplateFile(string $file, string $subDir = '') { - $a = DI::app(); - $template = new FriendicaSmarty(); - // Make sure $root ends with a slash / if ($subDir !== '' && substr($subDir, -1, 1) !== '/') { $subDir = $subDir . '/'; @@ -80,21 +93,20 @@ class FriendicaSmartyEngine implements ITemplateEngine $root = DI::basePath() . '/' . $subDir; - $theme = $a->getCurrentTheme(); - $filename = $template::SMARTY3_TEMPLATE_FOLDER . '/' . $file; + $filename = $this->smarty::SMARTY3_TEMPLATE_FOLDER . '/' . $file; - if (file_exists("{$root}view/theme/$theme/$filename")) { - $template_file = "{$root}view/theme/$theme/$filename"; - } elseif (!empty($a->theme_info['extends']) && file_exists(sprintf('%sview/theme/%s}/%s', $root, $a->theme_info['extends'], $filename))) { - $template_file = sprintf('%sview/theme/%s}/%s', $root, $a->theme_info['extends'], $filename); + if (file_exists("{$root}view/theme/$this->theme/$filename")) { + $template_file = "{$root}view/theme/$this->theme/$filename"; + } elseif (!empty($this->theme_info['extends']) && file_exists(sprintf('%sview/theme/%s}/%s', $root, $this->theme_info['extends'], $filename))) { + $template_file = sprintf('%sview/theme/%s}/%s', $root, $this->theme_info['extends'], $filename); } elseif (file_exists("{$root}/$filename")) { $template_file = "{$root}/$filename"; } else { $template_file = "{$root}view/$filename"; } - $template->filename = $template_file; + $this->smarty->filename = $template_file; - return $template; + return self::FILE_PREFIX . $template_file; } } diff --git a/src/Render/ITemplateEngine.php b/src/Render/TemplateEngine.php similarity index 50% rename from src/Render/ITemplateEngine.php rename to src/Render/TemplateEngine.php index b18af69f22..40fbfea6ca 100644 --- a/src/Render/ITemplateEngine.php +++ b/src/Render/TemplateEngine.php @@ -24,8 +24,37 @@ namespace Friendica\Render; /** * Interface for template engines */ -interface ITemplateEngine +abstract class TemplateEngine { - public function replaceMacros($s, $v); - public function getTemplateFile($file, $subDir = ''); + /** @var string */ + static $name; + + /** @var string */ + protected $theme; + /** @var array */ + protected $theme_info; + + /** + * @param string $theme The current theme name + * @param array $theme_info The current theme info array + */ + abstract public function __construct(string $theme, array $theme_info); + + /** + * Returns the rendered template output from the template string and variables + * + * @param string $template + * @param array $vars + * @return string + */ + abstract public function replaceMacros(string $template, array $vars); + + /** + * Returns the template string from a file path and an optional sub-directory from the project root + * + * @param string $file + * @param string $subDir + * @return mixed + */ + abstract public function getTemplateFile(string $file, string $subDir = ''); } diff --git a/tests/Util/AppMockTrait.php b/tests/Util/AppMockTrait.php index 1f6605390c..59e1b3f556 100644 --- a/tests/Util/AppMockTrait.php +++ b/tests/Util/AppMockTrait.php @@ -108,7 +108,7 @@ trait AppMockTrait ->andReturn($this->configMock); $this->app ->shouldReceive('getTemplateEngine') - ->andReturn(new FriendicaSmartyEngine()); + ->andReturn(new FriendicaSmartyEngine('frio', [])); $this->app ->shouldReceive('getCurrentTheme') ->andReturn('Smarty3');