大脑 / 层次结构
无依赖的软件包,体现WordPress模板层次结构
Requires
- php: >=7.1.3
Requires (Dev)
- brain/monkey: ^2.6.1
- inpsyde/php-coding-standards: ^1@dev
- mockery/mockery: ^1.3.0 || ^1.5.0
- php-stubs/wordpress-stubs: >=5.9@stable
- phpunit/phpunit: ^7.5.20 || ^9.5.16
- symfony/finder: ^v4.4.37
- vimeo/psalm: ^4.21.0
Suggests
- symfony/finder: Allows loading of templates using Symfony finder component.
README
使用PHP对象表示WordPress模板层次结构
目录
- 是什么/为什么?
- 模板层次结构表示
- 模板解析
- 介绍
QueryTemplate
- 返回模板内容
- 在输出之前编辑模板内容
- 模板查找器
- 模板加载的核心过滤器
- 介绍模板加载器
QueryTemplate
使用示例:加载和渲染Mustache模板- 要求
- 安装
- 许可证
是什么/为什么?
对于每个前端请求,WordPress都会运行一个查询,然后根据该查询加载一个模板文件。
查询 => 模板映射遵循在模板层次结构中定义的规则。
然而,给定一个查询对象,没有一种方法可以程序化地
- 知道WordPress将加载哪个模板
- 知道WordPress将搜索哪些模板
- 将相同的"查询到模板解析"应用于一个查询
此库提供了一种执行上述3件事的方法。
模板层次结构表示
给定一个查询,此库以PHP数组的形式提供了一个模板层次结构表示
示例
// we will show template hierarchy for the main query global $wp_query; $hierarchy = new Brain\Hierarchy\Hierarchy(); var_export($hierarchy->hierarchy($wp_query));
假设我们正在访问一个URL,例如 example.com/category/foo/page/2
,并且术语"foo"的分类ID是123,上述代码的输出是
array( 'category' => array('category-foo', 'category-123', 'category'), 'archive' => array('archive'), 'paged' => array('paged'), 'index' => array('index'), );
如果您将此数组与模板层次结构视觉概述进行比较,您可以验证上述内容是针对分类查询的模板层次结构的准确表示。
模板解析
如果您想回答的问题是
WordPress将为这个查询尝试找到哪些模板?
可以使用Hierarchy::templates()
方法简单地回答
// we will target the main query global $wp_query; $hierarchy = new Brain\Hierarchy\Hierarchy(); var_export($hierarchy->templates($wp_query));
假设与上述相同的查询,输出将是
array( 'category-foo', 'category-123', 'category', 'archive', 'paged', 'index', );
这是WordPress将搜索的模板列表,按照WordPress将使用的相同顺序。
模板解析示例
对于此示例,我将假设一个主题将模板文件存储在/templates
子文件夹中,并使用.phtml
作为文件扩展名。
根据模板层次结构加载这些模板所需的所有代码如下
add_action('template_redirect', function() { $templates = (new Brain\Hierarchy\Hierarchy())->templates(); foreach($templates as $template) { $path = get_theme_file_path("/templates/{$template}.phtml"); if (file_exists($path)) { require $path; exit(); } } });
上述示例有效,只是展示了您可以使用此库做什么的示例。
但是,为了加载模板,此库提供了一个特定的类:QueryTemplate
。
介绍 QueryTemplate
QueryTemplate
类使用Hierarchy
类来获取要搜索的模板列表,然后查找这些模板并加载第一个找到的模板。
示例
add_action('template_redirect', function(): void { global $wp_query; $queryTemplate = new \Brain\Hierarchy\QueryTemplate(); echo $queryTemplate->loadTemplate($wp_query); exit(); });
上述代码确实执行了WordPress所做的事情:在主题文件夹和在父主题文件夹中(如果当前主题是子主题)搜索合适的模板,然后加载第一个找到的模板并将其内容打印到页面上。
请注意,模板内容是通过QueryTemplate::loadTemplate()
返回的,所以需要使用echo
来实际显示页面内容。
但是,这只是一种默认行为,并且可以进行自定义。
模板查找器
默认情况下,QueryTemplate
类在主题(以及如果有,父主题)文件夹中搜索模板,就像WordPress所做的那样。
然而,可以使用不同的“模板查找”类来实现不同的功能。
所有模板查找类都必须实现 Brain\Hierarchy\Finder\TemplateFinder
接口。
库中包含一些实现该接口的类,当然,也可以编写自定义的一个。
Finder\ByFolders
类 Brain\Hierarchy\Finder\ByFolders
可以用来在某个 任意 文件夹中搜索模板,而不是主题和父主题文件夹。
示例
add_action('template_redirect', function(): void { $finder = new \Brain\Hierarchy\Finder\ByFolders([ __DIR__, get_stylesheet_directory(), get_template_directory(), ]); $queryTemplate = new \Brain\Hierarchy\QueryTemplate($finder); echo $queryTemplate->loadTemplate(); exit(); });
上面的代码片段将在 当前文件夹 中搜索模板,如果没有找到,它们将在主题和父主题文件夹中搜索。
自定义文件扩展名
Finder\ByFolders
类默认搜索具有 .php
扩展名的文件,但可以通过将它们作为第二个构造函数参数(字符串或字符串数组)传递来使用不同的文件扩展名。
// This will look for *.phtml files. $phtml_finder = new \Brain\Hierarchy\Finder\ByFolders( [get_stylesheet_directory(), get_template_directory()], 'phtml' ); // This will look for Twig files first, and fall back to standard PHP files if // no matching Twig file was found. $twig_finder = new \Brain\Hierarchy\Finder\ByFolders( [get_stylesheet_directory(), get_template_directory()], 'twig', 'php' );
Finder\BySubfolder
这个模板查找类与 Brain\Hierarchy\Finder\ByFolders
非常相似,但它在一个特定的主题(和父主题)子文件夹中查找模板,并使用主题(和父主题)文件夹作为后备。
add_action('template_redirect', function(): void { $finder = new \Brain\Hierarchy\Finder\BySubfolder('templates'); $queryTemplate = new \Brain\Hierarchy\QueryTemplate($finder); echo $queryTemplate->loadTemplate(); exit(); } );
使用上面的代码,模板按以下顺序搜索:
/path/to/wp-content/child-theme/templates/
/path/to/wp-content/parent-theme/templates/
/path/to/wp-content/child-theme/
/path/to/wp-content/parent-theme/
Finder\BySubfolder
类与 Finder\ByFolders
类一样,接受(可变数量的)自定义文件扩展名作为第二个构造函数参数。
Finder\Localized
这个查找类与另一个查找类结合使用,允许根据当前区域加载模板。
add_action('template_redirect', function(): void { $foldersFinder = new \Brain\Hierarchy\Finder\ByFolders(); $finder = new \Brain\Hierarchy\Finder\Localized($foldersFinder); $queryTemplate = new \Brain\Hierarchy\QueryTemplate($finder); echo $queryTemplate->loadTemplate(); exit(); } );
假设当前区域是 it_IT
,使用上面的代码,模板按以下顺序搜索:
/path/to/wp-content/child-theme/it_IT/
/path/to/wp-content/parent-theme/it_IT/
/path/to/wp-content/child-theme/it/
/path/to/wp-content/parent-theme/it/
/path/to/wp-content/child-theme/
/path/to/wp-content/parent-theme/
Finder\SymfonyFinderAdapter
这个类允许使用 Symfony Finder Component 来查找模板。
add_action('template_redirect', function() { $symfonyFinder = new \Symfony\Component\Finder\Finder(); $symfonyFinder = $symfonyFinder->files()->in(__DIR__); $finder = new \Brain\Hierarchy\Finder\SymfonyFinderAdapter($symfonyFinder); $queryTemplate = new \Brain\Hierarchy\QueryTemplate($finder); echo $queryTemplate->loadTemplate(); exit(); } );
Finder\ByCallback
这个类可以用来轻松地将第三方不同加载器与 QueryTemplate
类集成。
实际上,需要提供一个任意回调,该回调将被调用以查找模板。
回调将接收到不带文件扩展名的模板名称,例如 index
,并且如果找到模板,必须返回模板的完整路径,如果没有找到模板,则返回空字符串。
示例
add_action('template_redirect', function(): void { $callback = fn(string $tpl): string => realpath(__DIR__ . "{$template}.php") ?: ''; $finder = new \Brain\Hierarchy\Finder\ByCallback($callback); $queryTemplate = new \Brain\Hierarchy\QueryTemplate($finder); echo $queryTemplate->loadTemplate(); exit(); } );
模板加载的核心过滤器
当WordPress在 template-loader.php
中搜索模板时,它将触发不同形式的过滤器,形式为 {$type}_template
;例如 'single_template'、'page_template' 等等。
此外,找到的模板将经过 'template_include' 过滤器。
默认情况下,QueryTemplate::loadTemplate()
应用相同的过滤器,以最大限度地与核心行为兼容。
这无论使用哪个模板查找类都会发生。
然而,通过将 false
作为方法的第二个参数传递,将停止应用这些核心过滤器。
介绍模板加载器
在找到任何查找类的模板之后,QueryTemplate
必须将其“加载”。
默认情况下,加载只是由 ob_start()
/ ob_get_clean()
包裹的 require
,这样模板内容就可以原样返回。
然而,也可以以某种方式 处理 模板,例如,使用一个 模板引擎。
自定义模板加载器必须实现 Brain\Hierarchy\Loader\Loader
接口,该接口只有一个方法:load()
,该方法接收模板的完整路径并必须 返回 模板内容。
模板加载器可以作为第二个构造函数参数传递给 QueryTemplate
。
Loader\FileRequire
这是库中包含的加载器类,它提供了默认行为。
聚合加载器
聚合加载器使用不同的“内部”加载器来加载模板。
聚合加载器必须实现接口 Brain\Hierarchy\Loader\Aggregate
,该接口有两个方法。
addLoader(Loader\TemplateLoader $loader, callable $predicate)
addLoaderFactory(callable $loaderFactory, callable $predicate)
第一个用于添加模板加载器实例。第二个用于添加一个工厂,一旦调用,将返回一个模板加载器实例。
这两个方法接受第二个参数一个“断言”:一个回调函数,将接收要加载的模板文件路径,并返回一个布尔值。
当断言返回true
时,相关加载器用于加载模板。
Loader\Cascade
Loader\Cascade
是一个简单的聚合加载器实现,其中断言的评估顺序与它们添加的顺序相同(先进先出)。
Loader\ExtensionMap
Loader\ExtensionMap
是Hierarchy附带的其他聚合加载器实现。
它用于根据模板文件扩展名加载不同的加载器。
它需要一个传递给构造函数的“扩展名到加载器”的“映射”。
映射键是模板文件扩展名,值是使用的加载器。
加载器可以传递为
- 模板加载器实例
- 模板加载器完全限定类名
- 工厂回调,一旦调用就返回模板加载器实例
可以使用由多个文件扩展名通过竖线|
分隔的字符串作为映射键使用同一个加载器为多个文件扩展名服务。
示例
$loader = new Loader\ExtensionMap([ 'php|phtml' => new Loader\FileRequire(), 'mustache' => fn() => new MyMustacheAdapter(new Mustache_Engine), 'md' => MyMarkdownRenderer::class ]);
QueryTemplate
使用示例:加载和渲染Mustache模板
以下将展示所有必要的代码,以根据WordPress模板层次结构查找和渲染mustache模板。
namespace My\Theme; use Brain\Hierarchy\{Finder, Loader, QueryTemplate}; class MustacheTemplateLoader implements Loader\Loader { private $engine; public function __construct(\Mustache_Engine $engine) { $this->engine = $engine; } public function load(string $templatePath): string { // let's use a filter to build some context for the template $data = apply_filters('my_theme_data', ['query' => $GLOBALS['wp_query'], $templatePath); return $this->engine->render(file_get_contents($templatePath), $data); } } add_action('template_redirect', function() { if (!QueryTemplate::mainQueryTemplateAllowed()) { return; } $queryTemplate = new QueryTemplate( // will look for "*.mustache" templates in theme's "/templates" subfolder new Finder\BySubfolder('templates', 'mustache'), // the loader class defined above new MustacheTemplateLoader(new \Mustache_Engine()) ); // 3rd argument of loadTemplate() is passed by reference, and set to true if template is found $content = $queryTemplate->loadTemplate(null, true, $found); // if template was found, let's output it and exit, otherwise WordPress will continue its work $found and die($content); });
要求
Hierarchy需要PHP 7.1.3+和Composer已安装。
安装
最佳通过Composer提供,可在Packagist上找到,名称为brain/hierarchy
。
从2.*版本迁移
版本3的逻辑没有变化,但现在所有类都使用类型声明,其中一些已经被重命名。
基于Hierarchy并实现其加载器/查找接口的库将需要对类进行重命名并添加类型声明。
类FileExtensionPredicate
的签名略有变化。
仅使用Hierarchy
类的库应该无需任何更改即可工作,即使getHierarchy()
和getTemplates()
方法现在分别被hierarchy()
和templates()
替代,但旧方法在任何3.*版本中都不会被删除。
许可证
Hierarchy是在MIT许可下发布的。