PHP中的动态函数名

14
我想简化在WordPress中创建“自定义文章类型”的过程,因为反复手动更改所有自定义文章类型名称实例非常繁琐。通过创建一个包含CPT名称的变量并在需要时使用它,可以很容易地实现这一点。这样,我只需在脚本开头声明变量,就可以解决其余问题。唯一的问题是,为了使其工作,我还需要在脚本中的每个函数前缀CPT名称,而在PHP中在函数名中使用变量并不容易或甚至不推荐。那么我该如何解决这个问题呢?以下是一个示例,以使其更加清晰:
$prefix = 'news';

function news_custom_type_init()
{
    global $prefix;

    register_post_type($prefix, array(
    'labels' => array(
          'name' => $prefix,
          'singular_label' => $prefix,
          'add_new' => 'Add',
          ...
        ));

        register_taxonomy_for_object_type( 'category', $prefix );
}
add_action('init', $prefix.'_custom_type_init');

这段代码几乎可以标准化,只要我能够动态地重命名函数,而不必在函数前面写单词"news",而是使用"$prefix"即可。
这段代码本来很好,但却无法正常工作。
$prefix = 'news';

$functionName= $prefix."_custom_type_init";

function $functionName()
{
    global $prefix;

    register_post_type($prefix, array(
    'labels' => array(
          'name' => $prefix,
          'singular_label' => $prefix,
          'add_new' => 'Add',
          ...
        ));

        register_taxonomy_for_object_type( 'category', $prefix );
}
add_action('init', $prefix.'_custom_type_init');

我需要手动命名函数,这有点违背了我的初衷(特别是当脚本嵌入了许多像这个函数一样的函数时)。

有什么最好的方法可以解决这个问题呢?

PS:我搜索了很多关于这个问题的谷歌和stackoverflow,但没有找到符合我的需求并且不会生成WordPress错误信息的解决方案。

谢谢。

10个回答

19

很简单,这是一个类似的项目代码片段:

$function = $prefix . '_custom_type_init';
if(function_exists($function)) {
  $function();
}

2
我不确定我表达清楚了(或者可能是我没有理解你的观点)。你的脚本检查函数是否已经存在,因此该函数应该在第一次手动创建时就已经存在了,对吧?我不是试图“调用”一个现有的函数,我要做的正是“动态创建”那个函数。简单来说,在我的上面的脚本中,我只想从任何地方删除“news”这个词,但不包括第一条指令,并使脚本正常工作。如果我没有理解你的意思,我很抱歉。 - Baylock
如果我理解正确,您想要有许多函数,例如“news_something”和“blog_something”等。然后动态调用所需的函数。我提供的代码应该在init()中被调用并选择要调用的函数。如果您想要从几个位置调用相同的函数,我认为您需要创建这些函数并使它们调用相同的函数,因为它们需要在PHP中注册。 - Nick Andriopoulos
感谢Hexbot的帮助。我编辑了上面的消息,以便更好地理解我的目的,并提供了一个不起作用的示例。 - Baylock

11

这是一个旧的线程,但可以简单地使用匿名函数:

add_action('init', function() use($args) {
    //...
});

那么就没有必要声明那么多函数。


使用循环中的映射创建多个文章类型时,这是一种强大的技术。如果文章类型共享某些功能但在数据层次结构上有所不同,并且不应作为父级耦合在一起,那么这通常是一个好主意。 - sampoh
如果其他模块的函数被直接调用,那么这会存在问题。虽然不应该这样做,但确实可能发生。 - jgmjgm

3

编辑(2017-04):匿名函数(正确实现)是最好的方法,参见David Vielhuber的答案。

这个答案是不可取的,任何涉及将代码作为字符串处理的方法都是不可取的,因为这会引发(除其他外)拼接问题。


我不确定是否建议使用,或者它是否有帮助,但PHP允许您创建“匿名”函数:

function generateFunction ($prefix) {
    $funcname = create_function(
        '/* comma separated args here */', 
        '/* insert code as string here, using $prefix as you please */'
    );
    return $funcname;
}

$func= generateFunction ("news_custom_type_init");
$func(); // runs generated function

我假设add_action只是调用你传递的任何函数。
注意:create_function不会返回一个具有您想要的名称的函数,但函数的内容将在您的控制下,并且函数的真实名称并不重要。 http://php.net/manual/en/function.create-function.php

谢谢cernunnos,我会尝试一下。这里不仅受语言限制,还受WordPress机制限制。希望它能够起作用。我会回来告诉你的。 - Baylock
它不起作用。至少在WordPress中不行。它会在另一个核心页面上生成错误(致命错误:在/home/www/xxx/web/admin/wp-includes/rewrite.php中对非对象调用成员函数add_rewrite_tag())。此外,我不认为这是正确的方法,因为触发该函数的“add_action”需要一个字符串。 - Baylock
1
这就是问题所在,genetareFunction返回一个字符串,它不会有你期望的名称:它将不是$prefix."_customblabla"这样的名称,而是类似于"lamda_5",但该名称将作为字符串保存到变量中,并可以传递给add_action(它是有效的回调函数,wordpress add_action api表示它接受有效的回调函数)。尝试在代码字符串中添加die("i died here")以查看wordpress是否实际到达那里(或者error_log("bla"))。 - cernunnos
1
由于create_function将在PHP 7.2中被弃用,因此投下反对票。请使用匿名函数。唯一的缺点是,您无法使用匿名函数取消挂钩后续操作。根据PHP文档,“强烈不建议使用此功能”。 - sampoh
1
@sampoh 更新了答案,你绝对正确地进行了负评。 - cernunnos

2

2
您可以使用方括号中的字符串 ("function_name_{$dynamic_var}")()。它与HTML标签一样,不需要解释。

1
这篇文章有些旧了,但PHP 7可能会解决这个问题。
尝试:
$prefix = 'news';

$functionName = $prefix . "_custom_type_init";

${$functionName} = function() use ($prefix) {

    register_post_type($prefix, array(
        'labels' => array(
            'name' => $prefix,
            'singular_label' => $prefix,
            'add_new' => 'Add'
        )
    );

    register_taxonomy_for_object_type( 'category', $prefix );
};

add_action('init', '$' . $functionName);

我认为它应该适用于WordPress。


0

PHP中称为可变函数的动态函数

https://www.php.net/manual/en/functions.variable-functions.php

例如

$functionName = function () {
    global $prefix;

    register_post_type($prefix, array(
    'labels' => array(
          'name' => $prefix,
          'singular_label' => $prefix,
          'add_new' => 'Add',
          ...
        ));

        register_taxonomy_for_object_type( 'category', $prefix );
}

你可以通过输入$functionName()来调用它。


0

0

我也在寻找一种方法来实现这个,因为我想创建一个函数,可以像 Ruby 一样为函数创建别名。例如:

function an_insanely_long_function_name($a, $b) {
    // do something with $a and $b
}

// I'll go insane if I have to type that stupidly long function name every time 
// Aliases to the rescue :)
define_alias('shrt_nm', 'an_insanely_long_function_name');

shrt_nm('a', 'b');

我进行了相当多的研究——大约2分钟:p——但没有找到任何可以帮助我实现该功能的东西。

我快要放弃并手动完成所有操作,但是我想起了好老的“eval”,我的长期伙伴:)

这是你需要做的:

function define_alias($alias, $function) {
    if (function_exists($alias))
        throw new \Exception('A function named ' . $alias . ' already exists!');

    // create the function
    eval("function $alias(...\$args) { $function(...\$args); }")
}

谢谢。我写下这个问题已经过去了几年,现在我不需要答案了,但也许这会帮助到某个人。 PS:在Stack Overflow中提到“eval”有点大胆,因为大多数时候人们都不喜欢它;-) - Bachir Messaouri

0

我不确定以上任何一种方法是否能够回答有关动态创建自定义文章类型的问题。但是,以下方法对我有效:

$languages = ("English", "Spanish", "French");

foreach($languages as $language):

    $example = function () use ($language) {

        $labels = array(
                'name' => __( $language . ' Posts' ),
                'singular_name' => __( $language . ' Post' )
        );

        $args = array(
   
            'labels'              => $labels,
            'hierarchical'        => false,
            'public'              => true,
            'show_ui'             => true,
            'show_in_menu'        => true,
            'show_in_nav_menus'   => true,
            'show_in_admin_bar'   => true,
            'menu_position'       => 5,
            "rewrite" => array( "slug" => $language . "_posts", "with_front" => true ),
            'capability_type'     => 'page',
         );
        register_post_type( $language . "_posts", $args );  

    };
    add_action( 'init', $example, 0 );

endforeach;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接