详解 WordPress `WP_Term` 类的源码:它如何封装分类术语数据并提供属性访问。

大家好,欢迎来到今天的“解剖WordPress”系列讲座!今天我们要深入探讨WordPress世界中一个至关重要的角色——WP_Term 类。别害怕,虽然名字听起来有点技术范儿,但其实它就像一个勤劳的图书管理员,负责管理和提供我们网站上的各种“标签”和“分类”。

好,让我们开始吧!

WP_Term:术语数据的“容器”

首先,我们需要明确一点:在WordPress中,分类法(Taxonomies)是组织内容的一种方式。常见的分类法包括类别(Categories)和标签(Tags)。而术语(Terms)则是分类法下的具体值。例如,“科技”是一个类别,“WordPress”是一个标签。

WP_Term 类就是用来封装这些术语的数据的。它就像一个容器,把一个术语的所有信息都装进去,方便我们访问和操作。

源码探索:从构造函数开始

我们先来看看 WP_Term 类的构造函数,这通常是了解一个类如何工作的第一步。

/**
 * WP_Term class.
 *
 * @since 4.4.0
 */
class WP_Term {

    /**
     * Term ID.
     *
     * @since 4.4.0
     * @access public
     * @var int
     */
    public $term_id;

    /**
     * Term name.
     *
     * @since 4.4.0
     * @access public
     * @var string
     */
    public $name;

    /**
     * Term slug.
     *
     * @since 4.4.0
     * @access public
     * @var string
     */
    public $slug;

    /**
     * Term group.
     *
     * @since 4.4.0
     * @access public
     * @var int
     */
    public $term_group;

    /**
     * Term taxonomy ID.
     *
     * @since 4.4.0
     * @access public
     * @var int
     */
    public $term_taxonomy_id;

    /**
     * Taxonomy name.
     *
     * @since 4.4.0
     * @access public
     * @var string
     */
    public $taxonomy;

    /**
     * Term description.
     *
     * @since 4.4.0
     * @access public
     * @var string
     */
    public $description;

    /**
     * Term parent ID.
     *
     * @since 4.4.0
     * @access public
     * @var int
     */
    public $parent;

    /**
     * Term count.
     *
     * @since 4.4.0
     * @access public
     * @var int
     */
    public $count;

    /**
     * Object cache group.
     *
     * @since 4.4.0
     * @access public
     * @var string
     */
    public $filter = 'raw';

    /**
     * Constructor.
     *
     * @since 4.4.0
     *
     * @param WP_Term|object|int|null $term Term to initialise the WP_Term object with.
     */
    public function __construct( $term ) {
        if ( is_a( $term, 'WP_Term' ) ) {
            $this->init( $term->to_array() );
            return;
        }

        if ( is_object( $term ) || is_array( $term ) ) {
            $this->init( $term );
            return;
        }

        if ( is_numeric( $term ) ) {
            $term = get_term( (int) $term );
            if ( $term ) {
                $this->init( $term );
            }
        }
    }

    /**
     * Initialises term properties.
     *
     * @since 4.4.0
     * @access protected
     *
     * @param WP_Term|object|array $term Term to initialise the WP_Term object with.
     */
    protected function init( $term ) {
        foreach ( get_object_vars( $this ) as $key => $value ) {
            if ( isset( $term->$key ) ) {
                $this->$key = $term->$key;
            } elseif ( isset( $term[ $key ] ) ) {
                $this->$key = $term[ $key ];
            }
        }

        $this->filter = 'raw';
    }

    /**
     * Sanitizes term fields.
     *
     * @since 4.4.0
     * @access public
     */
    public function sanitize() {
        $this->name        = sanitize_term_field( 'name', $this->name, $this->term_id, $this->taxonomy, 'raw' );
        $this->slug        = sanitize_term_field( 'slug', $this->slug, $this->term_id, $this->taxonomy, 'raw' );
        $this->description = sanitize_term_field( 'description', $this->description, $this->term_id, $this->taxonomy, 'raw' );
        $this->filter      = 'display';
    }

    /**
     * Returns the term as an array.
     *
     * @since 4.4.0
     * @access public
     *
     * @return array Term as an array.
     */
    public function to_array() {
        $term = get_object_vars( $this );

        unset( $term['filter'] );

        return $term;
    }

    /**
     * __get magic method.
     *
     * @since 4.4.0
     * @access public
     *
     * @param string $key Key to get.
     * @return mixed Maybe filtered term field.
     */
    public function __get( $key ) {
        if ( 'filter' === $key ) {
            return $this->filter;
        }

        if ( in_array( $key, array( 'name', 'slug', 'description' ), true ) ) {
            return sanitize_term_field( $key, $this->$key, $this->term_id, $this->taxonomy, $this->filter );
        }

        return $this->$key;
    }

    /**
     * __isset magic method.
     *
     * @since 4.4.0
     * @access public
     *
     * @param string $key Key to check.
     * @return bool Whether the key exists.
     */
    public function __isset( $key ) {
        if ( 'filter' === $key ) {
            return true;
        }

        return isset( $this->$key );
    }
}

让我们分解一下:

  • 属性(Public Properties):

    • $term_id: 术语的ID,就像图书的唯一编号。
    • $name: 术语的名称,例如“科技”或“WordPress”。
    • $slug: 术语的别名,通常用于URL中,例如“technology”或“wordpress”。
    • $term_group: 术语组,通常不用。
    • $term_taxonomy_id: 术语分类ID,关联术语和分类法。
    • $taxonomy: 分类法的名称,例如“category”或“post_tag”。
    • $description: 术语的描述,就像图书的简介。
    • $parent: 父级术语的ID,用于层级分类法。
    • $count: 使用该术语的文章数量,就像图书的借阅次数。
    • $filter: 控制如何过滤术语的字段,默认为raw
  • 构造函数 __construct(): 这个函数负责初始化 WP_Term 对象。它可以接受一个 WP_Term 对象、一个对象、一个数组或一个术语ID作为参数。它会根据传入的参数类型,调用 init() 方法来填充对象的属性。

  • 初始化方法 init(): 这个方法接受一个数组或对象,然后遍历 WP_Term 类的所有属性,如果传入的数组或对象中存在相应的键或属性,就将它们的值赋给 WP_Term 对象的属性。

例子:创建一个 WP_Term 对象

假设我们有一个术语,它的ID是 123,名称是 "JavaScript",Slug 是 "javascript", Taxonomy 是 "category"。我们可以这样创建一个 WP_Term 对象:

$term_data = (object) array(
    'term_id' => 123,
    'name' => 'JavaScript',
    'slug' => 'javascript',
    'taxonomy' => 'category'
);

$term = new WP_Term( $term_data );

echo $term->name; // 输出:JavaScript
echo $term->term_id; // 输出:123

或者,如果已经有了一个术语ID,可以直接这样创建:

$term_id = 123;
$term = new WP_Term( $term_id );

if ($term && !is_wp_error($term)) {
    echo $term->name; // 输出:JavaScript
    echo $term->term_id; // 输出:123
} else {
    echo "Term not found or an error occurred.";
}

属性访问:直接访问与 __get() 方法

一旦我们创建了 WP_Term 对象,就可以直接访问它的属性,例如 $term->name$term->slug 等。

但是,WP_Term 类还使用了一个魔术方法 __get(),它会在我们尝试访问不存在的属性时被调用。在这个方法中,WordPress会对某些属性(如 nameslugdescription)进行过滤,以确保输出的内容是安全的。

    public function __get( $key ) {
        if ( 'filter' === $key ) {
            return $this->filter;
        }

        if ( in_array( $key, array( 'name', 'slug', 'description' ), true ) ) {
            return sanitize_term_field( $key, $this->$key, $this->term_id, $this->taxonomy, $this->filter );
        }

        return $this->$key;
    }

这里的 sanitize_term_field() 函数非常重要,它负责对术语的字段进行清理和过滤,以防止XSS攻击。$this->filter 变量决定了过滤的级别。默认情况下,$filter 的值为 raw,表示不进行任何过滤。但是,当我们调用 sanitize() 方法时,$filter 的值会被设置为 display,表示进行显示级别的过滤。

sanitize() 方法:安全卫士

sanitize() 方法用于清理和过滤术语的字段,以确保它们是安全的。

    public function sanitize() {
        $this->name        = sanitize_term_field( 'name', $this->name, $this->term_id, $this->taxonomy, 'raw' );
        $this->slug        = sanitize_term_field( 'slug', $this->slug, $this->term_id, $this->taxonomy, 'raw' );
        $this->description = sanitize_term_field( 'description', $this->description, $this->term_id, $this->taxonomy, 'raw' );
        $this->filter      = 'display';
    }

这个方法会调用 sanitize_term_field() 函数,对 nameslugdescription 字段进行清理和过滤。同时,它还会将 $filter 的值设置为 display,以便在后续访问这些字段时,进行显示级别的过滤。

to_array() 方法:转换为数组

to_array() 方法用于将 WP_Term 对象转换为数组。

    public function to_array() {
        $term = get_object_vars( $this );

        unset( $term['filter'] );

        return $term;
    }

这个方法会使用 get_object_vars() 函数获取 WP_Term 对象的所有属性,并将它们转换为一个数组。然后,它会从数组中移除 filter 属性,并返回最终的数组。

__isset() 方法:检查属性是否存在

__isset() 方法是一个魔术方法,用于检查一个属性是否存在。

    public function __isset( $key ) {
        if ( 'filter' === $key ) {
            return true;
        }

        return isset( $this->$key );
    }

这个方法会检查 $key 是否为 filter。如果是,则返回 true。否则,它会使用 isset() 函数检查 $this->$key 是否存在,并返回结果。

实际应用:获取分类术语信息

现在,让我们看看如何在实际应用中使用 WP_Term 类。假设我们要获取ID为 5 的分类术语的信息:

$term = get_term( 5, 'category' );

if ( $term && ! is_wp_error( $term ) ) {
    echo 'Term ID: ' . $term->term_id . '<br>';
    echo 'Term Name: ' . $term->name . '<br>';
    echo 'Term Slug: ' . $term->slug . '<br>';
    echo 'Term Description: ' . $term->description . '<br>';
    echo 'Term Link: ' . get_term_link( $term ) . '<br>';
} else {
    echo 'Term not found!';
}

在这个例子中,我们首先使用 get_term() 函数获取ID为 5 的分类术语。get_term() 函数会返回一个 WP_Term 对象。然后,我们可以直接访问 WP_Term 对象的属性,例如 term_idnameslugdescription

总结:WP_Term 的核心作用

总而言之,WP_Term 类在 WordPress 中扮演着以下核心角色:

  • 封装术语数据: 它将术语的所有信息(ID、名称、别名、描述等)封装在一个对象中,方便我们访问和操作。
  • 提供属性访问: 我们可以直接访问 WP_Term 对象的属性,获取术语的信息。
  • 进行数据过滤: 通过 __get() 方法和 sanitize_term_field() 函数,WP_Term 类可以对术语的字段进行过滤,以确保输出的内容是安全的。
  • 转换为数组: 通过 to_array() 方法,我们可以将 WP_Term 对象转换为数组,方便进行数据处理。

表格总结

属性/方法 描述
$term_id 术语的ID。
$name 术语的名称。
$slug 术语的别名。
$taxonomy 分类法的名称。
$description 术语的描述。
__construct() 构造函数,用于创建 WP_Term 对象。
init() 初始化方法,用于填充 WP_Term 对象的属性。
sanitize() 清理和过滤术语的字段,以确保它们是安全的。
to_array() WP_Term 对象转换为数组。
__get() 魔术方法,在访问不存在的属性时被调用,用于对某些属性进行过滤。
__isset() 魔术方法,用于检查一个属性是否存在。

补充说明:对象缓存

值得一提的是,WordPress 使用对象缓存来提高性能。当我们第一次获取一个术语时,WordPress 会将 WP_Term 对象存储在对象缓存中。当我们再次获取同一个术语时,WordPress 会直接从对象缓存中获取,而无需再次查询数据库。这可以显著提高网站的性能。

WP_Term 类本身不直接处理对象缓存,而是依赖于 WordPress 的核心函数,例如 get_term()get_term() 函数会负责从对象缓存中获取术语,或者在对象缓存中不存在时,从数据库中获取并存储到对象缓存中。

总结

WP_Term 类是 WordPress 中一个重要的类,它负责封装和管理分类术语的数据。通过了解 WP_Term 类的源码,我们可以更好地理解 WordPress 的工作原理,并能够更有效地开发 WordPress 插件和主题。

希望今天的讲座对你有所帮助!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注