From 121cfcf60fe9f3bc8f8cc3a68ad66239dab65884 Mon Sep 17 00:00:00 2001 From: Luiz Machado Date: Sat, 21 Jan 2023 15:47:34 -0300 Subject: [PATCH 1/5] added sections inheritance of fetched templates section mode handling is still missing though --- src/Template/Template.php | 90 +++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/src/Template/Template.php b/src/Template/Template.php index db11a9f..91ed0d5 100644 --- a/src/Template/Template.php +++ b/src/Template/Template.php @@ -261,21 +261,10 @@ public function stop() $this->sections[$this->sectionName] = ''; } - switch ($this->sectionMode) { + $sectionContent = ob_get_clean(); - case self::SECTION_MODE_REWRITE: - $this->sections[$this->sectionName] = ob_get_clean(); - break; - - case self::SECTION_MODE_APPEND: - $this->sections[$this->sectionName] .= ob_get_clean(); - break; + $this->pushSection($this->sectionMode, $this->sectionName, $sectionContent); - case self::SECTION_MODE_PREPEND: - $this->sections[$this->sectionName] = ob_get_clean().$this->sections[$this->sectionName]; - break; - - } $this->sectionName = null; $this->sectionMode = self::SECTION_MODE_REWRITE; $this->appendSection = false; /* for backward compatibility */ @@ -305,6 +294,71 @@ public function section($name, $default = null) return $this->sections[$name]; } + /** + * Allows templates that are fetching this template to + * fetch the sections as well. + * + * @return array + */ + protected function getSections() + { + return $this->sections; + } + + /** + * Joins another Template's sections into this taking in + * consideration the template's section mode. + * + * @param Template $template + * @return void + */ + protected function joinSections($template) + { + foreach ($template->getSections() as $sectionName => $sectionContent) { + $this->pushSection($template->sectionMode, $sectionName, $sectionContent); + } + } + + /** + * Pushes the given section content to the sections array. + * + * @param int $sectionMode + * @param string $sectionName + * @param string $sectionContent + * + * @return void + */ + private function pushSection($sectionMode, $sectionName, $sectionContent) + { + // if ob_clean failed for some reason let's just ignore the result + if ($sectionContent === false) { + return; + } + + // if this template doesn't have that section, so we just add it. + if (![$sectionName]) { + $this->sections[$sectionName] = $sectionContent; + return; + } + + // otherwise we need to consider the incoming section mode + switch ($sectionMode) { + + case self::SECTION_MODE_REWRITE: + $this->sections[$sectionName] = $sectionContent; + break; + + case self::SECTION_MODE_APPEND: + $this->sections[$sectionName] .= $sectionContent; + break; + + case self::SECTION_MODE_PREPEND: + $this->sections[$sectionName] = $sectionContent.$this->sections[$sectionName]; + break; + + } + } + /** * Fetch a rendered template. * @param string $name @@ -313,7 +367,15 @@ public function section($name, $default = null) */ public function fetch($name, array $data = array()) { - return $this->engine->render($name, $data); + $template = $this->engine->make($name); + $content = $template->render($data); + + // some info like 'sections' are only filled during + // the render processing, so here we have a window to + // fetch them and join to this template. + $this->joinSections($template); + + return $content; } /** From 6ecd911139d44c2f356b2b9e57c7b5161e834ee1 Mon Sep 17 00:00:00 2001 From: odahcam Date: Sun, 22 Jan 2023 19:35:44 -0300 Subject: [PATCH 2/5] resolves index does not exists on child section consuming --- src/Template/Template.php | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Template/Template.php b/src/Template/Template.php index 91ed0d5..cf9b79a 100644 --- a/src/Template/Template.php +++ b/src/Template/Template.php @@ -342,20 +342,23 @@ private function pushSection($sectionMode, $sectionName, $sectionContent) } // otherwise we need to consider the incoming section mode - switch ($sectionMode) { - - case self::SECTION_MODE_REWRITE: + if ($sectionMode === self::SECTION_MODE_REWRITE) { $this->sections[$sectionName] = $sectionContent; - break; + return; + } - case self::SECTION_MODE_APPEND: - $this->sections[$sectionName] .= $sectionContent; - break; + $existingContent = array_key_exists($sectionName, $this->sections) + ? $this->sections[$sectionName] + : ''; - case self::SECTION_MODE_PREPEND: - $this->sections[$sectionName] = $sectionContent.$this->sections[$sectionName]; - break; + if ($sectionMode === self::SECTION_MODE_APPEND) { + $this->sections[$sectionName] = $existingContent.$sectionContent; + return; + } + if ($sectionMode === self::SECTION_MODE_PREPEND) { + $this->sections[$sectionName] = $sectionContent.$existingContent; + return; } } From c8facf3c8aeaadf6a9b392dd1fc471ae3bfb4738 Mon Sep 17 00:00:00 2001 From: odahcam Date: Sun, 22 Jan 2023 19:38:45 -0300 Subject: [PATCH 3/5] consuming child sections with their original modes --- src/Template/Template.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Template/Template.php b/src/Template/Template.php index cf9b79a..98af94f 100644 --- a/src/Template/Template.php +++ b/src/Template/Template.php @@ -47,6 +47,14 @@ class Template */ protected $sections = array(); + /** + * Maps how sections are added so they cen + * be consumed by parent Templates later. + * + * @var array + */ + protected $sectionsAddedModes = array(); + /** * The name of the section currently being rendered. * @var string @@ -263,7 +271,7 @@ public function stop() $sectionContent = ob_get_clean(); - $this->pushSection($this->sectionMode, $this->sectionName, $sectionContent); + $this->addSection($this->sectionMode, $this->sectionName, $sectionContent); $this->sectionName = null; $this->sectionMode = self::SECTION_MODE_REWRITE; @@ -294,17 +302,6 @@ public function section($name, $default = null) return $this->sections[$name]; } - /** - * Allows templates that are fetching this template to - * fetch the sections as well. - * - * @return array - */ - protected function getSections() - { - return $this->sections; - } - /** * Joins another Template's sections into this taking in * consideration the template's section mode. @@ -312,10 +309,11 @@ protected function getSections() * @param Template $template * @return void */ - protected function joinSections($template) + private function joinSections($template) { - foreach ($template->getSections() as $sectionName => $sectionContent) { - $this->pushSection($template->sectionMode, $sectionName, $sectionContent); + foreach ($template->sections as $sectionName => $sectionContent) { + $sectionMode = $template->sectionsAddedModes[$sectionName]; + $this->addSection($sectionMode, $sectionName, $sectionContent); } } @@ -328,13 +326,15 @@ protected function joinSections($template) * * @return void */ - private function pushSection($sectionMode, $sectionName, $sectionContent) + private function addSection($sectionMode, $sectionName, $sectionContent) { // if ob_clean failed for some reason let's just ignore the result if ($sectionContent === false) { return; } + $this->sectionsAddedModes[$sectionName] = $sectionMode; + // if this template doesn't have that section, so we just add it. if (![$sectionName]) { $this->sections[$sectionName] = $sectionContent; @@ -343,7 +343,7 @@ private function pushSection($sectionMode, $sectionName, $sectionContent) // otherwise we need to consider the incoming section mode if ($sectionMode === self::SECTION_MODE_REWRITE) { - $this->sections[$sectionName] = $sectionContent; + $this->sections[$sectionName] = $sectionContent; return; } From 01af12ab986013fa559f8db995e9f2a2cd7fefdb Mon Sep 17 00:00:00 2001 From: odahcam Date: Sat, 4 Feb 2023 13:25:42 -0300 Subject: [PATCH 4/5] created Template Section class structure --- .gitignore | 8 ++ src/Template/Template.php | 101 ++++---------- src/Template/TemplateSection.php | 98 ++++++++++++++ src/Template/TemplateSectionCollection.php | 148 +++++++++++++++++++++ tests/Template/TemplateTest.php | 3 + 5 files changed, 281 insertions(+), 77 deletions(-) create mode 100644 src/Template/TemplateSection.php create mode 100644 src/Template/TemplateSectionCollection.php diff --git a/.gitignore b/.gitignore index 57e5dcd..de93b26 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,11 @@ doc/public doc/data/versions.json .generated .phpunit.result.cache +.idea/.gitignore +.idea/modules.xml +.idea/php-test-framework.xml +.idea/php.xml +.idea/phpunit.xml +.idea/plates.iml +.idea/vcs.xml +composer.phar diff --git a/src/Template/Template.php b/src/Template/Template.php index 98af94f..21c66f0 100644 --- a/src/Template/Template.php +++ b/src/Template/Template.php @@ -2,7 +2,6 @@ namespace League\Plates\Template; -use Exception; use League\Plates\Engine; use League\Plates\Exception\TemplateNotFound; use LogicException; @@ -43,17 +42,10 @@ class Template /** * An array of section content. - * @var array - */ - protected $sections = array(); - - /** - * Maps how sections are added so they cen - * be consumed by parent Templates later. * - * @var array + * @var TemplateSectionCollection */ - protected $sectionsAddedModes = array(); + protected $sections; /** * The name of the section currently being rendered. @@ -89,6 +81,7 @@ public function __construct(Engine $engine, $name) { $this->engine = $engine; $this->name = new Name($engine, $name); + $this->sections = new TemplateSectionCollection(); $this->data($this->engine->getData($name)); } @@ -181,7 +174,9 @@ public function render(array $data = array()) if (isset($this->layoutName)) { $layout = $this->engine->make($this->layoutName); - $layout->sections = array_merge($this->sections, array('content' => $content)); + $layout->sections->merge($this->sections); + $contentSectionName = 'content'; + $layout->sections[$contentSectionName] = $content; $content = $layout->render($this->layoutData); } @@ -259,19 +254,30 @@ public function unshift($name) */ public function stop() { - if (is_null($this->sectionName)) { + $sectionName = $this->sectionName; + + if (is_null($sectionName)) { throw new LogicException( 'You must start a section before you can stop it.' ); } - if (!isset($this->sections[$this->sectionName])) { - $this->sections[$this->sectionName] = ''; + if (!$this->sections->has($sectionName)) { + $this->sections[$sectionName] = ''; } $sectionContent = ob_get_clean(); - $this->addSection($this->sectionMode, $this->sectionName, $sectionContent); + // if ob_clean failed for some reason let's just ignore the result + if ($sectionContent === false) { + return; + } + + $this->sections->add( + $sectionName, + $sectionContent, + $this->sectionMode + ); $this->sectionName = null; $this->sectionMode = self::SECTION_MODE_REWRITE; @@ -302,68 +308,9 @@ public function section($name, $default = null) return $this->sections[$name]; } - /** - * Joins another Template's sections into this taking in - * consideration the template's section mode. - * - * @param Template $template - * @return void - */ - private function joinSections($template) - { - foreach ($template->sections as $sectionName => $sectionContent) { - $sectionMode = $template->sectionsAddedModes[$sectionName]; - $this->addSection($sectionMode, $sectionName, $sectionContent); - } - } - - /** - * Pushes the given section content to the sections array. - * - * @param int $sectionMode - * @param string $sectionName - * @param string $sectionContent - * - * @return void - */ - private function addSection($sectionMode, $sectionName, $sectionContent) - { - // if ob_clean failed for some reason let's just ignore the result - if ($sectionContent === false) { - return; - } - - $this->sectionsAddedModes[$sectionName] = $sectionMode; - - // if this template doesn't have that section, so we just add it. - if (![$sectionName]) { - $this->sections[$sectionName] = $sectionContent; - return; - } - - // otherwise we need to consider the incoming section mode - if ($sectionMode === self::SECTION_MODE_REWRITE) { - $this->sections[$sectionName] = $sectionContent; - return; - } - - $existingContent = array_key_exists($sectionName, $this->sections) - ? $this->sections[$sectionName] - : ''; - - if ($sectionMode === self::SECTION_MODE_APPEND) { - $this->sections[$sectionName] = $existingContent.$sectionContent; - return; - } - - if ($sectionMode === self::SECTION_MODE_PREPEND) { - $this->sections[$sectionName] = $sectionContent.$existingContent; - return; - } - } - /** * Fetch a rendered template. + * * @param string $name * @param array $data * @return string @@ -376,7 +323,7 @@ public function fetch($name, array $data = array()) // some info like 'sections' are only filled during // the render processing, so here we have a window to // fetch them and join to this template. - $this->joinSections($template); + $this->sections->merge($template->sections); return $content; } @@ -389,7 +336,7 @@ public function fetch($name, array $data = array()) */ public function insert($name, array $data = array()) { - echo $this->engine->render($name, $data); + echo $this->fetch($name, $data); } /** diff --git a/src/Template/TemplateSection.php b/src/Template/TemplateSection.php new file mode 100644 index 0000000..9050da3 --- /dev/null +++ b/src/Template/TemplateSection.php @@ -0,0 +1,98 @@ +name = $name; + $this->content = $content; + $this->mode = $mode; + } + + /** + * @param string $content + * @param int $mode + * @return void + */ + public function add(string $content, int $mode) + { + $this->mode = $mode; + + // if this template doesn't have that section, so we just add it. + if (empty($this->content)) { + $this->content = $content; + return; + } + + // otherwise we need to consider the incoming section mode + if ($mode === Template::SECTION_MODE_REWRITE) { + $this->content = $content; + return; + } + + if ($mode === Template::SECTION_MODE_APPEND) { + $this->content = $this->content . $content; + return; + } + + if ($mode === Template::SECTION_MODE_PREPEND) { + $this->content = $content . $this->content; + } + } + + /** + * @return string|null + */ + public function getContent(): string + { + return $this->content; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return int + */ + public function getMode(): int + { + return $this->mode; + } +} diff --git a/src/Template/TemplateSectionCollection.php b/src/Template/TemplateSectionCollection.php new file mode 100644 index 0000000..98c174d --- /dev/null +++ b/src/Template/TemplateSectionCollection.php @@ -0,0 +1,148 @@ + + */ + private $sections = array(); + + /** + * @param string $offset + * + * @return bool + */ + public function has(string $offset): bool + { + return array_key_exists($offset, $this->sections); + } + + /** + * @param string $offset + * + * @return TemplateSection + */ + public function get(string $offset) + { + return $this->sections[$offset]; + } + + /** + * @param string $offset + * @param TemplateSection|null $value + * + * @return void + */ + public function set(string $offset, $value) + { + if (!is_string($offset)) { + throw new InvalidArgumentException('The desired section offset must be a string.'); + } + if (!($value instanceof TemplateSection) && $value != null) { + throw new InvalidArgumentException('The section set must be of type TemplateSection.'); + } + $this->sections[$offset] = $value; + } + + /** + * @param string $offset + * + * @return bool + */ + public function offsetExists($offset): bool + { + if (!is_string($offset)) { + throw new InvalidArgumentException('The desired section offset must be a string.'); + } + return $this->has($offset); + } + + /** + * @param string $offset + * + * @return string + */ + public function offsetGet($offset) + { + if (!is_string($offset)) { + throw new InvalidArgumentException('The desired section offset must be a string.'); + } + return $this->get($offset)->getContent(); + } + + /** + * @param string $offset + * @param string $value + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (!is_string($offset)) { + throw new InvalidArgumentException('The desired section offset must be a string.'); + } + $this->add($offset, $value, Template::SECTION_MODE_REWRITE); + } + + /** + * @param string $offset + * + * @return void + */ + public function offsetUnset($offset) + { + if (!is_string($offset)) { + throw new InvalidArgumentException('The desired section offset must be a string.'); + } + $this->sections[$offset] = null; + unset($this->sections[$offset]); + } + + /** + * Pushes the given section content to the sections array. + * You can use the $mode to merge the content to an existing + * content if that section content already exists. + * + * @param string $name + * @param string $content + * @param ?int $mode + * + * @return void + */ + public function add(string $name, string $content, $mode = Template::SECTION_MODE_APPEND) + { + if ($this->has($name)) { + $this->sections[$name]->add($content, $mode); + return; + } + $this->sections[$name] = new TemplateSection($name, $content, $mode); + } + + /** + * Merges another Template Sections collection into + * this one taking in consideration the template + * section's modes. + * + * @return void + */ + public function merge(TemplateSectionCollection $templateSections) + { + foreach ($templateSections->sections as $section) { + $this->add( + $section->getName(), + $section->getContent(), + $section->getMode() + ); + } + } +} diff --git a/tests/Template/TemplateTest.php b/tests/Template/TemplateTest.php index 788acd0..7acc588 100644 --- a/tests/Template/TemplateTest.php +++ b/tests/Template/TemplateTest.php @@ -11,6 +11,9 @@ class TemplateTest extends TestCase { + /** + * @var Template + */ private $template; protected function setUp(): void From 262acb1db04a283e280fc3991a3c64ce7304da7d Mon Sep 17 00:00:00 2001 From: odahcam Date: Tue, 7 Mar 2023 20:04:37 -0300 Subject: [PATCH 5/5] added more template sections tests --- tests/Template/TemplateTest.php | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/Template/TemplateTest.php b/tests/Template/TemplateTest.php index 7acc588..54b1be2 100644 --- a/tests/Template/TemplateTest.php +++ b/tests/Template/TemplateTest.php @@ -186,6 +186,69 @@ public function testReplaceSection() $this->assertSame('See this instead!', $this->template->render()); } + public function testInheritedSection() + { + vfsStream::create( + array( + 'template-child.php' => 'push("textSection") ?>See this! stop() ?>', + 'template.php' => 'layout("layout")?>fetch("template-child")?>push("textSection") ?>See this too!stop() ?>', + 'layout.php' => 'section("textSection") ?>', + ) + ); + + $this->assertSame('See this! See this too!', $this->template->render()); + } + + public function testInheritedSectionUnshift() + { + vfsStream::create( + array( + 'template-child.php' => 'push("textSection") ?>See this!stop() ?>', + 'template.php' => 'layout("layout")?>fetch("template-child")?>unshift("textSection") ?>See this too! stop() ?>', + 'layout.php' => 'section("textSection") ?>', + ) + ); + + $this->assertSame('See this too! See this!', $this->template->render()); + } + + public function testInheritedSectionUnshiftsParent() + { + vfsStream::create( + array( + 'template-child.php' => 'unshift("textSection") ?>See this! stop() ?>', + 'template.php' => 'layout("layout")?>unshift("textSection") ?>See this too!stop() ?>fetch("template-child")?>', + 'layout.php' => 'section("textSection") ?>', + ) + ); + + $this->assertSame('See this! See this too!', $this->template->render()); + } + + public function testInheritedSectionReplacement() + { + vfsStream::create(array( + 'template-child.php' => 'push("textSection") ?>See this!stop() ?>', + 'template.php' => 'layout("layout")?>fetch("template-child")?>start("textSection") ?>See this too!stop() ?>', + 'layout.php' => 'section("textSection") ?>', + )); + + $this->assertSame('See this too!', $this->template->render()); + } + + public function testInheritedSectionReplacesParent() + { + vfsStream::create( + array( + 'template-child.php' => 'start("textSection") ?>See this replacement!stop() ?>', + 'template.php' => 'layout("layout")?>start("textSection") ?> See this too!stop() ?>fetch("template-child")?>', + 'layout.php' => 'section("textSection") ?>', + ) + ); + + $this->assertSame('See this replacement!', $this->template->render()); + } + public function testStartSectionWithInvalidName() { // The section name "content" is reserved.