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
Ущелья Буна,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.