各位老铁,大家好!我是今天的主讲人,咱们今天来聊聊 WordPress 源码里一个挺有意思的小家伙:_wp_filter_build_unique_id()
。 别看它名字长,其实干的活儿挺简单,就是给 WordPress 钩子(Hook)生成一个独一无二的 ID。
为啥要给钩子生成唯一 ID 呢? 这就涉及到 WordPress 插件和主题机制的核心了。你想啊,成百上千的插件和主题,都想往同一个钩子上挂载自己的函数,如果没有一个唯一 ID 来区分,那不就乱套了吗?
所以,这个函数的作用,就是确保每个挂载到钩子上的函数都有一个独一无二的身份证,方便 WordPress 内部进行管理和调用。
咱们废话不多说,直接撸代码!
/**
* Generates a unique function ID.
*
* @access private
*
* @param string $tag The name of the filter to hook the $function_to_add to.
* @param callable $function_to_add Function to be called when the filter is applied.
* @param int $priority Used to specify the order in which the functions associated with a
* particular action are executed. Lower numbers correspond with earlier
* execution, and functions with the same priority are executed in the
* order in which they were added to the action.
*
* @return string A unique function ID.
*/
function _wp_filter_build_unique_id( $tag, $function_to_add, $priority ) {
static $filter_id_count = 0;
if ( is_string( $function_to_add ) ) {
return $function_to_add;
}
if ( is_object( $function_to_add ) ) {
// Closures are currently implemented as objects.
$function_to_add = array( $function_to_add, '' );
} else {
$function_to_add = (array) $function_to_add;
}
if ( is_object( $function_to_add[0] ) ) {
// Object class calling.
if ( function_exists( 'spl_object_hash' ) ) {
return spl_object_hash( $function_to_add[0] ) . $function_to_add[1];
} else {
$obj_idx = get_class( $function_to_add[0] ) . $function_to_add[1];
if ( ! isset( $function_to_add[0]->wp_filter_id ) ) {
$filter_id_count ++;
$function_to_add[0]->wp_filter_id = $filter_id_count;
}
return $obj_idx . $function_to_add[0]->wp_filter_id;
}
} elseif ( is_string( $function_to_add[0] ) ) {
// Static Calling.
return $function_to_add[0] . '::' . $function_to_add[1];
}
}
咱们一行一行地拆解:
1. 函数声明和文档注释:
/**
* Generates a unique function ID.
*
* @access private
*
* @param string $tag The name of the filter to hook the $function_to_add to.
* @param callable $function_to_add Function to be called when the filter is applied.
* @param int $priority Used to specify the order in which the functions associated with a
* particular action are executed. Lower numbers correspond with earlier
* execution, and functions with the same priority are executed in the
* order in which they were added to the action.
*
* @return string A unique function ID.
*/
function _wp_filter_build_unique_id( $tag, $function_to_add, $priority ) {
@access private
: 说明这个函数是 WordPress 内部使用的,不建议开发者直接调用。$tag
: 钩子的名称,比如 ‘wp_head’ 或者 ‘the_content’。$function_to_add
: 要挂载到钩子上的函数。它可以是函数名字符串、数组(用于类方法或静态方法)或者匿名函数(闭包)。$priority
: 优先级,数值越小,优先级越高。@return string
: 返回生成的唯一 ID。
2. 静态变量:
static $filter_id_count = 0;
static $filter_id_count = 0;
: 这是一个静态变量,用于记录已经生成的 ID 数量。 静态变量的特点是,它的值在函数多次调用之间会被保留。 这个变量主要用于处理一些特殊情况,咱们后面会讲到。
3. 处理函数名是字符串的情况:
if ( is_string( $function_to_add ) ) {
return $function_to_add;
}
is_string( $function_to_add )
: 如果$function_to_add
是一个字符串,说明直接传递的是函数名。return $function_to_add;
: 直接返回函数名作为唯一 ID。 这种情况下,WordPress 认为函数名本身就足够唯一了。
举个例子:
add_filter( 'the_content', 'my_content_filter' ); // 函数名是字符串
在这种情况下,_wp_filter_build_unique_id()
会直接返回 'my_content_filter'
。
4. 处理匿名函数 (闭包) 的情况:
if ( is_object( $function_to_add ) ) {
// Closures are currently implemented as objects.
$function_to_add = array( $function_to_add, '' );
} else {
$function_to_add = (array) $function_to_add;
}
is_object( $function_to_add )
: 如果$function_to_add
是一个对象,通常意味着它是一个匿名函数 (闭包)。 在 PHP 中,闭包被实现为对象。$function_to_add = array( $function_to_add, '' );
: 将闭包对象包装成一个数组, 数组的第一个元素是闭包对象,第二个元素是一个空字符串。 这是为了统一处理类方法和静态方法的情况。else { $function_to_add = (array) $function_to_add; }
: 如果不是对象,则强制转换为数组,用于处理类方法和静态方法。
举个例子:
add_filter( 'the_content', function( $content ) { // 匿名函数
return $content . ' Added by anonymous function';
} );
在这种情况下,$function_to_add
是一个 Closure
对象,会被转换成 array( $closure_object, '' )
。
5. 处理类方法的情况:
if ( is_object( $function_to_add[0] ) ) {
// Object class calling.
if ( function_exists( 'spl_object_hash' ) ) {
return spl_object_hash( $function_to_add[0] ) . $function_to_add[1];
} else {
$obj_idx = get_class( $function_to_add[0] ) . $function_to_add[1];
if ( ! isset( $function_to_add[0]->wp_filter_id ) ) {
$filter_id_count ++;
$function_to_add[0]->wp_filter_id = $filter_id_count;
}
return $obj_idx . $function_to_add[0]->wp_filter_id;
}
}
is_object( $function_to_add[0] )
: 如果$function_to_add[0]
是一个对象,说明调用的是一个类的方法。function_exists( 'spl_object_hash' )
: 检查spl_object_hash()
函数是否存在。 这个函数是 PHP SPL 扩展提供的,用于生成对象的唯一哈希值。return spl_object_hash( $function_to_add[0] ) . $function_to_add[1];
: 如果spl_object_hash()
存在,就使用它生成对象的唯一哈希值,并和方法名$function_to_add[1]
拼接起来作为唯一 ID。spl_object_hash
能保证在脚本运行期间,同一个对象的哈希值是不变的。else { ... }
: 如果spl_object_hash()
不存在(比如 PHP 版本太老),就使用一种备用的方法。$obj_idx = get_class( $function_to_add[0] ) . $function_to_add[1];
: 获取类名和方法名,拼接起来。if ( ! isset( $function_to_add[0]->wp_filter_id ) ) { ... }
: 检查对象是否已经有一个wp_filter_id
属性。 这个属性是用来保存一个自增的 ID 的,目的是为了在没有spl_object_hash()
的情况下,尽可能保证唯一性。$filter_id_count ++;
: 自增静态变量$filter_id_count
。$function_to_add[0]->wp_filter_id = $filter_id_count;
: 将自增的 ID 赋值给对象的wp_filter_id
属性。
return $obj_idx . $function_to_add[0]->wp_filter_id;
: 将类名、方法名和自增的 ID 拼接起来作为唯一 ID。
举个例子:
class MyClass {
public function my_method( $content ) {
return $content . ' Added by class method';
}
}
$my_object = new MyClass();
add_filter( 'the_content', array( $my_object, 'my_method' ) ); // 类方法
在这种情况下,$function_to_add
是 array( $my_object, 'my_method' )
。 如果 spl_object_hash()
存在,则返回类似 4e0fb1a60e02a::my_method
的 ID。 如果 spl_object_hash()
不存在,则返回类似 MyClassmy_method1
的 ID (假设这是第一次给这个类的对象分配 ID)。
6. 处理静态方法的情况:
elseif ( is_string( $function_to_add[0] ) ) {
// Static Calling.
return $function_to_add[0] . '::' . $function_to_add[1];
}
is_string( $function_to_add[0] )
: 如果$function_to_add[0]
是一个字符串,说明调用的是一个静态方法。return $function_to_add[0] . '::' . $function_to_add[1];
: 将类名和方法名用::
拼接起来作为唯一 ID。
举个例子:
class MyClass {
public static function my_static_method( $content ) {
return $content . ' Added by static method';
}
}
add_filter( 'the_content', array( 'MyClass', 'my_static_method' ) ); // 静态方法
在这种情况下,$function_to_add
是 array( 'MyClass', 'my_static_method' )
, 返回的唯一 ID 是 MyClass::my_static_method
。
总结一下, _wp_filter_build_unique_id()
函数的生成逻辑可以概括为以下表格:
$function_to_add 类型 |
生成 ID 的方式 | 例子 |
---|---|---|
字符串 (函数名) | 直接返回函数名。 | add_filter( 'the_content', 'my_content_filter' ); 返回 'my_content_filter' |
闭包 (匿名函数) | 将闭包对象包装成数组 array( $closure_object, '' ) , 然后按照类方法的方式处理 (使用 spl_object_hash 或者自增 ID)。 |
add_filter( 'the_content', function( $content ) { ... } ); 返回类似 4e0fb1a60e02a 的哈希值 |
类方法 | 如果 spl_object_hash() 存在,使用它生成对象的唯一哈希值,并和方法名拼接起来。 如果 spl_object_hash() 不存在,则使用类名、方法名和自增 ID 拼接起来。 |
add_filter( 'the_content', array( $my_object, 'my_method' ) ); 返回类似 MyClassmy_method1 |
静态方法 | 将类名和方法名用 :: 拼接起来。 |
add_filter( 'the_content', array( 'MyClass', 'my_static_method' ) ); 返回 'MyClass::my_static_method' |
一些需要注意的点:
spl_object_hash()
的重要性:spl_object_hash()
函数是生成对象唯一 ID 的首选方法,因为它能保证在脚本运行期间,同一个对象的哈希值是不变的。 如果 PHP 版本支持,一定要使用它。- 自增 ID 的局限性: 在没有
spl_object_hash()
的情况下,使用自增 ID 只能在一定程度上保证唯一性。 如果同一个类的多个对象都挂载到同一个钩子上,并且在同一个请求中,那么它们的自增 ID 可能会重复。 虽然这种情况比较少见,但还是需要注意。 - 钩子的优先级:
_wp_filter_build_unique_id()
函数的$priority
参数并没有被使用到。 钩子的优先级是在另一个地方进行管理的,这个函数只负责生成唯一 ID。
为什么要区分类方法和静态方法?
主要原因是为了保证 ID 的唯一性。 静态方法属于类本身,而类方法属于类的实例对象。 如果不区分,可能会导致不同的实例对象的方法,生成相同的 ID,从而引起冲突。
实际应用场景:
_wp_filter_build_unique_id()
函数在 WordPress 内部被广泛使用, 主要是在 add_filter()
和 remove_filter()
函数中。
add_filter()
: 在添加钩子时,使用_wp_filter_build_unique_id()
生成唯一 ID,并将函数和 ID 存储在全局的$wp_filter
数组中。remove_filter()
: 在移除钩子时,使用_wp_filter_build_unique_id()
生成要移除的函数的唯一 ID,然后在$wp_filter
数组中找到对应的函数并移除。
总结:
_wp_filter_build_unique_id()
函数虽然看起来很简单,但它在 WordPress 钩子机制中扮演着重要的角色。 它通过不同的策略,为不同的函数类型生成唯一 ID,保证了插件和主题之间的兼容性和稳定性。
希望今天的讲解能帮助大家更好地理解 WordPress 的钩子机制, 也能对大家在开发 WordPress 插件和主题时有所启发。
今天的讲座就到这里,谢谢大家! 如果有什么问题,欢迎留言讨论。