【问题标题】:PHP use private method as callbackPHP使用私有方法作为回调
【发布时间】:2014-04-21 00:44:17
【问题描述】:

我是第一次尝试 PHP+WP。我打算使用 WP 插件挂钩。作为一名 C++ 程序员,我还打算将所有代码放入类中。目前我有点坚持以下应该安装 WP 插件挂钩的 sn-p:

   class SettingsHandler
   {
      public function __construct()
      {
         add_filter('plugin_action_links', array($this, 'AddSettingsLink'), 10, 2);
      }

      private function AddSettingsLink($links, $file)
      {
         if ($file==plugin_basename(__FILE__))
         {
            $settings_link = '<a href="options-general.php?page=options_page">Settings</a>';
            array_unshift($links, $settings_link);
         }       
         return $links;
      }
   }

   $settingsHandler = new SettingsHandler();

这给了我一条错误消息: 警告:call_user_func_array() 期望参数 1 为有效回调,无法访问 E:\xampp\apps\wordpress\htdocs\wp-includes\plugin.php 中的私有方法 SettingsHandler::AddSettingsLink() 199 行

当我将回调切换为公开时,错误消失了。看来我不能在 PHP/WP 中使用私有方法作为回调。这将是非常糟糕的,因为它揭示了许多不应被其他任何人直接访问的回调方法。我可以将此类回调设为私有吗?

我还发现了以下运行良好的 sn-p:

   class a
   {
      public function __construct()
      {
         $str = " test test ";
         $result = preg_replace_callback('/test/', array($this, 'callback'), $str);
         echo $result;
      } 

      private function callback($m)
      {
         return 'replaced';
      }
   }

   $a = new a();

为什么第二个 sn-p 工作而第一个失败?区别在哪里?

【问题讨论】:

  • 您的第一个回调不是从您的对象上下文中调用的,而是从外部插件处理程序中调用的。那为什么还要坚持回调方法私有呢?
  • 为此类私有方法编写包装器(公共方法)并将这些包装器作为回调传递。

标签: php callback wordpress private


【解决方案1】:

第二个版本有效,因为preg_match_all 是从类范围内调用的,并且会立即执行回调。

但是the add_filter function only adds the callback to the global $wp_filter array。然后,该数组中的回调将在稍后阶段从您定义该方法的类外部调用。因此,可见性规则适用,使回调无法访问。

您可以通过将方法调用包装到 anonymous function 中来解决此问题,例如

public function __construct()
{
    add_filter(
        'plugin_action_links',
        function($links, $file) {
            return $this->AddSettingsLink($links, $file);
        },
        10,
        2
    );
}

但是,由于$this 在该版本之前的匿名函数中不可用,这至少需要 PHP 5.4(请参阅手册中的更改日志)。

另一种选择是让 SettingsHandler 实现 __invoke 以将其变成 Functor,例如你加

public function __invoke($links, $file)
{
    return $this->AddSettingsLink($links, $file);
}

并将 ctor 代码更改为

add_filter('plugin_action_links', $this, 10, 2);

由于该类将__invoke 实现为公共入口点,因此该实例可以用作回调。这样,您就可以将私人内容保密。

顺便说一句,我会将添加过滤器的代码移到类之外。将它放在 ctor 中会使类的可测试性降低(是的,我知道没有人测试 WP 插件,但仍然如此)。相反,将其放入您的插件文件中,然后包含那里所需的所有其他内容,例如

// your-plugin.php
include 'SettingsHandler.php';
add_filter('plugin_action_links', new SettingsHandler, 10, 2);

但这取决于你。

¹注意:显然这在 Wordpress 中不起作用,因为它会尝试在途中的某个地方序列化闭包。一般来说,这是一种提供私有方法作为回调的工作模式。

【讨论】:

  • 我尝试了匿名函数解决方法...错误“不允许序列化'Closure'”
  • @Silicomancer 嗯,很有趣。不幸的是,我对 WP 内部工作原理不够熟悉,不知道它在哪里尝试序列化 Closure/Anonymous 函数。我在没有 WP 的情况下进行了测试,然后它就可以工作了。我会更新问题以记录您的结果。
  • 我无法确认这一点,匿名函数的解决方案对我来说非常有效;其实很简单;谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多