name; ?>
-date("DateTime")->format("Y/m/d"); ?>
-content("Str")->excerpt(20)->get(); ?>
+ +clockFormat("Y/m/d"); ?>
+stExcerpt(20); ?>
-
- feed()->fetch()->get() as $row): ?>
+ fetch() as $row): ?>
-
- headline("Str")->ucfirst()->get(); ?>
+ headline->strUcFirst(); ?>
description; ?>
@@ -108,29 +109,29 @@ The file **/resources/partials/article.php** has already been binded under the i
```
#### Partial functionality
-The partials all arguments will automatically be converted to a object with alot of extedned functionallity. Here is some:
+The partials all arguments will automatically be converted to an object with a lot of extended functionality. Here is some:
```php
-echo $obj->date; // 2023-02-30 15:33:22
-echo $obj->date("DateTime")->format("Y/m/d"); // 2023/02/30
+echo $date; // 2023-02-30 15:33:22
+echo $date->clockFormat("Y/m/d"); // 2023/02/30
// Will strip all html tags, replace regular line breaks with "
" and uppercase the first letter -echo $obj->content("Str")->stripTags()->nl2br()->ucfirst()->get(); +echo $content->strStripTags()->strNl2br()->strUcfirst(); // Loop through an array -if($obj->feed()->count() > 0) foreach($obj->feed()->fetch()->get() as $row) { - echo $row->headline("Str")->ucfirst()->get()."
"; +if($feed->count() > 0) foreach($feed->fetch() as $row) { + echo $row->headline->strUcFirst() . "
"; } ``` #### Run the template engine -You can run the template engine later in a empty file, emitter or router dipatcher. It all depends on your setup. +You can run the template engine later in an empty file, emitter or router dispatcher. It all depends on your setup. ```php echo $swift->index()->get(); ``` #### Dynamic views -You can also create a dynamic view that will over write the current view if called. This is great for e.g. showing a 404 page. +You can also create a dynamic view that will overwrite the current view if called. This is great for e.g. showing a 404 page. In this example the current view which is **/resources/views/main** will be replaced with the view **/resources/views/httpStatus.php** when response status code is (403, 404 e.g.). @@ -148,10 +149,10 @@ $swift->findBind($response->getStatusCode()); ``` #### Easy DOM manipulation -Advance DOM creation and works great with stuff like the Meta data becouse you can later in change values and attributes in the controller. +Advance DOM creation and works great with stuff like the Metadata because you can later in change values and attributes in the controller. ```php -// Advance DOM creation and works great with stuff like the Meta data +// Advance DOM creation and works great with stuff like the Metadata $dom = MaplePHP\Output\Dom\Document::dom("head"); $dom->bindTag("title", "title")->setValue("Meta title"); $dom->bindTag("meta", "description")->attr("name", "Description")->attr("content", "Lorem ipsum dolor sit amet."); @@ -160,5 +161,4 @@ $dom->bindTag("meta", "description")->attr("name", "Description")->attr("content $head = MaplePHP\Output\Dom\Document::dom("head"); $head->getElement("title")->setValue("New meta title"); $head->getElement("description")->attr("content", "New meta description..."); - ``` diff --git a/SwiftRender.php b/SwiftRender.php index bee400f..2ee3a7a 100755 --- a/SwiftRender.php +++ b/SwiftRender.php @@ -9,9 +9,13 @@ namespace MaplePHP\Output; +use MaplePHP\Cache\Cache; +use MaplePHP\Cache\Handlers\FileSystemHandler; +use MaplePHP\Cache\Interfaces\CacheInterface; use MaplePHP\Container\Interfaces\ContainerInterface; use Exception; use BadMethodCallException; +use MaplePHP\DTO\Format\Arr; use MaplePHP\DTO\Format\Str; use MaplePHP\DTO\Traverse; use MaplePHP\Output\Dom\Document; @@ -21,12 +25,13 @@ class SwiftRender { public const VIEWS = ["index", "buffer", "view", "partial"]; - private $file; - private $ending = "php"; + private ?string $file = null; + private string $ending = "php"; private $buffer; private $index; private $view; private $partial; + private $partialKey; private $get; private $arg; // Default args private $dir; @@ -34,22 +39,24 @@ class SwiftRender private $bindView; private $bindArr; private $container; - //private $arguments; // Removed + private ?CacheInterface $cache = null; /** * Used to build output buffer; */ public function __construct() { + $path = realpath(__DIR__ . "/../../../storage/caches"); + $this->cache = new Cache(new FileSystemHandler($path)); } /** - * This will make shortcuts to container. - * @param string $m [description] - * @param string $a [description] + * Access the container inside a template file + * @param string $method + * @param array $args * @return ContainerInterface */ - public function __call($method, $args): ContainerInterface + public function __call(string $method, array $args): ContainerInterface { if (!is_null($this->container)) { if ($this->container->has($method)) { @@ -70,6 +77,16 @@ public function setContainer(ContainerInterface $container): void $this->container = $container; } + /** + * Set a PSR-6 cache instance for partial views, not required + * @param CacheInterface $cache + * @return void + */ + public function setCache(CacheInterface $cache): void + { + $this->cache = $cache; + } + /** * Get container instance * @return ContainerInterface @@ -136,7 +153,7 @@ public function setViewDir(string $dir): self } /** - * Set dir path to buffer files (ONLY used if you have binded a view and setViewDir is empty!) + * Set dir path to buffer files (ONLY used if you have bound a view and setViewDir is empty!) * @param string $dir Dir path * @return self */ @@ -160,9 +177,10 @@ public function setPartialDir(string $dir): self } /** - * Create a index view - * @param string|callable $file Filename + * Create an index view + * @param string|callable $file Filename * @return self + * @throws Exception */ public function setIndex(string|callable $file): self { @@ -175,7 +193,8 @@ public function setIndex(string|callable $file): self } /** - * Create a buffer/factory outout + * Create a buffer/factory output + * @param string $output * @return self */ public function setBuffer(string $output): self @@ -188,11 +207,12 @@ public function setBuffer(string $output): self /** * Create a Main view - * @param string|callable $file Filename - * @param array $args Pass on argummets to template + * @param string|callable $file Filename + * @param array $args Pass on arguments to template * @return self + * @throws Exception */ - public function setView(string|callable $file, array $args = array()): self + public function setView(string|callable $file, array $args = []): self { if (is_null($this->file) && is_string($file)) { $this->setFile($file); @@ -204,11 +224,12 @@ public function setView(string|callable $file, array $args = array()): self /** * Keep prev view immutable and create a new one. - * @param string $file [description] - * @param array $args [description] + * @param string $file [description] + * @param array $args [description] * @return self + * @throws Exception */ - public function withView(string $file, array $args = array()): self + public function withView(string $file, array $args = []): self { $inst = clone $this; $inst->setView($file, $args); @@ -217,19 +238,21 @@ public function withView(string $file, array $args = array()): self /** * Create a partial view - * @param string $file Filename - * @param string $partial partial/key - * @param array $args Args + * @param string $partial partial/key + * @param array $args Args + * @param int|false $cacheTime Cache view + * @return SwiftRender + * @throws Exception */ - public function setPartial(string $partial, array $args = array()): self + public function setPartial(string $partial, array $args = [], int|false $cacheTime = false): self { $keys = $this->selectPartial($partial, $file); if (is_null($this->file)) { $this->setFile($file); } - $func = $this->build($this->file, $args); + $func = $this->build($this->file, $args, $partial, $cacheTime); $this->partial[$keys[0]][$keys[1]] = $func; - + return $this; } @@ -240,11 +263,11 @@ public function hasPartial($partial): bool } /** - * Unset a setted partial - * @param string $key Partal key, example: ("sidebar", "breadcrumb") + * Unset a set partial + * @param string $key Partial key, example: ("sidebar", "breadcrumb") * @return void */ - public function unsetPartial($key): void + public function unsetPartial(string $key): void { if (isset($this->partial[$key])) { unset($this->partial[$key]); @@ -252,13 +275,14 @@ public function unsetPartial($key): void } /** - * Bind a View to a HTTP status reponse code - * @param array $statusArr Array with responese that will be bind to View (Array(404, 410...)) - * @param string $file View file - * @param array $args Pass on arguments/data to be used in view + * Bind a View to an HTTP status repose code + * @param string $key + * @param array $bindArr + * @param array $args Pass on arguments/data to be used in view * @return self + * @throws Exception */ - public function bindToBody(string $key, array $bindArr, array $args = array()): self + public function bindToBody(string $key, array $bindArr, array $args = []): self { $this->setFile($key); $func = $this->build($this->file, $args); @@ -268,7 +292,7 @@ public function bindToBody(string $key, array $bindArr, array $args = array()): } /** - * IF find in specified Bind Array the it will return the view + * IF find in specified Bind Array it will return the view * @param string|int $find * @param bool $overwrite * @return void @@ -338,25 +362,17 @@ public function partial(string $key): self /** * Return prepared view - * @param array|null $args merge args + * @param array|null $args merge args * @return string + * @throws Exception */ final public function get(?array $args = null): string { $this->buildView(); $output = $this->{$this->get}; - - // Will merge/replace arguments with current/deafult arguments - /* - if (is_array($args)) { - $args = array_merge($this->arguments, $args); - } - */ - ob_start(); if (!is_null($this->arg)) { if (is_array($output)) { - //if(isset($output[$this->arg])) $output[$this->arg]($args); if (isset($output[$this->arg])) { foreach ($output[$this->arg] as $part) { $part($args); @@ -365,7 +381,7 @@ final public function get(?array $args = null): string } } else { if (is_null($output)) { - throw new Exception("Expecting the \"{$this->get}\" view.", 1); + throw new Exception("Expecting the \"$this->get\" view.", 1); } else { $output($args); } @@ -377,15 +393,17 @@ final public function get(?array $args = null): string return (string)$output; } + /** + * Check for expecting view dependencies + * @return void + */ private function buildView(): void { if (!is_null($this->bindView)) { - $hasBuffer = $this->existAtGet("buffer"); - $hasIndex = $this->existAtGet("index"); - if ($hasBuffer) { + if ($this->existAtGet("buffer")) { $this->buffer = $this->bindView; } - if ($hasIndex) { + if ($this->existAtGet("index")) { $this->view = $this->bindView; } } @@ -394,15 +412,26 @@ private function buildView(): void /** * Build and Contain template and data until it's executed, * this means that code is prepared and will not take any extra memory if view would not be called. - * So you can if you want prepare a bunch of partial views and just call the the ones you want - * @param string|callable $file the filename - * @param array $args Pass arguments to template + * So you can if you want to prepare a bunch of partial views and just call the ones you want + * @param string|callable $file the filename + * @param array $args Pass arguments to template + * @param string|null $partialKey + * @param int|false $cacheTime * @return callable + * @throws Exception */ - private function build(string|callable $file, array $args = array()): callable - { - //$this->arguments = $args; - $func = function ($argsFromFile) use ($file, $args) { + private function build( + string|callable $file, + array $args = [], + ?string $partialKey = null, + int|false $cacheTime = false + ): callable { + + if(is_null($this->cache) && $cacheTime !== false) { + throw new Exception("Cache is not configured"); + } + + $func = function ($argsFromFile) use ($file, $args, $partialKey, $cacheTime) { if (($dir = ($this->dir[$this->get] ?? null)) || !is_null($dir)) { if (is_callable($file)) { $out = $file($this, $args); @@ -412,7 +441,7 @@ private function build(string|callable $file, array $args = array()): callable } else { $throwError = true; - $missingFiles = array(); + $missingFiles = []; $files = explode("|", $file); foreach($files as $file) { @@ -420,12 +449,13 @@ private function build(string|callable $file, array $args = array()): callable $file = substr($file, 1); $throwError = false; } - $filePath = realpath("{$dir}{$file}.{$this->ending}"); + $filePath = realpath("$dir$file.$this->ending"); if (is_file($filePath)) { if (is_array($argsFromFile) && count($argsFromFile) > 0) { $args = array_merge($args, $argsFromFile); } - $this->inclRouterFileData($filePath, Traverse::value($args), $args); + + $this->getOutput($filePath, $partialKey, $args, $cacheTime); break; } else { @@ -433,26 +463,68 @@ private function build(string|callable $file, array $args = array()): callable } } if($throwError && count($missingFiles) > 0) { - throw new Exception("Could not require template \"{$this->get}\" files: ".implode(", ", $missingFiles).".", 1); + throw new Exception("Could not require template \"$this->get\" files: ".implode(", ", $missingFiles).".", 1); } } } else { $file = (is_string($file)) ? $file : "[Callable]"; throw new Exception("You need to call @" . str_replace("_", "", (string)$this->get) . - "DIR and specify dir path for {$file}.", 1); + "DIR and specify dir path for $file.", 1); } + }; $this->file = null; return $func; } + + /** + * Get the view output + * @param string $filePath + * @param string|null $partialKey + * @param array $args + * @param int|false $cacheTime + * @return void + */ + private function getOutput( + string $filePath, + ?string $partialKey = null, + array $args = [], + int|false $cacheTime = false + ): void { + if($this->get == "partial" && !is_null($this->cache) && $cacheTime !== false) { + $partialKey = str_replace(["!", "/"], ["", "_"], $partialKey); + $updateTime = filemtime($filePath); + $cacheKey = $partialKey."-".$updateTime; + + if(!$this->cache->has($cacheKey)) { + $clear = Arr::value($this->cache->getAllKeys())->wildcardSearch("$partialKey-*")->get(); + if(count($clear)) { + $this->cache->deleteMultiple($clear); + } + + ob_start(); + $this->inclRouterFileData($filePath, Traverse::value($args), $args); + $out = ob_get_clean(); + $this->cache->set($cacheKey, $out, $cacheTime); + echo $out; + + } else { + echo $this->cache->get($cacheKey); + } + + } else { + $this->inclRouterFileData($filePath, Traverse::value($args), $args); + } + } + /** * Check if partial exists - * @param string $key + * @param string $key * @return bool */ - public function partialExists($key): bool + public function partialExists(string $key): bool { return isset($this->partial[$key]); } @@ -487,7 +559,10 @@ private function existAtGet(string $key): bool */ private function inclRouterFileData(string $filePath, object $obj, array $args): void { - //extract($args); + $extract = ($obj instanceof Traverse) ? $obj->toArray(function ($row) { + return Traverse::value($row); + }) : $args; + extract($extract); include($filePath); } @@ -496,20 +571,22 @@ public function dom(string $key): Document return Document::dom($key); } + /** + * @throws Exception + */ public function createTag(string $element, string $value, ?array $attr = null) { $inst = new Document(); $elem = $inst->create($element, $value); if (!($elem instanceof Element)) { - throw new \Exception("Could not find connection to Element instance", 1); + throw new Exception("Could not find connection to Element instance", 1); } - $elem = $elem->attrArr($attr); - return $elem; + return $elem->attrArr($attr); } public function isDoc($elem): bool { - return ($elem instanceof Document || $elem instanceof Element); + return ($elem instanceof Document); } public function isEl($elem): bool @@ -535,7 +612,7 @@ final protected function selectPartial(string $partialKey, ?string &$file = null $file = $key2 = $this->cleanKey($key2); $key = $partial[0]; if (isset($partial[2])) { - $key2 = "{$key}-{$partial[2]}"; + $key2 = "$key-$partial[2]"; } if (($pos = strpos($key2, "|")) !== false) { $key2 = substr($key2, 0, $pos); @@ -551,7 +628,7 @@ final protected function selectPartial(string $partialKey, ?string &$file = null */ final protected function cleanKey(string $key): string { - if(strpos($key, "!") === 0) { + if(str_starts_with($key, "!")) { return substr($key, 1); } return $key; diff --git a/composer.json b/composer.json index 89c29cb..a858600 100755 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "maplephp/swiftrender", "type": "library", + "version": "v1.1.0", "description": "PHP SwiftRender is a pure and highly portable PHP template library.", "keywords": ["output buffer", "template", "engine", "partials", "components", "views"], "homepage": "https://wazabii.se", @@ -17,7 +18,7 @@ ], "require": { "php": ">=8.0", - "maplephp/dto": "^1.0", + "maplephp/dto": "^2.0", "maplephp/container": "^1.0" }, "autoload": {