分析 WordPress `add_option()` 函数源码:与 `wp_options` 表的交互与缓存机制。

各位听众朋友们,大家好!我是今天的主讲人,咱们今天来扒一扒 WordPress 里一个默默无闻但又举足轻重的函数—— add_option(),看看它到底是怎么跟数据库里的 wp_options 表眉来眼去,以及它背后的缓存机制是怎么运作的。

准备好了吗?咱们这就开始!

add_option():初识庐山真面目

add_option() 函数,顾名思义,就是用来添加一个选项(option)到 WordPress 的选项数据库中。这个数据库,实际上就是咱们熟悉的 wp_options 表。这个表就像一个巨大的键值对存储,WordPress 用它来保存各种各样的设置,比如站点标题、URL、主题设置等等。

先来看一下 add_option() 函数的原型:

function add_option( string $name, mixed $value = '', string $deprecated = '', string $autoload = 'yes' ): bool {
  global $wpdb, $wp_suspend_cache_addition;

  // 省略代码...
}

参数解释:

  • $name:选项的名字,必须是唯一的,就像人的名字一样,不能重名。
  • $value:选项的值,可以是字符串、数字、数组、对象,你想存什么都可以。
  • $deprecated:已弃用的参数,别管它。
  • $autoload:是否自动加载。如果设置为 'yes',WordPress 会在每次加载时自动加载这个选项,提高访问速度。如果设置为 'no',只有在需要的时候才会加载。

返回值:

  • true:成功添加选项。
  • false:添加失败,通常是因为选项已经存在。

add_option() 的幕后英雄:wp_options

wp_options 表是 add_option() 函数的舞台。这个表结构通常如下:

字段名 数据类型 描述
option_id bigint(20) unsigned 主键,自增长,唯一标识一个选项。
option_name varchar(191) 选项的名字,就是咱们在 add_option() 中传入的 $name 参数。
option_value longtext 选项的值,就是咱们在 add_option() 中传入的 $value 参数。
autoload varchar(20) 是否自动加载,就是咱们在 add_option() 中传入的 $autoload 参数,值为 'yes''no'

add_option() 函数的主要任务就是往这张表里插入数据。

源码解读:add_option() 的内部运作

接下来,咱们深入 add_option() 的源码,看看它到底是怎么工作的:

function add_option( string $name, mixed $value = '', string $deprecated = '', string $autoload = 'yes' ): bool {
  global $wpdb, $wp_suspend_cache_addition;

  // 1. 检查选项名字是否为空
  if ( empty( $name ) ) {
    return false;
  }

  // 2. 检查选项是否已经存在
  if ( get_option( $name ) !== false ) {
    return false;
  }

  // 3. 序列化选项值
  $value = maybe_serialize( $value );

  // 4. 执行数据库插入操作
  $autoload = ( 'no' === $autoload ) ? 'no' : 'yes';
  $result   = $wpdb->insert(
    $wpdb->options,
    array(
      'option_name'  => $name,
      'option_value' => $value,
      'autoload'   => $autoload,
    ),
    array(
      '%s',
      '%s',
      '%s',
    )
  );

  // 5. 如果插入成功,则将选项添加到缓存中
  if ( $result ) {
    if ( ! isset( $wp_suspend_cache_addition ) || ! $wp_suspend_cache_addition ) {
      wp_cache_add( $name, $value, 'options' );
    }

    /**
     * Fires after an option is successfully added.
     *
     * @since 2.0.0
     *
     * @param string $option Name of the option to add.
     * @param mixed  $value  Value of the option.
     */
    do_action( 'add_option', $name, $value );

    return true;
  }

  return false;
}

代码分析:

  1. 参数检查: 首先,函数会检查选项的名字 $name 是否为空。如果为空,直接返回 false,因为没有名字的选项就像没有名字的人一样,是无法存在的。

  2. 检查选项是否已经存在: 使用 get_option() 函数来检查选项是否已经存在。如果存在,也返回 false,因为同一个名字的选项只能有一个,就像一个人不能有两个身份证号一样。

    if ( get_option( $name ) !== false ) {
        return false;
    }

    get_option() 函数的作用是从数据库或缓存中获取选项的值。后面咱们会详细讲解 get_option() 函数。

  3. 序列化选项值: 使用 maybe_serialize() 函数对选项的值 $value 进行序列化。这是因为 wp_options 表的 option_value 字段是 longtext 类型,只能存储字符串。如果 $value 是数组或对象,就需要先将其序列化成字符串才能存储到数据库中。

    $value = maybe_serialize( $value );

    maybe_serialize() 函数的源码如下:

    function maybe_serialize( $data ) {
        if ( is_array( $data ) || is_object( $data ) ) {
            return serialize( $data );
        }
    
        if ( is_serialized( $data ) ) {
            return serialize( $data );
        }
    
        return $data;
    }

    这个函数会判断 $data 是否是数组或对象,如果是,就使用 serialize() 函数将其序列化成字符串。如果 $data 已经是序列化的字符串,也会再次序列化,以确保其格式正确。

  4. 执行数据库插入操作: 使用 $wpdb->insert() 方法将选项插入到 wp_options 表中。

    $autoload = ( 'no' === $autoload ) ? 'no' : 'yes';
    $result   = $wpdb->insert(
        $wpdb->options,
        array(
            'option_name'  => $name,
            'option_value' => $value,
            'autoload'   => $autoload,
        ),
        array(
            '%s',
            '%s',
            '%s',
        )
    );

    这里,$wpdb 是 WordPress 的数据库操作对象,$wpdb->options 表示 wp_options 表。$wpdb->insert() 方法接受三个参数:

    • 表名:$wpdb->options
    • 要插入的数据:一个关联数组,键是字段名,值是要插入的值。
    • 数据类型:一个数组,指定每个字段的数据类型。%s 表示字符串。
  5. 添加到缓存: 如果数据库插入操作成功,就使用 wp_cache_add() 函数将选项添加到缓存中。

    if ( $result ) {
        if ( ! isset( $wp_suspend_cache_addition ) || ! $wp_suspend_cache_addition ) {
            wp_cache_add( $name, $value, 'options' );
        }
    
        // ...
    }

    wp_cache_add() 函数用于将数据添加到 WordPress 的对象缓存中。对象缓存是一种内存缓存机制,可以显著提高 WordPress 的性能。

    wp_cache_add() 函数接受三个参数:

    • $key:缓存的键,这里是选项的名字 $name
    • $data:要缓存的数据,这里是选项的值 $value
    • $group:缓存组,这里是 'options'

    $wp_suspend_cache_addition 是一个全局变量,用于临时禁用缓存添加操作。

  6. 触发 Action Hook: 成功添加选项后,会触发一个 Action Hook add_option,允许其他插件或主题执行自定义操作。

    do_action( 'add_option', $name, $value );

    Action Hook 是 WordPress 提供的一种扩展机制,允许开发者在特定的事件发生时执行自定义代码。

  7. 返回结果: 最后,函数返回 true 表示添加成功,否则返回 false

缓存机制:加速访问,提升性能

add_option() 函数的源码中,咱们看到了 wp_cache_add() 函数。这个函数是 WordPress 缓存机制的关键。为了理解 add_option() 函数的缓存机制,咱们需要了解以下几个概念:

  • 对象缓存(Object Cache): WordPress 的对象缓存是一种内存缓存机制,用于缓存数据库查询结果和其他计算密集型操作的结果。它可以显著提高 WordPress 的性能,减少数据库的负载。
  • 缓存组(Cache Group): 对象缓存可以分为多个缓存组,每个缓存组用于存储特定类型的数据。add_option() 函数使用的缓存组是 'options',用于存储选项数据。
  • 缓存键(Cache Key): 每个缓存项都有一个唯一的键,用于标识该缓存项。add_option() 函数使用的缓存键是选项的名字 $name

add_option() 函数的缓存机制如下:

  1. 当使用 add_option() 函数添加一个选项时,如果数据库插入操作成功,该选项会被添加到对象缓存的 'options' 缓存组中,缓存键为选项的名字。
  2. 当使用 get_option() 函数获取一个选项时,WordPress 会首先从对象缓存中查找该选项。如果找到,就直接返回缓存中的值,而无需查询数据库。
  3. 如果对象缓存中没有找到该选项,WordPress 才会查询数据库,并将查询结果添加到对象缓存中,以便下次访问时可以直接从缓存中获取。

这种缓存机制可以显著提高 WordPress 的性能,因为从内存中读取数据比从数据库中读取数据要快得多。

get_option():选项读取的利器

既然提到了缓存,就不得不说一下 get_option() 函数,它是 add_option() 的好搭档,负责从数据库或缓存中获取选项的值。

function get_option( string $option, mixed $default = false ) {
  global $wpdb;

  // 1. 尝试从缓存中获取选项
  $value = wp_cache_get( $option, 'options' );

  // 2. 如果缓存中没有找到,则从数据库中获取
  if ( false === $value ) {
    $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );

    // 3. 如果数据库中没有找到,则返回默认值
    if ( is_null( $row ) ) {
      return $default;
    }

    // 4. 反序列化选项值
    $value = $row->option_value;
    $value = maybe_unserialize( $value );

    // 5. 将选项添加到缓存中
    wp_cache_add( $option, $value, 'options' );
  }

  /**
   * Filters the value of an existing option.
   *
   * The dynamic portion of the hook name, `$option`, refers to the option name.
   *
   * @since 2.2.0
   *
   * @param mixed $value Value of the option.
   */
  return apply_filters( "option_{$option}", $value );
}

代码分析:

  1. 尝试从缓存中获取: 首先,get_option() 函数会尝试从对象缓存的 'options' 缓存组中获取选项的值。如果找到了,就直接返回缓存中的值。

    $value = wp_cache_get( $option, 'options' );

    wp_cache_get() 函数用于从 WordPress 的对象缓存中获取数据。它接受两个参数:

    • $key:缓存的键,这里是选项的名字 $option
    • $group:缓存组,这里是 'options'
  2. 从数据库中获取: 如果缓存中没有找到该选项,get_option() 函数会查询数据库,从 wp_options 表中获取选项的值。

    if ( false === $value ) {
      $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
    
      // ...
    }

    这里,$wpdb->get_row() 方法用于执行数据库查询,并返回结果的第一行。$wpdb->prepare() 方法用于防止 SQL 注入攻击。

  3. 返回默认值: 如果在数据库中也没有找到该选项,get_option() 函数会返回默认值 $default

  4. 反序列化选项值: 如果从数据库中找到了该选项,get_option() 函数会使用 maybe_unserialize() 函数对选项的值进行反序列化。这是因为 wp_options 表的 option_value 字段存储的是序列化后的字符串。

    $value = $row->option_value;
    $value = maybe_unserialize( $value );

    maybe_unserialize() 函数的源码如下:

    function maybe_unserialize( $original ) {
        if ( is_serialized( $original ) ) { // don't attempt to unserialize data that wasn't serialized going in
            return @unserialize( $original );
        }
    
        return $original;
    }

    这个函数会判断 $original 是否是序列化的字符串,如果是,就使用 unserialize() 函数将其反序列化成数组或对象。

  5. 添加到缓存: 从数据库中获取选项的值后,get_option() 函数会将该选项添加到对象缓存中,以便下次访问时可以直接从缓存中获取。

    wp_cache_add( $option, $value, 'options' );
  6. 应用 Filter Hook: 最后,get_option() 函数会应用一个 Filter Hook option_{$option},允许其他插件或主题修改选项的值。

    return apply_filters( "option_{$option}", $value );

    Filter Hook 是 WordPress 提供的一种扩展机制,允许开发者在特定的事件发生时修改数据。

update_option():修改选项,与时俱进

除了添加和获取选项,WordPress 还提供了 update_option() 函数,用于修改已存在的选项。

function update_option( string $option, mixed $value, string|null $autoload = null ): bool {
  global $wpdb, $wp_suspend_cache_invalidation;

  // 1. 序列化选项值
  $value = maybe_serialize( $value );

  // 2. 查询选项是否已经存在
  $old_value = get_option( $option );

  // 3. 如果选项已经存在,则更新数据库
  if ( false !== $old_value ) {
    // ...
  } else {
    // 4. 如果选项不存在,则添加选项
    return add_option( $option, $value, '', $autoload );
  }
  // 省略代码...
}

update_option() 函数会首先序列化选项值,然后查询选项是否已经存在。如果选项已经存在,则更新数据库中的选项值,并更新缓存。如果选项不存在,则调用 add_option() 函数添加选项。

delete_option():删除选项,挥手告别

最后,咱们再来看看 delete_option() 函数,用于删除一个选项。

function delete_option( string $option ): bool {
  global $wpdb;

  // 1. 从数据库中删除选项
  $result = $wpdb->delete( $wpdb->options, array( 'option_name' => $option ) );

  // 2. 从缓存中删除选项
  if ( $result ) {
    wp_cache_delete( $option, 'options' );

    /**
     * Fires after an option is successfully deleted.
     *
     * @since 2.0.0
     *
     * @param string $option Name of the option to delete.
     */
    do_action( 'delete_option', $option );

    return true;
  }

  return false;
}

delete_option() 函数会首先从数据库中删除选项,然后从缓存中删除选项。

总结:add_option()get_option()update_option()delete_option() 的协作

add_option()get_option()update_option()delete_option() 这四个函数是 WordPress 中选项管理的核心函数。它们共同协作,实现了选项的添加、获取、修改和删除操作。

  • add_option():添加一个选项到 wp_options 表,并将选项添加到缓存中。
  • get_option():从缓存或数据库中获取选项的值。
  • update_option():修改 wp_options 表中的选项值,并更新缓存。如果选项不存在,则添加选项。
  • delete_option():从 wp_options 表中删除选项,并从缓存中删除选项。

这四个函数的协作,保证了 WordPress 选项数据的持久化存储和高效访问。

好了,今天的讲座就到这里。希望大家对 add_option() 函数以及相关的选项管理机制有了更深入的了解。记住,理解这些底层机制,才能更好地开发 WordPress 插件和主题,打造更加强大的 WordPress 站点!感谢大家的收听!

发表回复

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