add_action ссылается на класс

38

Можно ли ссылаться на класс вместо функции в «add_action»? Не могу понять это. Вот лишь основной пример рассматриваемой функции.

add_action( 'admin_init', 'MyClass' );
class MyClass {
     function __construct() {
          .. This is where stuff gets done ..
     }
}

Так что да, это не работает. Я также попробовал:

$var = new MyClass();
add_action( 'admin_init', array( &$var ) );

А также:

$var = new MyClass();
add_action( 'admin_init', array( &$var, '__construct' ) );

А также:

add_action( 'admin_init', 'MyClass::__construct' );

Есть ли в любом случае я могу сделать это без необходимости создания отдельной функции, которая загружает класс? Я хотел бы иметь возможность просто запустить конструктор классов через add_action. Это все, что нужно загружать, чтобы мяч катился.

Мэтью Радди
источник

Ответы:

69

Нет, вы не можете «инициализировать» или создать экземпляр класса с помощью хука, а не напрямую. Всегда требуется некоторый дополнительный код (и это нежелательно, когда вы открываете банку с червями для себя).

Вот лучший способ сделать это:

class MyClass {
     function __construct() {
          add_action( 'admin_init',array( $this, 'getStuffDone' ) );
     }
     function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
$var = new MyClass();

Конечно, можно создать интерфейсный класс, чтобы упростить его для общего случая еще дальше:

class IGetStuffDone {
    function IGetStuffDone(){
        add_action( 'admin_init',array( $this, 'getStuffDone' ) );
    }
    public abstract function getStuffDone();
}

Обратите внимание, что в качестве интерфейса вы не можете создать объект этого типа напрямую, но вы можете создать подкласс, позволяющий вам сказать:

class CDoingThings extends IGetStuffDone {
    function getStuffDone(){
        // doing things
    }
}
$var = new CDoingThings();

Который затем автоматически добавит все хуки, вам просто нужно определить, что именно делается в подклассе, а затем создать его!

На конструкторах

Я бы не добавил конструктор в качестве функции ловушки, это плохая практика и может привести к множеству необычных событий. Также в большинстве языков конструктор возвращает объект, для которого создается экземпляр, поэтому, если вашему хуку нужно вернуть что-то похожее на фильтр, он не вернет отфильтрованную переменную, как вы хотите, а вместо этого вернет объект класса.

Вызов конструктора или деструктора - очень, очень, очень плохая практика программирования, независимо от того, на каком языке вы находитесь, и никогда не следует делать это.

Конструкторы также должны создавать объекты, чтобы инициализировать их готовыми к использованию, а не для реальной работы. Работа, которую должен выполнить объект, должна быть в отдельной функции.

Методы статического класса, и не нужно вообще создавать / инициализировать

Если ваш метод класса является статическим методом класса, вы можете передать имя класса в кавычках, а не $thisкак показано ниже:

class MyClass {
     public static function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
add_action( 'admin_init', array('MyClass','getStuffDone' ) );

Закрытия и PHP 5.3

К сожалению, вы не можете избежать линии, создающей новый класс. Единственное другое решение, чтобы пропустить это, включало бы код котельной пластины, который все еще имеет эту строку, и потребовал бы PHP 5.3+, например:

add_action('admin_init',function(){
    $var = new MyClass();
    $var->getStuffDone();
});

В этот момент вы можете также пропустить класс и просто использовать функцию:

add_action('admin_init',function(){
    // do stuff
});

Но имейте в виду, что вы сейчас представили призрак анонимных функций. Нет способа удалить вышеуказанное действие с помощью remove_action, и это может и действительно причиняет большую боль разработчикам, которые должны работать с кодом других людей.

На амперсандах

Вы можете увидеть действия, используемые следующим образом:

array( &$this, 'getStuffDone' );

Это плохо . &был добавлен обратно в PHP 4, когда объекты передавались как значения, а не как ссылки. PHP 4 более десяти лет, и WordPress не поддерживался в течение очень долгого времени.

Там нет никаких оснований для использования &thisпри добавлении крючков и фильтров, и удаление ссылки не вызовет никаких проблем, и может даже улучшить совместимость с будущими версиями PHP

Используйте это вместо:

array( $this, 'getStuffDone' );
Том Дж Новелл
источник
Хорошо. Спасибо вам от всего этого; действительно научился совсем немного. Я действительно чувствую себя комфортно только в PHP на основе классов. Это то, что я сделал, и это работает, но не могли бы вы сказать мне, является ли это плохой практикой / неправильной в любом случае? Я инициировал класс внутри статической функции, внутри самого класса. Затем ссылался на статическую функцию в add_action. Смотрите эту ссылку: pastebin.com/0idyPwwY
Мэтью Радди
да, вы могли бы сделать это таким образом, хотя я мог бы избежать использования в $classкачестве имени переменной, эти слова, как правило, зарезервированы. Я думаю, что вы стараетесь изо всех сил избегать говорить что-то подобное $x = new Y();в глобальной области, и вы добавляете сложность там, где ничего не нужно. Ваша попытка уменьшить количество написанного кода повлекла за собой написание большего количества кода!
Том Дж. Новелл
Я бы отметил, что во всех вышеперечисленных случаях вам лучше использовать функцию, а не класс, так как этот класс все равно будет отброшен и служит той же цели. Это пустая трата ресурсов. Помните, что создание и уничтожение объекта требует определенных затрат, вам нужно несколько объектов, и вы хотите, чтобы они были долгоживущими
Том Дж. Ноуэлл
Хорошая точка зрения. Изменил способ, которым я смотрел на это. Я думаю, что я буду называть это в глобальной области видимости, избегая лишнего кода.
Мэтью Радди
1
Я хотел бы добавить, что если вы поместили свой класс в пространство имен, вам придется добавить это тоже, иначе add_action () / add_filter () не найдет его - вот так:add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
jschrab
10

Пример класса

Заметки:

  • Начните урок только один раз
    • Вызовите приоритет 0, чтобы позже вы могли использовать тот же хук с приоритетом по умолчанию
    • Заверните его в a, ! class_existsчтобы не вызывать его дважды, и поместите инициализатор внутри
  • Сделай initфункцию и класс varstatic
  • Вызывайте конструктор из вашего init, когда вы вызываете класс new self.

Вот пример

if ( ! class_exists( 'WPSESampleClass' ) )
{
    // Init the class on priority 0 to avoid adding priority inside the class as default = 10
    add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );

class WPSESampleClass
{
    /**
     * The Class Object
     */
    static private $class = null;

    public static function init()
    {
        if ( null === self::$class ) 
            self :: $class = new self;

        return self :: $class;
    }

    public function __construct()
    {
        // do stuff like add action calls:
        add_action( 'init', array( $this, 'cb_fn_name' ) );
    }

    public function cb_fn_name()
    {
        // do stuff 
    }
} // END Class WPSESampleClass

} // endif;

Php 5+

Пожалуйста , оставьте &вне. Мы уже за php4. :)

кайзер
источник
2
if (!class_exists("AllInOneWoo")){
    class AllInOneWoo {
        function __construct(){
            add_action('admin_menu', array($this, 'all_in_one_woo') );
        }
        function all_in_one_woo(){
            $page_title = 'All In One Woo';
            $menu_title = 'All In One Woo';
            $capability = 'manage_options';
            $menu_slug  = 'all-in-one-woo-menu';
            $function   = array($this, 'all_in_one_woo_menu');
            $icon_url   = 'dashicons-media-code';
            $position   = 59;

            add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position);
        }
        function all_in_one_woo_menu(){?>
            <div class="wrap">
                <h1><?php _e('All In One Woo', 'all_in_one_woo'); ?></h1>
            </div>
        <?php }
    }// end class
}// end if

if (class_exists("AllInOneWoo")){       
    $all_in_one_woo = new AllInOneWoo();
}
Закир Саджиб
источник
Пожалуйста, измените свой ответ и добавьте объяснение: почему это может решить проблему?
fuxia
0

Вы можете запускать события в своем классе без необходимости загружать его изначально . Это удобно, если вы не хотите загружать весь класс заранее, но вам нужен доступ к фильтрам и действиям WordPress.

Вот очень простой пример

<?php
class MyClass
{
    public static function init()
    {
        add_filter( 'the_content', array('MyClass', 'myMethod') );
    }

    public static function myMethod($content)
    {
        $content = $content . 'Working without loading the object';
    }
}

MyClass::init();

Это очень упрощенная версия ответа Кайзера, но демонстрирующая простым языком поведение. Может пригодиться тем, кто смотрит на этот стиль впервые.

Другие методы могут при необходимости инициировать объект. Я лично использую этот метод, чтобы разрешить дополнительным частям моего плагина ставить в очередь сценарии, но только вызывать объект по запросу AJAX.

Джон Рейд
источник
0

Это работает для меня:

class foo
{
    public static function bar()
    {
        echo 'it works!';
    }
}

add_action('foo_bar', array('foo', 'bar'));
Джонатан
источник
0

Вообще говоря, вы не добавили бы целый класс к крючку. В add_action()/ add_filter()Крючки ожидают обратного вызова функции , которые могут быть ссылки из внутри класса .

Допустим, у вас есть init()функция внутри вашего класса, которую вы хотите подключить к крюку WordPress init.

Поместите ваш add_action()вызов в ваш класс, а затем определите обратный вызов следующим образом:

add_action( 'init', array( $this, 'init' ) );

(Примечание: я предполагаю, что ваш класс правильно пространства имен; в противном случае, убедитесь, что пространство имен ваших функций обратного вызова.)

Чип Беннетт
источник
А что если текущий класс еще не был инициирован? Я пытался использовать add_action для фактического запуска, поэтому мне не нужно добавлять $ var = new MyClass (); заранее в другом месте.
Мэтью Радди
Разве вы не можете просто написать обратный вызов для создания экземпляра вашего класса init(или там, где вам это нужно)?
Чип Беннетт
0

Вы должны быть в состоянии сделать это, передавая имя класса вместо экземпляра объекта:

add_action( 'init', array( 'MyClass', '__construct' ) );

(Теоретически, ваше другое решение должно работать

$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );

Не уверен с головы, почему это не так. Может быть, если вы не позвоните по ссылке?)

Ущелья Бун
источник
Первый не работает. Просто делает страницу пустой. Второй работает, но только потому, что класс был инициирован в первой строке. Это своего рода побеждает цель добавления действия, потому что класс уже был инициирован. Но он не делает то, что я пытаюсь сделать, а именно инициировать класс через действие. В реальном коде, над которым я работаю, действие - это не admin_init, а настраиваемое действие в другом классе. Я не хочу, чтобы функция 'MyClass' была запущена, если в другом классе нет действия. Извините, если я что-то упустил; учусь по ходу дела
Мэтью Радди
Да, я был неправ. Это работает, только если вы вызываете статический initметод. См. Wordpress.stackexchange.com/a/48093/14052
Ущелья Буна,