From f10310cba06ca63f3ab3013b40dfd831c6990245 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Sun, 2 Apr 2023 07:18:01 +0300 Subject: [PATCH 1/2] Added Support for Loading Template From Current Directory --- tests/webfiori/test/ui/LoadTemplateTest.php | 27 ++++++++++- tests/webfiori/test/ui/sub-component.php | 3 ++ tests/webfiori/test/ui/template.php | 18 ++++++++ webfiori/ui/HTMLNode.php | 34 ++++++++++++-- webfiori/ui/TemplateCompiler.php | 50 +++++++++++++++++++-- 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 tests/webfiori/test/ui/sub-component.php create mode 100644 tests/webfiori/test/ui/template.php diff --git a/tests/webfiori/test/ui/LoadTemplateTest.php b/tests/webfiori/test/ui/LoadTemplateTest.php index 630602b..8f3fa15 100644 --- a/tests/webfiori/test/ui/LoadTemplateTest.php +++ b/tests/webfiori/test/ui/LoadTemplateTest.php @@ -17,7 +17,8 @@ class LoadTemplateTest extends TestCase { * @test */ public function test00() { - $this->expectException('Exception'); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Empty template path'); $compiler = new TemplateCompiler(''); } /** @@ -112,6 +113,28 @@ public function test07() { $node = $compiler->getCompiled(); $this->assertEquals("
\n No posts.\n
", $node->toHTML()); } + /** + * @test + */ + public function test08() { + $compiler = new TemplateCompiler('template.php', [ + 'message' => 'Good Job!', + 'posts' => [ + 'One', + 'Two', + 'Three' + ]]); + $this->assertEquals("
" + . "" + . "
\n" + . " Good Job!" + . "
" + . "
", $compiler->getCompiled()->toHTML()); + } /** * @test */ @@ -179,7 +202,7 @@ public function testHeadTemplate04() { */ public function testAddChildFromTemplate00() { $node = new HTMLNode(); - $node->component(self::TEST_TEMPLATES_PATH.'component-00.html', [ + $node->include(self::TEST_TEMPLATES_PATH.'component-00.html', [ 'base' => 'https://example.com', 'home-label' => 'Home Page', 'about-label' => 'About Us', diff --git a/tests/webfiori/test/ui/sub-component.php b/tests/webfiori/test/ui/sub-component.php new file mode 100644 index 0000000..1b24761 --- /dev/null +++ b/tests/webfiori/test/ui/sub-component.php @@ -0,0 +1,3 @@ +
+ +
diff --git a/tests/webfiori/test/ui/template.php b/tests/webfiori/test/ui/template.php new file mode 100644 index 0000000..18ccbec --- /dev/null +++ b/tests/webfiori/test/ui/template.php @@ -0,0 +1,18 @@ +
+ + + + +
diff --git a/webfiori/ui/HTMLNode.php b/webfiori/ui/HTMLNode.php index e92955a..dcf348f 100644 --- a/webfiori/ui/HTMLNode.php +++ b/webfiori/ui/HTMLNode.php @@ -676,6 +676,33 @@ public function codeSnippet(string $title, $code, array $attributes = []) : HTML public function comment(string $txt) { return $this->addCommentNode($txt); } + /** + * Loads HTML-like or PHP component and make it a child of current node. + * + * This method can be used to load any component that uses HTML syntax + * into an object and make it a child of the instance at which the method is + * called in. If the component file contains more than one node as a root note, + * all nodes will be added as children. + * + * @param string $path The location of the file that + * will have the HTML component. + * + * @param array $values An array that contains slots values or variables + * to be passed to PHP template. A slot in + * the component is a string which is enclosed between two curly braces (such as {{name}}). + * This array must be associative. The indices of the array are slots names + * and values of the indices are slots values. The values of the slots can be + * also sub-array that contains more values. For example, if we + * have a slot with the name {{ user-name }}, then the array can have the + * index 'user-name' with the value of the slot. + * + * @throws TemplateNotFoundException If the file that the component is + * loaded from does not exist. + * + */ + public function include(string $path, array $values = []) { + $this->component($path, $values); + } /** * Loads HTML-like component and make it a child of current node. * @@ -698,9 +725,9 @@ public function comment(string $txt) { * @throws TemplateNotFoundException If the file that the component is * loaded from does not exist. * - * + * @deprecated Use HTMLNode::include() */ - public function component(string $path, array $slotsValues) { + public function component(string $path, array $slotsValues = []) { $loaded = self::fromFile($path, $slotsValues); if (gettype($loaded) == 'array') { @@ -719,7 +746,6 @@ public function component(string $path, array $slotsValues) { * * @return int The number of child nodes attached to the node. * - * */ public function count() : int { return $this->childrenCount(); @@ -825,7 +851,7 @@ public function form(array $attributes = []) : HTMLNode { * * @throws TemplateNotFoundException */ - public static function fromFile(string $absPath, array $slotsOrVars) { + public static function fromFile(string $absPath, array $slotsOrVars = []) { $compiler = new TemplateCompiler($absPath, $slotsOrVars); return $compiler->getCompiled(); } diff --git a/webfiori/ui/TemplateCompiler.php b/webfiori/ui/TemplateCompiler.php index feeac64..bc51e41 100644 --- a/webfiori/ui/TemplateCompiler.php +++ b/webfiori/ui/TemplateCompiler.php @@ -10,6 +10,7 @@ */ namespace webfiori\ui; +use InvalidArgumentException; use webfiori\collections\Queue; use webfiori\ui\exceptions\InvalidNodeNameException; use webfiori\ui\exceptions\TemplateNotFoundException; @@ -50,15 +51,56 @@ class TemplateCompiler { * @throws InvalidNodeNameException */ public function __construct(string $templatePath, array $vars = []) { - if (!file_exists($templatePath)) { - throw new TemplateNotFoundException('No file was found at "'.$templatePath.'".'); + $trimmedPath = trim($templatePath); + if (strlen($trimmedPath) == 0) { + throw new InvalidArgumentException('Empty template path'); } - $extArr = explode('.', $templatePath); + if (!file_exists($trimmedPath)) { + $possibleLocations = self::getCallingFilesPaths(); + + foreach ($possibleLocations as $dir) { + if (file_exists($dir.$trimmedPath)) { + $trimmedPath = $dir.$trimmedPath; + break; + } + } + if (!file_exists($trimmedPath)) { + throw new TemplateNotFoundException('No file was found at "'.$trimmedPath.'".'); + } + } + $extArr = explode('.', $trimmedPath); $this->tType = strtolower($extArr[count($extArr) - 1]); - $this->path = $templatePath; + $this->path = $trimmedPath; $this->rawOutput = ''; $this->compile($vars); } + /** + * Returns an array that contains directories names of the calling files. + * + * This method is used to extract the pathes of files at which they called + * this method. + * + * @return array An array that contains directories names of the calling files. + */ + public static function getCallingFilesPaths() : array { + $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); + $retVal = []; + foreach ($debugTrace as $traceEntry) { + if (isset($traceEntry['file'])) { + $file = $traceEntry['file']; + $split = explode(DIRECTORY_SEPARATOR, $file); + $withoutFile = array_diff($split, [$split[count($split) - 1]]); + $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); + if (strlen($dir) != 0) { + $dir .= DIRECTORY_SEPARATOR; + if (!in_array($dir, $retVal)) { + $retVal[] = $dir; + } + } + } + } + return $retVal; + } /** * Compile the template and return its representation as an object. From 3ca6100896cf47ec22a98a443025d19b89f39247 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Sun, 2 Apr 2023 07:23:05 +0300 Subject: [PATCH 2/2] Run CS Fixer --- webfiori/ui/CodeSnippet.php | 13 +++--- webfiori/ui/HTMLNode.php | 59 ++++++++++++------------ webfiori/ui/HeadNode.php | 78 ++++++++++++++++---------------- webfiori/ui/Input.php | 16 +++---- webfiori/ui/TemplateCompiler.php | 64 ++++++++++++++------------ 5 files changed, 115 insertions(+), 115 deletions(-) diff --git a/webfiori/ui/CodeSnippet.php b/webfiori/ui/CodeSnippet.php index 581fa96..f81db5e 100644 --- a/webfiori/ui/CodeSnippet.php +++ b/webfiori/ui/CodeSnippet.php @@ -37,7 +37,6 @@ * @version 1.0.3 */ class CodeSnippet extends HTMLNode { - private $code; private $codeDisplay; @@ -250,12 +249,12 @@ private function addLineHelper() { $span = new HTMLNode('span'); $span->setClassName('line-number'); $span->setAttribute('style', - 'font-weight: bold;' - .'display: block;' - .'font-family: monospace;' - .'border-right: 1px dotted white;' - .'padding-right: 4px;' - .'color: #378e80;'); + 'font-weight: bold;' + .'display: block;' + .'font-family: monospace;' + .'border-right: 1px dotted white;' + .'padding-right: 4px;' + .'color: #378e80;'); $span->addTextNode($this->currentLineNum); $this->currentLineNum++; $this->lineNumsNode->addChild($span); diff --git a/webfiori/ui/HTMLNode.php b/webfiori/ui/HTMLNode.php index dcf348f..ef064e8 100644 --- a/webfiori/ui/HTMLNode.php +++ b/webfiori/ui/HTMLNode.php @@ -676,33 +676,6 @@ public function codeSnippet(string $title, $code, array $attributes = []) : HTML public function comment(string $txt) { return $this->addCommentNode($txt); } - /** - * Loads HTML-like or PHP component and make it a child of current node. - * - * This method can be used to load any component that uses HTML syntax - * into an object and make it a child of the instance at which the method is - * called in. If the component file contains more than one node as a root note, - * all nodes will be added as children. - * - * @param string $path The location of the file that - * will have the HTML component. - * - * @param array $values An array that contains slots values or variables - * to be passed to PHP template. A slot in - * the component is a string which is enclosed between two curly braces (such as {{name}}). - * This array must be associative. The indices of the array are slots names - * and values of the indices are slots values. The values of the slots can be - * also sub-array that contains more values. For example, if we - * have a slot with the name {{ user-name }}, then the array can have the - * index 'user-name' with the value of the slot. - * - * @throws TemplateNotFoundException If the file that the component is - * loaded from does not exist. - * - */ - public function include(string $path, array $values = []) { - $this->component($path, $values); - } /** * Loads HTML-like component and make it a child of current node. * @@ -853,6 +826,7 @@ public function form(array $attributes = []) : HTMLNode { */ public static function fromFile(string $absPath, array $slotsOrVars = []) { $compiler = new TemplateCompiler($absPath, $slotsOrVars); + return $compiler->getCompiled(); } @@ -1245,6 +1219,33 @@ public function img(array $attributes = []) : HTMLNode { return $this->addChild($img); } + /** + * Loads HTML-like or PHP component and make it a child of current node. + * + * This method can be used to load any component that uses HTML syntax + * into an object and make it a child of the instance at which the method is + * called in. If the component file contains more than one node as a root note, + * all nodes will be added as children. + * + * @param string $path The location of the file that + * will have the HTML component. + * + * @param array $values An array that contains slots values or variables + * to be passed to PHP template. A slot in + * the component is a string which is enclosed between two curly braces (such as {{name}}). + * This array must be associative. The indices of the array are slots names + * and values of the indices are slots values. The values of the slots can be + * also sub-array that contains more values. For example, if we + * have a slot with the name {{ user-name }}, then the array can have the + * index 'user-name' with the value of the slot. + * + * @throws TemplateNotFoundException If the file that the component is + * loaded from does not exist. + * + */ + public function include(string $path, array $values = []) { + $this->component($path, $values); + } /** * Adds new input (<input>, <select> or <textarea>) * element as a child to the body of the node. @@ -1976,7 +1977,6 @@ public function setNodeName(string $name) : bool { * */ public function setStyle(array $cssStyles, bool $override = false) : HTMLNode { - if (!$override) { $styleArr = $this->getStyle(); } else { @@ -2690,8 +2690,7 @@ private function validateAttrNameHelper(string $name) : bool { * * */ - private function validateFormattingOptions(array $FO): array - { + private function validateFormattingOptions(array $FO): array { $defaultFormat = self::DEFAULT_CODE_FORMAT; foreach ($defaultFormat as $key => $value) { diff --git a/webfiori/ui/HeadNode.php b/webfiori/ui/HeadNode.php index f42fc0b..d52be1c 100644 --- a/webfiori/ui/HeadNode.php +++ b/webfiori/ui/HeadNode.php @@ -215,27 +215,6 @@ public function addChild($node, $attrsOrChain = [], bool $chainOnParent = true) } - return $this; - } - /** - * Add multiple CSS resources files. - * - * @param array $files An array that holds paths to CSS files. This also - * can be an associative array. In this case, the indices are paths to files - * and the value of each index is a sub associative array of attributes. - * - * @return HeadNode The method will return the instance at which the method - * is called on. - */ - public function addCSSFiles(array $files) : HeadNode { - foreach ($files as $index => $options) { - if (gettype($index) == 'integer') { - $this->addCSS($options); - } else { - $this->addCSS($index, $options); - } - } - return $this; } /** @@ -306,24 +285,24 @@ public function addCSS(string $href, array $otherAttrs = []) : HeadNode { return $this; } /** - * Add multiple JS resources files. + * Add multiple CSS resources files. * - * @param array $files An array that holds paths to JS files. This also + * @param array $files An array that holds paths to CSS files. This also * can be an associative array. In this case, the indices are paths to files * and the value of each index is a sub associative array of attributes. * * @return HeadNode The method will return the instance at which the method * is called on. */ - public function addJSFiles(array $files) : HeadNode { + public function addCSSFiles(array $files) : HeadNode { foreach ($files as $index => $options) { if (gettype($index) == 'integer') { - $this->addJs($options); + $this->addCSS($options); } else { - $this->addJs($index, $options); + $this->addCSS($index, $options); } } - + return $this; } /** @@ -396,6 +375,27 @@ public function addJs(string $loc, array $otherAttrs = []) : HeadNode { return $this; } + /** + * Add multiple JS resources files. + * + * @param array $files An array that holds paths to JS files. This also + * can be an associative array. In this case, the indices are paths to files + * and the value of each index is a sub associative array of attributes. + * + * @return HeadNode The method will return the instance at which the method + * is called on. + */ + public function addJSFiles(array $files) : HeadNode { + foreach ($files as $index => $options) { + if (gettype($index) == 'integer') { + $this->addJs($options); + } else { + $this->addJs($index, $options); + } + } + + return $this; + } /** * Adds new 'link' node. * Note that if the 'rel' attribute value is 'canonical' or 'alternate', no node will be @@ -451,18 +451,6 @@ public function addLink(string $rel, string $href, array $otherAttrs = []) : Hea return $this; } - /** - * Adds a set of meta tags. - * - * @param array $tags An associative array. The indices of the array - * are the values of the attribute 'name' and the value of the index is - * the value of the attribute 'content'. - */ - public function addMetaTags(array $tags) { - foreach ($tags as $name => $content) { - $this->addMeta($name, $content); - } - } /** * Adds new meta tag. * @@ -514,6 +502,18 @@ public function addMeta(string $name, string $content, bool $override = false) : return $this; } + /** + * Adds a set of meta tags. + * + * @param array $tags An associative array. The indices of the array + * are the values of the attribute 'name' and the value of the index is + * the value of the attribute 'content'. + */ + public function addMetaTags(array $tags) { + foreach ($tags as $name => $content) { + $this->addMeta($name, $content); + } + } /** * Returns a linked list of all alternate nodes that was added to the header. * diff --git a/webfiori/ui/Input.php b/webfiori/ui/Input.php index 2af9d40..a7427c8 100644 --- a/webfiori/ui/Input.php +++ b/webfiori/ui/Input.php @@ -163,8 +163,7 @@ public function addChild($node, $attrsOrChain = [], bool $chainOnParent = true) * * @since 1.0.1 */ - public function addOption(array $options = []): Input - { + public function addOption(array $options = []): Input { if ($this->getNodeName() == 'select' && gettype($options) == 'array' && isset($options['value']) && isset($options['label'])) { $option = new HTMLNode('option'); $option->setAttribute('value', $options['value']); @@ -202,8 +201,7 @@ public function addOption(array $options = []): Input * * @since 1.0.1 */ - public function addOptions(array $arrayOfOpt): Input - { + public function addOptions(array $arrayOfOpt): Input { if (gettype($arrayOfOpt) == 'array') { foreach ($arrayOfOpt as $value => $lblOrOptions) { if (gettype($lblOrOptions) == 'array') { @@ -249,8 +247,7 @@ public function addOptions(array $arrayOfOpt): Input * is called on. * @since 1.0.1 */ - public function addOptionsGroup(array $optionsGroupArr): Input - { + public function addOptionsGroup(array $optionsGroupArr): Input { if ($this->getNodeName() == 'select' && gettype($optionsGroupArr) == 'array' && isset($optionsGroupArr['label']) && isset($optionsGroupArr['options'])) { $optGroup = new HTMLNode('optgroup'); $optGroup->setAttribute('label', $optionsGroupArr['label']); @@ -382,8 +379,7 @@ public function setMin(int $min) : Input { * * @since 1.0 */ - public function setMinLength(int $length): Input - { + public function setMinLength(int $length): Input { if ($length >= 0) { $iType = $this->getType(); @@ -426,8 +422,7 @@ public function setNodeName(string $name) : bool { * @return Input The method will return the instance at which the method * is called on. */ - public function setPlaceholder(string $text = null): Input - { + public function setPlaceholder(string $text = null): Input { if ($text !== null) { $iType = $this->getType(); @@ -491,6 +486,7 @@ private function addOptionsToGroupHelper($optionsGroupArr, $optGroup) { foreach ($optionsGroupArr['options'] as $value => $labelOrOptions) { $o = new HTMLNode('option'); $o->setAttribute('value', $value); + if (gettype($labelOrOptions) == 'array' && isset($labelOrOptions['label'])) { $o->addTextNode($labelOrOptions['label'],false); diff --git a/webfiori/ui/TemplateCompiler.php b/webfiori/ui/TemplateCompiler.php index bc51e41..6250a93 100644 --- a/webfiori/ui/TemplateCompiler.php +++ b/webfiori/ui/TemplateCompiler.php @@ -52,18 +52,21 @@ class TemplateCompiler { */ public function __construct(string $templatePath, array $vars = []) { $trimmedPath = trim($templatePath); + if (strlen($trimmedPath) == 0) { throw new InvalidArgumentException('Empty template path'); } + if (!file_exists($trimmedPath)) { $possibleLocations = self::getCallingFilesPaths(); - + foreach ($possibleLocations as $dir) { if (file_exists($dir.$trimmedPath)) { $trimmedPath = $dir.$trimmedPath; break; } } + if (!file_exists($trimmedPath)) { throw new TemplateNotFoundException('No file was found at "'.$trimmedPath.'".'); } @@ -74,33 +77,6 @@ public function __construct(string $templatePath, array $vars = []) { $this->rawOutput = ''; $this->compile($vars); } - /** - * Returns an array that contains directories names of the calling files. - * - * This method is used to extract the pathes of files at which they called - * this method. - * - * @return array An array that contains directories names of the calling files. - */ - public static function getCallingFilesPaths() : array { - $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); - $retVal = []; - foreach ($debugTrace as $traceEntry) { - if (isset($traceEntry['file'])) { - $file = $traceEntry['file']; - $split = explode(DIRECTORY_SEPARATOR, $file); - $withoutFile = array_diff($split, [$split[count($split) - 1]]); - $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); - if (strlen($dir) != 0) { - $dir .= DIRECTORY_SEPARATOR; - if (!in_array($dir, $retVal)) { - $retVal[] = $dir; - } - } - } - } - return $retVal; - } /** * Compile the template and return its representation as an object. @@ -196,7 +172,6 @@ public static function fromHTMLText(string $text, bool $asHTMLDocObj = true) { } } else { if (count($nodesArr) != 1) { - foreach ($nodesArr as $node) { $asHtmlNode = self::fromHTMLTextHelper00($node); $retVal[] = $asHtmlNode; @@ -211,6 +186,37 @@ public static function fromHTMLText(string $text, bool $asHTMLDocObj = true) { return null; } + /** + * Returns an array that contains directories names of the calling files. + * + * This method is used to extract the pathes of files at which they called + * this method. + * + * @return array An array that contains directories names of the calling files. + */ + public static function getCallingFilesPaths() : array { + $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); + $retVal = []; + + foreach ($debugTrace as $traceEntry) { + if (isset($traceEntry['file'])) { + $file = $traceEntry['file']; + $split = explode(DIRECTORY_SEPARATOR, $file); + $withoutFile = array_diff($split, [$split[count($split) - 1]]); + $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); + + if (strlen($dir) != 0) { + $dir .= DIRECTORY_SEPARATOR; + + if (!in_array($dir, $retVal)) { + $retVal[] = $dir; + } + } + } + } + + return $retVal; + } /** * Returns the compiled template. *