解析 WordPress `wp_get_nav_menu_object()` 函数的源码:如何根据菜单 ID 或名称获取菜单对象。

各位观众老爷们,晚上好!今天咱们聊聊WordPress里一个默默奉献,但又非常重要的函数:wp_get_nav_menu_object()。 别看它名字长,其实功能很简单,就是根据菜单的ID或者名称,把对应的菜单对象给你找出来。 就像一个专业的服务员,你告诉他你要哪道菜的名字,他就能准确地把这道菜端到你面前。

那么,这个“服务员”是怎么工作的呢?接下来咱们就深入源码,扒一扒它的老底儿。

一、wp_get_nav_menu_object() 的基本用法

首先,咱们先看看这个函数的庐山真面目:

<?php
/**
 * Retrieve nav menu object by ID or name.
 *
 * @since 3.0.0
 *
 * @param mixed $menu ID, slug, or name of the menu to get.
 * @return WP_Term|false WP_Term object on success, false if the menu doesn't exist.
 */
function wp_get_nav_menu_object( $menu ) {
    // 函数体在这里
}

从注释里我们可以看到,wp_get_nav_menu_object() 接受一个参数 $menu,它可以是菜单的ID(整数),菜单的slug(字符串,通常是菜单名称的小写和空格替换为短横线),或者菜单的名称(字符串)。 函数的返回值是WP_Term对象(如果找到菜单)或者false(如果没找到菜单)。

举个例子:

<?php
// 假设我们知道菜单的ID是 5
$menu_object = wp_get_nav_menu_object( 5 );

if ( $menu_object ) {
    echo '菜单名称:' . $menu_object->name . '<br>';
    echo '菜单ID:' . $menu_object->term_id . '<br>';
    echo '菜单Slug:' . $menu_object->slug . '<br>';
} else {
    echo '找不到该菜单!';
}

// 假设我们知道菜单的名称是 "我的导航"
$menu_object = wp_get_nav_menu_object( '我的导航' );

if ( $menu_object ) {
    echo '菜单名称:' . $menu_object->name . '<br>';
    echo '菜单ID:' . $menu_object->term_id . '<br>';
    echo '菜单Slug:' . $menu_object->slug . '<br>';
} else {
    echo '找不到该菜单!';
}

// 假设我们知道菜单的slug是 "my-navigation"
$menu_object = wp_get_nav_menu_object( 'my-navigation' );

if ( $menu_object ) {
    echo '菜单名称:' . $menu_object->name . '<br>';
    echo '菜单ID:' . $menu_object->term_id . '<br>';
    echo '菜单Slug:' . $menu_object->slug . '<br>';
} else {
    echo '找不到该菜单!';
}

二、源码剖析:wp_get_nav_menu_object() 内部的乾坤

接下来,才是真正的重头戏。咱们把 wp_get_nav_menu_object() 的源码拆开,一行一行地分析,看看它到底是怎么实现的。

<?php
function wp_get_nav_menu_object( $menu ) {
    static $cache = array();

    $menu_obj = false;

    if ( is_object( $menu ) ) {
        $menu_obj = $menu;
    } elseif ( is_numeric( $menu ) ) {
        $menu = (int) $menu;
        if ( isset( $cache[ $menu ] ) ) {
            $menu_obj = $cache[ $menu ];
        } else {
            $menu_obj = get_term( $menu, 'nav_menu' );
            if ( $menu_obj && ! is_wp_error( $menu_obj ) ) {
                $cache[ $menu ] = $menu_obj;
            }
        }
    } else {
        $menu = trim( $menu );
        if ( isset( $cache[ $menu ] ) ) {
            $menu_obj = $cache[ $menu ];
        } else {
            $terms = get_terms(
                'nav_menu',
                array(
                    'hide_empty' => true,
                    'name'       => $menu,
                )
            );

            if ( ! empty( $terms ) ) {
                $menu_obj = array_shift( $terms );
            }

            if ( empty( $menu_obj ) ) {
                $terms = get_terms(
                    'nav_menu',
                    array(
                        'hide_empty' => true,
                        'slug'       => $menu,
                    )
                );
                if ( ! empty( $terms ) ) {
                    $menu_obj = array_shift( $terms );
                }
            }

            if ( $menu_obj && ! is_wp_error( $menu_obj ) ) {
                $cache[ $menu ] = $menu_obj;
            }
        }
    }

    if ( empty( $menu_obj ) || is_wp_error( $menu_obj ) ) {
        $menu_obj = false;
    }

    return $menu_obj;
}

现在,咱们把这段代码拆成几个部分,逐一讲解:

1. 缓存机制:static $cache = array();

<?php
static $cache = array();

这行代码定义了一个静态变量 $cache,它是一个数组。 static 关键字意味着这个变量只会在函数第一次被调用时初始化,之后每次调用函数都会使用同一个 $cache 变量。 这个 $cache 数组用来缓存已经查询过的菜单对象,避免重复查询数据库,提高性能。 就像你经常去同一家餐厅吃饭,服务员记住你喜欢吃什么,下次你来的时候就直接给你推荐一样。

2. 初始化菜单对象:$menu_obj = false;

<?php
$menu_obj = false;

这行代码初始化一个变量 $menu_obj,并将其设置为 false。 这个变量用来存储最终找到的菜单对象。 如果函数执行过程中没有找到菜单,那么 $menu_obj 仍然是 false,函数最终也会返回 false

3. 如果 $menu 已经是一个对象:if ( is_object( $menu ) ) { ... }

<?php
if ( is_object( $menu ) ) {
    $menu_obj = $menu;
}

这部分代码检查传入的 $menu 参数是否已经是一个对象。 如果是,就直接将 $menu 赋值给 $menu_obj。 这种情况通常发生在其他函数已经获取了菜单对象,然后将该对象传递给 wp_get_nav_menu_object() 函数。 就像你已经把菜端到桌子上了,服务员只是确认一下而已。

4. 如果 $menu 是一个数字(菜单ID):elseif ( is_numeric( $menu ) ) { ... }

<?php
elseif ( is_numeric( $menu ) ) {
    $menu = (int) $menu;
    if ( isset( $cache[ $menu ] ) ) {
        $menu_obj = $cache[ $menu ];
    } else {
        $menu_obj = get_term( $menu, 'nav_menu' );
        if ( $menu_obj && ! is_wp_error( $menu_obj ) ) {
            $cache[ $menu ] = $menu_obj;
        }
    }
}

这部分代码处理 $menu 是一个数字的情况,即菜单的ID。

  • 类型转换:$menu = (int) $menu; 首先,将 $menu 转换为整数类型,确保后续的操作不会出错。
  • 检查缓存:if ( isset( $cache[ $menu ] ) ) { ... } 然后,检查 $cache 数组中是否已经存在以 $menu (菜单ID) 为键的缓存。 如果存在,就直接从缓存中获取菜单对象,赋值给 $menu_obj,并结束查找。
  • 查询数据库:$menu_obj = get_term( $menu, 'nav_menu' ); 如果缓存中不存在,就调用 get_term() 函数从数据库中查询菜单对象。 get_term() 函数是 WordPress 提供的一个用于获取分类术语(taxonomy term)的函数,这里我们用它来获取 nav_menu 分类下的菜单对象。
  • 缓存结果:if ( $menu_obj && ! is_wp_error( $menu_obj ) ) { ... } 如果 get_term() 函数成功找到了菜单对象,并且没有发生错误,就将该菜单对象缓存到 $cache 数组中,以便下次使用。

5. 如果 $menu 是一个字符串(菜单名称或Slug):else { ... }

<?php
else {
    $menu = trim( $menu );
    if ( isset( $cache[ $menu ] ) ) {
        $menu_obj = $cache[ $menu ];
    } else {
        $terms = get_terms(
            'nav_menu',
            array(
                'hide_empty' => true,
                'name'       => $menu,
            )
        );

        if ( ! empty( $terms ) ) {
            $menu_obj = array_shift( $terms );
        }

        if ( empty( $menu_obj ) ) {
            $terms = get_terms(
                'nav_menu',
                array(
                    'hide_empty' => true,
                    'slug'       => $menu,
                )
            );
            if ( ! empty( $terms ) ) {
                $menu_obj = array_shift( $terms );
            }
        }

        if ( $menu_obj && ! is_wp_error( $menu_obj ) ) {
            $cache[ $menu ] = $menu_obj;
        }
    }
}

这部分代码处理 $menu 是一个字符串的情况,即菜单的名称或Slug。

  • 去除空格:$menu = trim( $menu ); 首先,使用 trim() 函数去除 $menu 字符串两端的空格,避免因为空格导致查找失败。
  • 检查缓存:if ( isset( $cache[ $menu ] ) ) { ... } 然后,检查 $cache 数组中是否已经存在以 $menu (菜单名称或Slug) 为键的缓存。 如果存在,就直接从缓存中获取菜单对象,赋值给 $menu_obj,并结束查找。
  • 按名称查询:$terms = get_terms( ..., array( 'name' => $menu, ) ); 如果缓存中不存在,就调用 get_terms() 函数,根据菜单的名称查询菜单对象。 get_terms() 函数可以根据多个条件查询分类术语。 这里我们使用 name 参数来指定要查询的菜单名称。 hide_empty 设置为 true 表示只查询非空的菜单。
  • 处理结果:if ( ! empty( $terms ) ) { ... } get_terms() 函数返回一个数组,包含所有符合条件的菜单对象。 如果数组不为空,就使用 array_shift() 函数从数组中取出第一个菜单对象,赋值给 $menu_objarray_shift() 函数会移除数组中的第一个元素,并返回该元素的值。
  • 按Slug查询:$terms = get_terms( ..., array( 'slug' => $menu, ) ); 如果按名称没有找到菜单,就再次调用 get_terms() 函数,根据菜单的Slug查询菜单对象。 这里我们使用 slug 参数来指定要查询的菜单Slug。
  • 处理结果:if ( ! empty( $terms ) ) { ... } 和按名称查询一样,如果 get_terms() 函数返回的数组不为空,就使用 array_shift() 函数从数组中取出第一个菜单对象,赋值给 $menu_obj
  • 缓存结果:if ( $menu_obj && ! is_wp_error( $menu_obj ) ) { ... } 如果找到了菜单对象,并且没有发生错误,就将该菜单对象缓存到 $cache 数组中,以便下次使用。

6. 最终处理:if ( empty( $menu_obj ) || is_wp_error( $menu_obj ) ) { ... }

<?php
if ( empty( $menu_obj ) || is_wp_error( $menu_obj ) ) {
    $menu_obj = false;
}

这部分代码对最终的 $menu_obj 进行检查。 如果 $menu_obj 为空,或者是一个 WP_Error 对象(表示发生了错误),就将其设置为 false

7. 返回结果:return $menu_obj;

<?php
return $menu_obj;

最后,函数返回 $menu_obj,它可能是找到的菜单对象(WP_Term 对象),也可能是 false(表示没有找到菜单)。

三、流程图总结

为了更清晰地理解 wp_get_nav_menu_object() 函数的工作流程,我们可以用一个简单的流程图来表示:

graph TD
    A[开始] --> B{是否是对象?};
    B -- 是 --> C[直接赋值给 $menu_obj];
    B -- 否 --> D{是否是数字?};
    D -- 是 --> E[检查缓存(ID)];
    E -- 命中 --> F[从缓存获取];
    E -- 未命中 --> G[get_term(ID, 'nav_menu')];
    G --> H{是否成功?};
    H -- 是 --> I[缓存结果(ID)];
    H -- 否 --> J[menu_obj = false];
    D -- 否 --> K[trim($menu)];
    K --> L[检查缓存(Name/Slug)];
    L -- 命中 --> F;
    L -- 未命中 --> M[get_terms(Name)];
    M --> N{是否找到?};
    N -- 是 --> O[array_shift()];
    N -- 否 --> P[get_terms(Slug)];
    P --> Q{是否找到?};
    Q -- 是 --> O;
    Q -- 否 --> J;
    O --> I[缓存结果(Name/Slug)];
    I --> R{是否为空或错误?};
    F --> R;
    R -- 是 --> J;
    R -- 否 --> S[返回 $menu_obj];
    J --> S;
    S --> T[结束];

四、表格总结

为了更清晰地整理 wp_get_nav_menu_object() 函数的关键点,咱们用一个表格来总结一下:

功能 描述
参数 $menu:菜单的ID(整数)、名称(字符串)或Slug(字符串)
返回值 WP_Term 对象(如果找到菜单)或 false(如果没找到菜单)
缓存机制 使用静态变量 $cache 缓存已经查询过的菜单对象,避免重复查询数据库,提高性能
查询方式 优先从缓存中查找,如果缓存中不存在,则根据 $menu 的类型选择不同的查询方式:
– 如果是ID,则调用 get_term() 函数查询
– 如果是名称或Slug,则调用 get_terms() 函数查询
错误处理 如果查询过程中发生错误,或者最终没有找到菜单,则返回 false
关键函数 get_term():根据ID获取分类术语
get_terms():根据条件获取分类术语

五、注意事项

  • 性能优化: 缓存机制非常重要,可以显著提高性能。
  • 名称和Slug: 菜单名称和Slug可能会重复,如果存在重复的菜单,wp_get_nav_menu_object() 函数只会返回第一个匹配的菜单。
  • nav_menu 分类: wp_get_nav_menu_object() 函数只能用于获取 nav_menu 分类下的菜单对象。

六、实际应用

wp_get_nav_menu_object() 函数在WordPress主题和插件开发中被广泛使用。 它可以用来:

  • 获取菜单信息: 获取菜单的名称、ID、Slug等信息,用于自定义菜单的显示。
  • 判断菜单是否存在: 在代码中判断某个菜单是否存在,避免出现错误。
  • 动态生成菜单: 根据菜单的信息动态生成菜单的HTML代码。

七、总结

好了,各位观众老爷们,今天咱们就聊到这里。 wp_get_nav_menu_object() 函数虽然看起来简单,但内部的逻辑还是比较清晰的。 理解了这个函数的源码,可以帮助我们更好地理解WordPress的菜单系统,并在实际开发中更加灵活地使用它。

希望今天的讲解对大家有所帮助! 咱们下期再见!

发表回复

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