大脑 / 枕骨
枕骨是一个WordPress中资产管理包。
Requires
- php: >=5.4
- brain/brain: ~0.1.0
- brain/support: ~0.1.0
Requires (Dev)
- 10up/wp_mock: dev-master
- mockery/mockery: dev-master
- phpunit/phpunit: 3.7.*
This package is auto-updated.
Last update: 2023-02-26 11:02:46 UTC
README
枕骨是大脑模块,用于样式和脚本管理。
它允许以更好的方式在WordPress中排队脚本和样式。
枕骨,与其他大脑模块一样,不是一个完整的插件,而是一个嵌入到更大项目中的包。
但是,将枕骨嵌入到主题或插件中仅需要,字面上说,3行代码,并提供一个逐步指南来将枕骨包含到主题中。
##WordPress中标准样式和脚本处理有什么问题?
查看相关的博客文章。
#功能
-
为每个资产定义它提供的其他资产,以便WordPress可以跳过加载它们,减少每页的HTTP请求次数。
-
易于使用的API,允许在单个地方添加资产和附加数据,无需使用3个不同的函数,如
wp_register_script
、wp_enqueue_script
和wp_localize_script
(或wp_add_inline_style
用于样式)。 -
条件回调具有丰富的上下文:对于每个资产,都可以设置一个“条件”作为回调:只有当回调返回true时,资产才会打印到页面。该回调接收当前前端
WP_Query
对象和当前后端WP_Screen
对象(如果有的话),以及当前登录用户(如果有的话)作为参数。 -
丰富的API,可以实时获取和修改资产的属性(URL、依赖项、提供的资产、版本等)。
-
在资产打印到标记之前和之后触发自定义钩子,具有很高的灵活性。
-
正确处理登录/注册页面的
<head>
中的样式。 -
就像所有的大脑包一样,它使用所有现代的OOP PHP代码编写,并完全进行了单元测试。
#快速入门
枕骨中的所有事物都可以通过API类进行:Brain\Assets
。
在执行任何调用之前,您应该等待'brain_loaded'
钩子,或者如果您更喜欢核心钩子,则可以使用'init'
。
以下是一个示例,展示了所有可用的脚本方法(仅需要2个):
<?php Brain\Assets::addScript( 'my-script', 'front' ) ->src( "//example.com/path/to/style.js" ) ->deps( [ "jquery" ] ) ->provide( "bootstrap-tooltip", "lightbox", "plugin-script" ) ->ver( "20141007" ) ->isFooter( TRUE ) ->localizeData( [ 'name' => 'MyData', 'data' => [ 'foo' => 'bar' ] ] ) ->condition( function( $query, $user ) { return $query->is_front_page() && user_can( $user, 'edit_pages' ); });
addScript
方法允许在前端、后端和登录页面中添加脚本。要添加特定位置的资产,可以使用
addScript
的第二个参数,例如
Brain\Assets::addScript( 'my-script', 'front' ) // or 'admin' or 'login'
- 使用特定的API方法,如
addFrontScript
、addAdminScript
或addLoginScript
- 在特定的钩子上调用API方法,如
"wp_enqueue_scripts"
或"admin_enqueue_scripts"
。枕骨还提供了自定义替代钩子,有关详细信息,请参阅枕骨钩子。
API方法返回刚刚添加的资产对象实例,或者在出现错误时返回WP_Error
。
对于样式,有类似的方法:addStyle
、addFrontStyle
等等。
与核心相比的改进是
- "
提供
"参数,可以节省HTTP请求 - 一个API管理所有函数
- 资产属性在添加后轻松获取/更新
- 表达性流畅的API:无需记住参数的确切顺序
- 具有丰富上下文的条件回调(前端使用
WP_Query
+WP_User
,后端使用WP_Screen
+WP_User
) - 面向对象代码:易于嵌入面向对象项目中,并易于在单元测试中使用模拟进行测试。
#要求
- PHP 5.4+
- WordPress 3.9+
- 需要 Composer 来安装
#安装
您需要 Composer 来安装此包。它托管在 Packagist 上,因此您只需要在您的 composer.json
中的 require 对象中插入 "brain/occipital": "dev-master"
{ "require": { "php": ">=5.4", "brain/occipital": "dev-master" } }
之后,在您的控制台中,导航到包文件夹,并输入
composer install --no-dev
在使用生产环境之前,别忘了使用 --no-dev
标志,否则所有开发依赖项以及相关的自动加载内容将在每次页面加载时加载,这将大大减慢页面加载速度。
#兼容性
Occipital 与核心工作流程 100% 兼容,并兼容所有使用正确、推荐方式将资源排入 WordPress 网站的主题和插件。
未使用推荐钩子或直接将样式和脚本标签打印到页面标记中的代码与 Occipital 不兼容,而且无论如何他们都是在做错事。
#文档
##API 类
Occipital 随带了一个 API,简化了其使用,无需获取、实例化或深入了解包对象。API 类是 Brain\Assets
。
Occipital 中的所有操作都可以通过其 API 执行,例如作为静态方法:
Brain\Assets::addStyle( 'foo' );
addStyle
上面(就像其他所有 API 方法一样)不是一个“真正的”静态方法:API 类就像一个 代理,用于访问正确实例化的对象的正确方法。这与 Laravel facades 中的概念相同,但 Occipital 使用 Brain 模块,该模块是一个 Pimple 容器,具有一些 WordPress 特定的 糖分。
##引导
在能够使用任何 Occipital API 方法之前,我们必须确保它已完全加载。作为一个 Brain 模块,我们可以使用特定的 "brain_loaded"
钩子来完成此操作,但核心的 "init"
钩子也行。
此外,我们需要引导 Occipital,只需在 Composer 自动加载加载后即可。在使用 Occipital API 之前,您需要执行的所有操作都在这里:
<?php require_once '/path/to/vendor/autoload.php'; Brain\Occipital::boot(); add_action( 'brain_loaded', function() { // here go all the API methods } );
##添加资源
Occipital 支持在 3 个地方添加资源(样式和脚本):前端、后端和登录页面。
添加资源的主要 API 方法是:addStyle
和 addScript
。
这些方法的签名是:
addStyle( $handle, $args, $where )
$handle
是样式/脚本的 ID,它必须是唯一的,并且是唯一必需的参数。
$args
是资源参数的数组,更多关于这个 稍后在页面中。
$where
是您希望添加资源的具体位置,可以是
- "admin"(别名:"back","backend")
- "front"(别名:"frontend","public")
- "login"(别名:"register")
- "all"(别名:"*")
如果没有为 $where
提供任何内容,则假定“all”,因此资源将被添加到前端、后端和登录页面。
要在特定位置添加资产,有一些特定的方法
addFrontStyle
/addFrontScript
(用于前端)addAdminStyle
/addAdminScript
(用于后端)addLoginStyle
/addLoginScript
(用于登录页面)addSiteStyle
/addSiteScript
(用于站内资产,即任何地方)
###参数摘要
add*
方法的第二个参数是 $args
,一个数组,可以用来设置资产的全部参数。
以下是支持的键的完整列表
src
(字符串)资产完整URLdeps
(数组)资产依赖数组ver
(字符串|整数|空值)资产版本,NULL
表示不添加任何版本condition
(可调用函数)这是在添加资产之前运行的回调。如果回调返回一个假值,则不会添加资产。关于此参数的更多信息 请参阅页面后面的内容provide
(数组)被添加的资产中包含的资产数组。关于此参数的更多信息 请参阅页面后面的内容。media
(字符串)仅用于样式。CSS的“media”属性footer
(布尔值)仅用于脚本。如果为真,则脚本将在页脚中添加after
(字符串)仅用于样式。允许在CSS添加到页面后添加内联样式。要使用此参数,是调用wp_add_inline_style
的替代方法localizeData
(数组)仅用于脚本。设置一个要传递给javascript的数据对象。数组必须包含2个键:“name”用于javascript对象的名称,以及“data”用于数据本身。要使用此参数,是调用wp_localize_script
的替代方法
如你所见,大多数参数都与 wp_register_*
和 wp_enqueue_*
函数的核心参数相匹配,或者与其他核心函数(wp_add_inline_style
,wp_localize_script
)相匹配。
只有“condition”和“provide”是新参数,它们将在本页后面进一步解释。
###流畅接口
通过数组将配置参数传递给函数是WordPress世界中的一项非常常见的任务。然而,对我来说,也许其他人也是这样认为,一些流行的PHP框架所使用的流畅接口非常容易使用和阅读。这是Occipital支持此模式的主要原因(但不是唯一原因)。
基本上,每个参数都有一个setter方法(命名方式与参数数组中相关的键完全一致),并且每个setter方法都返回资产对象本身,允许以“链式”方式调用另一个setter方法(你了解jQuery吗?类似的东西)。
示例
<?php Brain\Assets::addFrontScript( 'my-script' ) ->src( "//example.com/path/to/script.js" ) ->deps( [ "jquery" ] ) ->provide( "bootstrap-tooltip", "lightbox", "plugin-script" ) ->ver( "20141007" ) ->footer( TRUE ) ->localizeData( [ 'name' => 'MyData', 'data' => [ 'foo' => 'bar' ] ] ) ->condition( function( $query, $user ) { return $query->is_front_page() && user_can( $user, 'edit_pages' ); });
localizeData
(或after
用于样式)可以多次调用,以向javascript发送更多对象,示例
<?php Brain\Assets::addFrontScript( 'my-script' ) ->src( "//example.com/path/to/script.js" ) ->localizeData( [ 'name' => 'MyData1', 'data' => [ 'id' => '1' ] ] ) ->localizeData( [ 'name' => 'MyData2', 'data' => [ 'id' => '2' ] ] ) ->localizeData( [ 'name' => 'MyData3', 'data' => [ 'id' => '3' ] ] );
请注意,setter方法不需要以“链式”方式调用,它们可以在任何时候调用资产对象的实例。
多亏了可以在代码的任何部分检索资产对象(如何实现将在本页后面解释),这种模式提供了“数组方式”无法提供的很多灵活性。
仅注意,在资产打印到页面之后调用setter没有任何意义,当然也不会产生任何效果。
###条件回调
很多时候,只有在特定条件下才应该添加资产,这就是WordPress中资产必须通过特定的操作钩子添加的主要原因:确保存在上下文以决定是否添加资产。
Occipital采用了不同的方法来解决该问题:它允许设置一个回调作为条件,并且只在正确的时机进行评估,无论资产何时添加到堆栈中。这样就可以避免在两个单独的函数wp_register_*
和wp_enqueue_*
中添加资产,还可以将上下文传递给条件回调,以方便开发者工作。
以下是一个前端条件示例
<?php $args['condition'] = function( WP_Query $query, $user ) { return $query->is_page( 'special_page' ) && user_can( $user, 'edit_pages') }
以及后端
<?php $args['condition'] = function( WP_Screen $screen, WP_User $user ) { return $screen->base === 'post' && user_can( $user, 'edit_pages') }
条件回调接收3个参数
- 第一个参数是前端请求中的主查询对象,以及后端中当前的
WP_Screen
对象,登录页面为FALSE
。 - 第二个参数是当前用户对象,如果没有用户登录则为
FALSE
。在登录页面上始终为FALSE
。 - 第三个参数是一个整数,用于内部识别当前的“侧面”(前端、后端或登录)。避免使用它,如果需要识别正确的上下文,请使用第一个参数。
###提供
提供允许设置一个数组,其中包含被添加的资产“包含”的资产,这可以避免WordPress加载这些文件,同时确保与第三方代码的兼容性。
WordPress资产的一个大问题是典型页面上HTTP请求的数量很高(你读过这篇博客文章吗?)。
如果一个用户安装了20个插件,其中一半添加了脚本和样式,并且主题添加了几个样式和脚本,则页面加载将需要24个HTTP请求来加载所有内容。
这相当疯狂。
Occipital的方法很简单:它允许网站所有者将连接的脚本和样式排队,并声明哪些资产包含在“大”文件中,以确保与其他任何代码的兼容性。
例如,假设在一个网站头部有
<link rel='stylesheet' id='open-sans-css' href='//fonts.googleapis.com/css?family=Open+Sans' type='text/css' media='all' /> <link rel='stylesheet' id='theme-style-css' href='//example.com/path/to/style.css' type='text/css' media='all' /> <link rel='stylesheet' id='plugin1-style-css' href='//example.com/path/to/plugin1.css' type='text/css' media='all' /> <link rel='stylesheet' id='plugin2-style-css' href='//example.com/path/to/plugin2.css' type='text/css' media='all' />
网站所有者可以使用Occipital做类似这样的事情
<?php Brain\Assets::addFrontStyle( 'mystyle' ) ->src( '//cdn.example.com/path/to/mystyle.css' ) ->provide([ 'open-sans', 'theme-style', 'plugin1-style', 'plugin2-style' ]);
然后,就像魔术一样,4个HTTP请求变成了1个来自CDN的请求。
当然,连接的样式不强制包含作为提供者声明的样式,实际上也不强制包含它们,这只是强制WordPress不添加它们的一种方式,即使其他代码会将它们排队或声明为依赖项。
例如,假设在同一网站上,所有者想要尝试一个插件,并且该插件在其代码中
<?php wp_enqueue_style( 'plugin3-style', $url, array( 'plugin1-style', 'thickbox') );
该插件中的样式将按预期排队,thickbox
将被添加,因为它被声明为依赖项,并且没有其他样式提供,但'plugin1-style'
将不会添加,因为它被声明为提供者。
####登录页面上的“提供”样式的问题
如果网站所有者使用provide
Occipital功能在自定义连接的文件中包含一些核心用于登录页面的样式(它们的句柄为'buttons'
、'open-sans'
、'dashicons'
、'login'
),则它将不起作用。原因是登录页面上的核心样式是通过直接调用wp_admin_css
打印的,该函数不能被过滤,并且运行在“login_enqueue_scripts”之前,因此任何使用该钩子添加的样式都会在核心样式已经打印时被考虑在内。对于登录页面中的自定义样式,provide
功能按预期工作。
###对开发者的重大警告
到目前为止,我总是说provide
功能应该由网站所有者使用。从未提到插件/主题开发者。
原因在于,插件/主题开发者在使用provide
功能时应非常谨慎。
假设一个插件使用了以下代码
<?php Brain\Assets::addFrontScript( 'pluginscript' ) ->src( $scripturl ) ->provide([ 'jquery', 'jquery-ui-core' ]);
这意味着插件添加的脚本包含了一个版本的jQuery和jQuery UI。
如果另一个插件再次提供相同的脚本会发生什么?在这种情况下,Occipital唯一能做的就是再次排队这些脚本,因为它无法从一个连接的文件中删除部分内容...
然而,开发者可以充分利用provide
功能来管理自己的插件资产。
让我们用一个例子来澄清。
假设一个插件有一个免费版本和高级插件。免费版本需要添加样式和脚本,如下所示
<?php Brain\Assets::addFrontStyle( 'awesome_plugin_free_style' ) ->src( $style_url ); Brain\Assets::addFrontScript( 'awesome_plugin_free_script' ) ->src( $script_url );
高级版本可以这样做
<?php Brain\Assets::addFrontStyle( 'awesome_plugin_premium_style' ) ->src( $style_url ) ->provide( [ 'awesome_plugin_free_style' ] ); Brain\Assets::addFrontScript( 'awesome_plugin_premium_script' ) ->src( $script_url ) ->provide( [ 'awesome_plugin_free_script' ] );
这样,高级插件的用户,而不是4个HTTP请求,只需要2个。太棒了,不是吗?
当然,插件安装的网站的拥有者可以将'awesome_plugin_premium_script'
嵌入到一个大型的连接文件中,与其他插件的脚本一起,但大多数用户不会这样做,对于插件来说,减少一半的HTTP请求总是好事。
另一个用例可以是包含从子主题到父主题的样式,避免在CSS中使用@import
,并确保与任何以父主题CSS为依赖项的样式兼容。
总之,开发者应该使用“provide”功能来减少自己插件或主题资产的HTTP请求,但绝不能用它来分发核心资产。
##删除资产
有时需要删除资产。在Occipital(目前)中,你只能删除使用Occipital添加的资产。
这通过removeStyle
和removeScript
API方法完成。
这些方法只接受一个参数,即资产句柄。当然,要删除一个资产,你需要确保添加它的回调已经运行。与WordPress不同,Occipital提供了一个特定的钩子,用于范围(或者说,4个钩子):"brain_assets_remove"
,在所有“方面”(前端、后端和登录页面)以及其他三个特定方面的钩子中触发。更多信息请参阅Occipital钩子段落。
##获取和设置资产属性
假设在插件代码的某个地方添加了一个资产,如下所示
<?php Brain\Assets::addAdminScript( 'awesome_script', [ 'src' => $script_url ] );
在(相同的插件、另一个插件、主题)任何地方都可以获取之前行添加的资产,并检索有关它的信息,如果需要也可以修改它。
这是通过两个API方法完成的:getStyle
和getScript
。
在下面的例子中,我将获取上面添加的脚本
- 我将更改其URL
- 我将添加一个条件,使得脚本不会在slug为'not-here'的页面上添加
这是Occipital的代码
<?php $script = Brain\Assets::getScript( 'awesome_script' ); $script->setSrc( str_replace( "example.com", "foo.com", $script->getSrc() ) ) ->setCondition( function( WP_Query $query ) { return ! $query->is_page( 'not-here' ); });
使用核心代码做同样的事情需要
<?php add_action( 'wp_print_scripts', function() { wp_dequeue_script( 'awesome_script' ); if ( is_page('not-here') ) { return; } global $wp_scripts; $script = $wp_scripts->registered['awesome_script']; $args = get_object_properties( $script ); $in_footer = isset( $args['extra']['group'] ) && $args['extra']['group']; wp_deregister_script( 'awesome_script' ); wp_enqueue_script( 'awesome_script', $args['src'], $args['deps'], $args['ver'], $in_footer ); }, 1 );
区别不仅仅是代码行的数量,区别还在于
- 可读性
- 在WordPress中获取相同结果需要使用具有未记录属性的全球变量,例如,你知道如果将
extra['group']
参数设置为1,脚本将在页脚打印出来吗?(即使你知道这一点,你肯定不是从文档中了解到的)。甚至我们访问的对象的类名为_WP_Dependency
,这表明它是一个“私有”类。相反,在Occipital中,一切都是使用表达性、公开和文档化的API完成的。 - 为了使它能在核心中工作,我们需要使用特定的钩子,而Occipital在时间方面提供了很多灵活性。
关于Occipital API,在上面的例子中有一个getSrc()
方法:它只是资产对象可用的getter之一,事实上,对于每个setter都有一个getter,因此我们有
setHandle()
--->getHandle()
setSrc()
--->getSrc()
setDeps()
--->getDeps()
setVer()
--->getVer()
setCondition()
--->getCondition()
setProvided()
--->getProvided()
setMedia()
--->getMedia()
(仅用于样式)setAfter()
--->getAfter()
(仅用于样式)setFooter()
--->getFooter()
(仅用于脚本)setLocalizeData()
--->getLocalizeData()
(仅用于脚本)
在 "流畅接口" 段落中,设置器的名称有所不同,这是因为所有设置器都有一个没有前缀 set
且首字母小写的 缩写别名。然而,在流畅接口中使用“完整”的设置器名称是完全合理的。
##资产类
在本页的不同部分,我提到了“资产类”和“资产对象”,实际上,在 Occipital 中,添加的每个资产都是一个对象(坦白说,就像在核心中一样)。
样式的默认类是 Brain\Occipital\Style
,脚本的默认类是 Brain\Occipital\Script
。
我说“默认”是因为开发者可以编写 2 个接口的自定义实现:Brain\Occipital\StyleInterface
和 Brain\Occipital\ScriptInterface
。
它们都扩展了 Brain\Occipital\EnqueuableInterface
,并且所有这些都在代码中用 phpDoc 注释进行了很好的文档说明。
###获取非 Occipital 资产的信息
如前一段所述,获取 WordPress 中入队资产的 信息既不简单也不直接。Occipital 提供了一种从任何非 Occipital 注册资产创建 Occipital 资产对象的方法,这样就可以使用 Occipital 获取器来获取信息。
这是通过实例化一个 Occipital 资产对象 并调用其上的 fillFromRegistered()
方法来实现的。
例如,某处有一段如下所示的代码
<?php wp_register_script( 'foo', 'http://example.com/path/to/script.js', array( 'jQuery' ), '20141008', TRUE ); wp_localize_script( 'foo', 'FooData', array( 'foo'=>'bar', 'bar'=>'baz' ) );
安装 Occipital 后,我们可以
<?php $script = new Brain\Occipital\Script; $script->fillFromRegistered( 'foo' ); $data = $script->getLocalizeData();
$data
将是一个包含单个元素的 数组:一个简单的对象 (stdClass
),具有 2 个属性
name
将等于"FooData"
data
将等于array( 'foo'=>'bar', 'bar'=>'baz' )
它是一个数组的原因是 wp_localize_script
(就像 Occipital 中的 setLocalizeData
)可以在脚本处理器上多次调用,在这种情况下,数组将包含多个对象。
我可以保证,使用核心函数做同样的事情 并不容易:例如,你知道 WP 将脚本的本地化数据存储在 字符串 形式中(如果有多个 wp_localize_script
脚本,则以换行符分隔)吗?
在当前 Occipital 版本中,只能使用获取器(任何设置器都会更改创建的对象上的属性,但不会影响入队资产的行为)。
##错误处理
所有 add*
API 方法都返回一个资产对象(见上一段),它可以用于所有目的:(调试、获取信息、编辑...)。
当然,所有“链式”设置器方法都返回相同的实例(在内部,它们返回 $this
)。
然而,当出现错误时,任何方法都可能返回一个 WP_Error
对象。
你可能合理地认为,在使用 流畅接口 时,如果方法链的开始或中间返回一个错误对象,则下一个方法将导致致命错误:但是,这并不正确。
这个小“魔法”是通过一个扩展 WP_Error
的自定义错误类来实现的,使其“链式”:每次调用不存在的方法时,都会将错误消息添加到对象错误堆栈中(WP_Error
支持多个错误),然后返回对象本身。
由于自定义错误类扩展了 WP_Error
,因此可以通过 is_wp_error
进行检查,并且可以运行其所有方法。
##枕部钩子
枕部触发了一些自定义动作钩子,它们可以分为两组:“就绪钩子”和“打印钩子”。
###就绪钩子
它们是
"brain_assets_ready_front"
"brain_assets_ready_admin"
"brain_assets_ready_login"
"brain_assets_ready"
"brain_assets_remove_front"
"brain_assets_remove_admin"
"brain_assets_remove_login"
"brain_assets_remove"
"brain_assets_done"
前三个与WordPress的 "wp_enqueue_scripts"
、"admin_enqueue_scripts"
和 "login_enqueue_scripts"
相对应。然而,当使用枕部API时,可以在从 init
到 wp_print_styles
(资产即将打印时)的任何时间添加资产,因此不需要使用这些钩子之一。它们存在的原因是,所有这些(就像运行在前面三个之前的通用 "brain_assets_ready"
)都将资产容器类的实例 Brain\Occipital\Container
传递给钩子回调,这可以用来添加钩子,而无需使用API和其他(目前未记录的)高级操作。
"brain_assets_remove"
和三个 "brain_assets_remove_*"
钩子,正如预期的那样,可以用来删除添加的资产。实际上,要删除一个资产,我们需要确保添加它的函数已经处理过。在WordPress中,通常,一个安全的地方是 wp_print_styles
,枕部提供了特定的钩子来处理这个范围。通用的 "brain_assets_remove"
在任何地方都可用,此外,还有一个钩子用于任何受支持的“侧面”。
系列中的最后一个钩子,"brain_assets_done"
在队列过程完成后触发。它主要用于内部,但它是获取有关已队列资产信息的安全地方,例如,当这个钩子触发时,已经丢弃了条件回调返回了false值的资产。
###打印钩子
它们是
"brain_doing_style"
"brain_style_done"
"brain_doing_script"
"brain_script_done"
"brain_doing_style"
在将样式 <link>
标签打印到页面之前立即触发,而 "brain_style_done"
在之后立即触发。
"brain_doing_script"
在将脚本 <script>
标签打印到页面之前立即触发,而 "brain_script_done"
在之后立即触发。
请注意,WordPress没有提供类似的功能,有时可能需要这样的功能,例如,在添加脚本后立即打印一个无冲突调用。
##许可证
枕部的代码根据 GPLv2+ 许可。通过Composer安装代码,来自