diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9eebab6e..f4139eb6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,6 @@ We accept contributions via Pull Requests on [Github](https://github.com/thephpl ## Running Tests ``` bash -$ phpunit +$ make test ``` -**Happy coding**! \ No newline at end of file +**Happy coding**! diff --git a/src/Engine.php b/src/Engine.php index 832fd091..52115364 100644 --- a/src/Engine.php +++ b/src/Engine.php @@ -21,9 +21,7 @@ public function __construct($config = []) { 'php_extensions' => ['php', 'phtml'], 'image_extensions' => ['png', 'jpg'], ]); - $this->container->add('compose', function($c) { - return Util\id(); - }); + $this->container->addComposed('compose', function() { return []; }); $this->container->add('fileExists', function($c) { return 'file_exists'; }); diff --git a/src/Extension/Data/DataExtension.php b/src/Extension/Data/DataExtension.php index 05fe9cf1..3c10ad12 100644 --- a/src/Extension/Data/DataExtension.php +++ b/src/Extension/Data/DataExtension.php @@ -12,13 +12,12 @@ public function register(Plates\Engine $plates) { $c->add('data.globals', []); $c->add('data.template_data', []); $c->merge('config', ['merge_parent_data' => true]); - $c->wrap('compose', function($compose, $c) { - return Plates\Util\compose(...array_filter([ - $c->get('config')['merge_parent_data'] ? mergeParentDataCompose() : null, - $c->get('data.globals') ? globalResolveData($c->get('data.globals')) : null, - $c->get('data.template_data') ? perTemplateResolveData($c->get('data.template_data')) : null, - $compose - ])); + $c->wrapComposed('compose', function($composers, $c) { + return array_merge(array_filter([ + 'data.perTemplateData' => $c->get('data.template_data') ? perTemplateDataCompose($c->get('data.template_data')) : null, + 'data.mergeParentData' => $c->get('config')['merge_parent_data'] ? mergeParentDataCompose() : null, + 'data.addGlobals' => $c->get('data.globals') ? addGlobalsCompose($c->get('data.globals')) : null, + ]), $composers); }); $plates->addMethods([ diff --git a/src/Extension/Data/data.php b/src/Extension/Data/data.php index 97668077..61008211 100644 --- a/src/Extension/Data/data.php +++ b/src/Extension/Data/data.php @@ -6,7 +6,7 @@ function addGlobalsCompose(array $globals) { return function(Template $template) use ($globals) { - return $template->withAddedData($globals); + return $template->withData(array_merge($globals, $template->data)); }; } @@ -20,10 +20,10 @@ function mergeParentDataCompose() { function perTemplateDataCompose(array $template_data_map) { return function(Template $template) use ($template_data_map) { - $name = $template->name; + $name = $template->get('normalized_name', $template->name); return isset($template_data_map[$name]) - ? $template->withAddedData($template_data_map[$name]) + ? $template->withData(array_merge($template_data_map[$name], $template->data)) : $template; }; } diff --git a/src/Extension/Folders/FoldersExtension.php b/src/Extension/Folders/FoldersExtension.php index fd69af74..89b8d4e3 100644 --- a/src/Extension/Folders/FoldersExtension.php +++ b/src/Extension/Folders/FoldersExtension.php @@ -12,7 +12,7 @@ public function register(Plates\Engine $plates) { 'folder_separator' => '::', ]); $c->add('folders.folders', []); - $c->wrap('path.resolvePath.stack', function($stack, $c) { + $c->wrapStack('path.resolvePath', function($stack, $c) { $config = $c; return array_merge([ 'folders' => foldersResolvePath( @@ -22,6 +22,11 @@ public function register(Plates\Engine $plates) { ) ], $stack); }); + $c->wrapComposed('path.normalizeName', function($composed, $c) { + return array_merge($composed, [ + 'folders.stripFolders' => stripFoldersNormalizeName($c->get('folders.folders')) + ]); + }); $plates->addMethods([ 'addFolder' => function($plates, $folder, $prefixes, $fallback = false) { $prefixes = is_string($prefixes) ? [$prefixes] : $prefixes; diff --git a/src/Extension/Folders/folders.php b/src/Extension/Folders/folders.php index 53ce30f6..60068d64 100644 --- a/src/Extension/Folders/folders.php +++ b/src/Extension/Folders/folders.php @@ -32,3 +32,17 @@ function foldersResolvePath(array $folders, $sep = '::', $file_exists = 'file_ex return $path; }; } + +function stripFoldersNormalizeName(array $folders, $sep = '::') { + return function($name) use ($folders, $sep) { + foreach ($folders as $folder) { + foreach (array_filter($folder['prefixes']) as $prefix) { + if (strpos($name, $prefix) === 0) { + return $folder['folder'] . $sep . substr($name, strlen($prefix) + 1); + } + } + } + + return $name; + }; +} diff --git a/src/Extension/LayoutSections/LayoutSectionsExtension.php b/src/Extension/LayoutSections/LayoutSectionsExtension.php index d82ebfed..e764649e 100644 --- a/src/Extension/LayoutSections/LayoutSectionsExtension.php +++ b/src/Extension/LayoutSections/LayoutSectionsExtension.php @@ -11,8 +11,8 @@ public function register(Plates\Engine $plates) { $c = $plates->getContainer(); $c->merge('config', ['default_layout_path' => null]); - $c->wrap('compose', function($compose, $c) { - return Plates\Util\compose($compose, sectionsCompose()); + $c->wrapComposed('compose', function($composed, $c) { + return array_merge($composed, ['layoutSections.sections' => sectionsCompose()]); }); $c->wrap('renderTemplate.factories', function($factories, $c) { $default_layout_path = $c->get('config')['default_layout_path']; diff --git a/src/Extension/NormalizeName/NormalizeNameExtension.php b/src/Extension/NormalizeName/NormalizeNameExtension.php deleted file mode 100644 index b3a67201..00000000 --- a/src/Extension/NormalizeName/NormalizeNameExtension.php +++ /dev/null @@ -1,14 +0,0 @@ -getContainer(); - $c->add('path.resolvePath.stack', function($c) { + $c->add('path.resolvePath.prefixes', function($c) { $config = $c->get('config'); // wrap base dir in an array if not already $base_dir = isset($config['base_dir']) ? $config['base_dir'] : null; $base_dir = $base_dir ? (is_string($base_dir) ? [$base_dir] : $base_dir) : $base_dir; - + return $base_dir; + }); + $c->addComposed('path.normalizeName', function($c) { + return [ + 'path.stripExt' => stripExtNormalizeName(), + 'path.stripPrefix' => stripPrefixNormalizeName($c->get('path.resolvePath.prefixes')) + ]; + }); + $c->addStack('path.resolvePath', function($c) { + $config = $c->get('config'); + $prefixes = $c->get('path.resolvePath.prefixes'); return array_filter([ - 'relative' =>relativeResolvePath(), - 'ext' => isset($config['ext']) ? extResolvePath($config['ext']) : null, - 'prefix' => $base_dir ? prefixResolvePath($base_dir, $c->get('fileExists')) : null, - 'id' => idResolvePath(), + 'path.relative' =>relativeResolvePath(), + 'path.ext' => isset($config['ext']) ? extResolvePath($config['ext']) : null, + 'path.prefix' => $prefixes ? prefixResolvePath($prefixes, $c->get('fileExists')) : null, + 'path.id' => idResolvePath(), ]); }); - $c->add('path.resolvePath', function($c) { - return Plates\Util\stack($c->get('path.resolvePath.stack')); - }); - $c->wrap('compose', function($compose, $c) { - return Plates\Util\compose( - $compose, - resolvePathCompose($c->get('path.resolvePath')) - ); + $c->wrapComposed('compose', function($composed, $c) { + return array_merge([ + 'path.resolvePath' => resolvePathCompose($c->get('path.resolvePath')), + 'path.normalizeName' => normalizeNameCompose($c->get('path.normalizeName')) + ], $composed); }); } } diff --git a/src/Extension/Path/path.php b/src/Extension/Path/path.php index 61e3aff8..73277c45 100644 --- a/src/Extension/Path/path.php +++ b/src/Extension/Path/path.php @@ -10,6 +10,39 @@ function resolvePathCompose(callable $resolve_path) { }; } +function normalizeNameCompose(callable $normalize_name) { + return function(Plates\Template $template) use ($normalize_name) { + return $template->with( + 'normalized_name', + Plates\Util\isPath($template->name) ? $normalize_name($template->get('path')) : $template->name + ); + }; +} + +function stripExtNormalizeName() { + return function($name) { + $ext = pathinfo($name, PATHINFO_EXTENSION); + if (!$ext) { + return $name; + } + + return substr($name, 0, (strlen($ext) + 1) * -1); // +1 for the leading `.` + }; +} + +function stripPrefixNormalizeName(array $prefixes) { + $prefixes = array_filter($prefixes); + return function($name) use ($prefixes) { + foreach ($prefixes as $prefix) { + if (strpos($name, $prefix . '/') === 0) { + return substr($name, strlen($prefix) + 1); // +1 for the trailing `/` + } + } + + return $name; + }; +} + /** appends an extension to the name */ function extResolvePath($ext = 'phtml') { @@ -32,7 +65,7 @@ function prefixResolvePath(array $prefixes, $file_exists = 'file_exists') { } foreach ($prefixes as $cur_prefix) { - $path = strpos($args->path, '/') === 0 + $path = Plates\Util\isAbsolutePath($args->path) ? $next($args) : $next($args->withPath( Plates\Util\joinPath([$cur_prefix, $args->path]) @@ -69,10 +102,7 @@ function prefixResolvePath(array $prefixes, $file_exists = 'file_exists') { /** Figures out the path based off of the parent templates current path */ function relativeResolvePath() { return function(ResolvePathArgs $args, $next) { - $is_relative = ( - strpos($args->path, './') === 0 - || strpos($args->path, '../') === 0 - ) && $args->template->parent; + $is_relative = Plates\Util\isRelativePath($args->path) && $args->template->parent; if (!$is_relative) { return $next($args); // nothing to do diff --git a/src/Extension/RenderContext/RenderContextExtension.php b/src/Extension/RenderContext/RenderContextExtension.php index 89033d60..ffe12330 100644 --- a/src/Extension/RenderContext/RenderContextExtension.php +++ b/src/Extension/RenderContext/RenderContextExtension.php @@ -9,10 +9,7 @@ final class RenderContextExtension implements Plates\Extension { public function register(Plates\Engine $plates) { $c = $plates->getContainer(); - $c->add('renderContext.func', function($c) { - return Plates\Util\stack($c->get('renderContext.func.stack')); - }); - $c->add('renderContext.func.stack', function($c) { + $c->addStack('renderContext.func', function($c) { return [ 'plates' => Plates\Util\stackGroup([ aliasNameFunc($c->get('renderContext.func.aliases')), @@ -48,11 +45,13 @@ public function register(Plates\Engine $plates) { ]; }); - $c->wrap('compose', function($compose, $c) { - return Plates\Util\compose($compose, renderContextCompose( - $c->get('renderContext.factory'), - $c->get('config')['render_context_var_name'] - )); + $c->wrapComposed('compose', function($composed, $c) { + return array_merge($composed, [ + 'renderContext.renderContext' => renderContextCompose( + $c->get('renderContext.factory'), + $c->get('config')['render_context_var_name'] + ) + ]); }); $c->add('include.bind', function($c) { return renderContextBind($c->get('config')['render_context_var_name']); diff --git a/src/Util/Container.php b/src/Util/Container.php index 2ef09c96..af2cc67a 100644 --- a/src/Util/Container.php +++ b/src/Util/Container.php @@ -13,13 +13,31 @@ public function add($id, $value) { } $this->boxes[$id] = [$value, $value instanceof \Closure ? true : false]; } + public function addComposed($id, callable $define_composers) { + $this->add($id, function($c) use ($id) { + return compose(...array_values($c->get($id . '.composers'))); + }); + $this->add($id . '.composers', $define_composers); + } + public function wrapComposed($id, callable $wrapped) { + $this->wrap($id . '.composers', $wrapped); + } + public function addStack($id, callable $define_stack) { + $this->add($id, function($c) use ($id) { + return stack($c->get($id . '.stack')); + }); + $this->add($id . '.stack', $define_stack); + } + public function wrapStack($id, callable $wrapped) { + $this->wrap($id . '.stack', $wrapped); + } public function merge($id, array $values) { $old = $this->get($id); $this->add($id, array_merge($old, $values)); } public function wrap($id, $wrapper) { if (!$this->has($id)) { - throw new \LogicException('Cannot wrap a service that does not exist.'); + throw new \LogicException('Cannot wrap service ' . $id . ' that does not exist.'); } $box = $this->boxes[$id]; $this->boxes[$id] = [function($c) use ($box, $wrapper) { @@ -31,7 +49,7 @@ public function get($id) { return $this->cached[$id]; } if (!$this->has($id)) { - throw new \LogicException('Cannot retrieve service that does exist.'); + throw new \LogicException('Cannot retrieve service ' . $id . ' that does exist.'); } $result = $this->unbox($this->boxes[$id], $this); if ($this->boxes[$id][1]) { // only cache services diff --git a/src/Util/util.php b/src/Util/util.php index 03f6ce87..024970b4 100644 --- a/src/Util/util.php +++ b/src/Util/util.php @@ -70,6 +70,19 @@ function joinPath(array $parts, $sep = DIRECTORY_SEPARATOR) { }, null); } +function isAbsolutePath($path) { + return strpos($path, '/') === 0; +} +function isRelativePath($path) { + return strpos($path, './') === 0 || strpos($path, '../') === 0; +} +function isResourcePath($path) { + return strpos($path, '://') !== false; +} +function isPath($path) { + return isAbsolutePath($path) || isRelativePath($path) || isResourcePath($path); +} + /** returns the debug type of an object as string for exception printing */ function debugType($v) { if (is_object($v)) { diff --git a/test/integration/fixtures/normalize-name/main.phtml b/test/integration/fixtures/normalize-name/main.phtml new file mode 100644 index 00000000..fb59508a --- /dev/null +++ b/test/integration/fixtures/normalize-name/main.phtml @@ -0,0 +1 @@ +name: diff --git a/test/integration/path.spec.php b/test/integration/path.spec.php new file mode 100644 index 00000000..ea3acbdb --- /dev/null +++ b/test/integration/path.spec.php @@ -0,0 +1,16 @@ + __DIR__ . '/fixtures/normalize-name' + ]); + $plates->addGlobals(['name' => 'Bar']); + $plates->assignTemplateData('main', ['name' => 'Foo']); + + $expected = "name: Foo"; + expect($plates->render(__DIR__ . '/fixtures/normalize-name/main'))->equal($expected); + }); +}); diff --git a/test/unit/extension-path.spec.php b/test/unit/extension-path.spec.php index 3a36ad7d..f0c5127b 100644 --- a/test/unit/extension-path.spec.php +++ b/test/unit/extension-path.spec.php @@ -7,6 +7,9 @@ Extension\Path\relativeResolvePath, Extension\Path\prefixResolvePath, Extension\Path\extResolvePath, + Extension\Path\stripPrefixNormalizeName, + Extension\Path\stripExtNormalizeName, + Extension\Path\normalizeNameCompose, Util\joinPath, Util\stack }; @@ -78,4 +81,45 @@ expect($path)->equal('foo.bar'); }); }); + describe('normalizeNameCompose', function() { + it('normalizes the name field into the normalized_name template attribute if it is a path', function() { + $compose = normalizeNameCompose(function($name) { + return $name . 'bar'; + }); + expect($compose((new Template('/foo', [], ['path' => '/foo'])))->get('normalized_name'))->equal('/foobar'); + }); + it('sets the name into the normalized_name attribute if it is not a path', function() { + $compose = normalizeNameCompose(function($name) { + return $name . 'bar'; + }); + expect($compose((new Template('foo', [], ['path' => 'foo'])))->get('normalized_name'))->equal('foo'); + }); + }); + describe('stripExtNormalizeName', function() { + it('strips the extension if it exists', function() { + $nn = stripExtNormalizeName(); + expect($nn('abc.ext'))->equal('abc'); + }); + it('does nothing if no extension found', function() { + $nn = stripExtNormalizeName(); + expect($nn('abc'))->equal('abc'); + }); + }); + describe('stripPrefixNormalizeName', function() { + it('strips a prefix if there is a match', function() { + $nn = stripPrefixNormalizeName([ + '/a/b/c', + '/b', + ]); + expect($nn('/a/b/c/d'))->equal('d'); + expect($nn('/b/d/c'))->equal('d/c'); + }); + it('filters any empty prefixes before matching', function() { + $nn = stripPrefixNormalizeName([ + '', + '/b', + ]); + expect($nn('/a'))->equal('/a'); + }); + }); });