Ссылка на класс в add_action
Возможно ли сослаться на класс вместо функции в 'add_action'? Никак не могу разобраться. Вот простой пример функции, о которой идет речь.
add_action( 'admin_init', 'MyClass' );
class MyClass {
function __construct() {
// Здесь выполняется работа
}
}
Да, это не работает. Я также пробовал:
$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. Это всё, что нужно загрузить, чтобы начать процесс.

Нет, невозможно «инициализировать» или создать экземпляр класса через хук напрямую.
Всегда требуется дополнительный код (и это нежелательно, так как вы создаёте себе проблемы).
Вот более правильные способы сделать это:
class MyClass {
function __construct() {
add_action( 'admin_init', [ $this, 'getStuffDone' ] );
}
function getStuffDone() {
// .. Здесь выполняется работа ..
}
}
$var = new MyClass();
Конечно, можно создать интерфейсный класс для ещё большего упрощения в общем случае:
class IGetStuffDone {
function IGetStuffDone(){
add_action( 'admin_init', [ $this, 'getStuffDone' ] );
}
public abstract function getStuffDone();
}
Обратите внимание, что, как интерфейс, вы не можете создать объект этого типа напрямую, но вы можете создать подкласс, позволяя вам сделать так:
class CDoingThings extends IGetStuffDone {
function getStuffDone(){
// выполнение действий
}
}
$var = new CDoingThings();
Это автоматически добавит все хуки, вам остаётся только определить, что именно выполняется в подклассе, а затем создать его!
О конструкторах
Я бы не добавлял конструктор в качестве функции хука — это плохая практика, которая может привести к множеству неожиданных проблем. Кроме того, в большинстве языков конструктор возвращает объект, который создаётся, поэтому, если ваш хук должен что-то возвращать (например, в фильтре), он вернёт не отфильтрованное значение, как вам нужно, а объект класса.
Прямой вызов конструктора или деструктора — это очень, очень, очень плохая практика программирования, независимо от языка, и никогда не должна применяться.
Конструкторы должны создавать объекты и инициализировать их для использования, а не выполнять работу. Работа, выполняемая объектом, должна быть в отдельной функции.
Статические методы класса и отсутствие необходимости в создании экземпляра/инициализации
Если метод вашего класса статический, вы можете передать имя класса в кавычках вместо $this
, как показано ниже:
class MyClass {
public static function getStuffDone() {
// .. Здесь выполняется работа ..
}
}
add_action( 'admin_init', [ __NAMESPACE__ . '\MyClass','getStuffDone' ] );
Обратите внимание на использование __NAMESPACE__
, которое требуется, если ваш класс находится в пространстве имён.
Замыкания
К сожалению, нельзя избежать строки, создающей новый класс. Единственное другое решение, позволяющее пропустить её, включает шаблонный код, который всё ещё содержит эту строку, например:
add_action( 'admin_init', function() {
$var = new MyClass();
$var->getStuffDone();
} );
В этом случае лучше вообще отказаться от класса и просто использовать функцию:
add_action( 'admin_init', function() {
// выполнение действий
} );
Но помните, что теперь вы ввели в код анонимные функции. Невозможно удалить это действие с помощью remove_action
, и это может (и действительно) вызывать серьёзные проблемы у разработчиков, которым приходится работать с чужим кодом.
Об амперсандах
Вы могли видеть действия, использованные так:
array( &$this, 'getStuffDone' )
Это плохо. Символ &
был добавлен ещё в PHP 4, когда объекты передавались по значению, а не по ссылке. PHP 4 уже более десяти лет не поддерживается, и WordPress давно не работает с ним.
Нет причин использовать &this
при добавлении хуков и фильтров. Удаление ссылки не вызовет проблем и даже может улучшить совместимость с будущими версиями PHP.
Используйте вместо этого:
[ $this, 'getStuffDone' ]

Хорошо. Большое спасибо за все это; действительно узнал довольно много. Сейчас я только начинаю чувствовать себя комфортно с классовым PHP. Вот что я сделал, и это работает, но не могли бы вы сказать, является ли это плохой практикой/неправильным в каком-либо отношении? Я инициализировал класс внутри статической функции, внутри самого класса. Затем сослался на статическую функцию в add_action. Смотрите ссылку: http://pastebin.com/0idyPwwY

да, вы могли бы сделать это таким образом, хотя я бы избегал использования $class
в качестве имени переменной, такие слова обычно зарезервированы. Мне кажется, вы слишком усложняете, чтобы избежать написания чего-то вроде $x = new Y();
в глобальной области видимости, и добавляете сложность там, где в ней нет необходимости. Ваша попытка уменьшить количество кода привела к написанию большего количества кода!

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

Хорошее замечание. Изменил свой взгляд на это. Думаю, лучше вызывать это в глобальной области видимости, избегая лишнего кода.

Хочу добавить, что если ваш класс находится в пространстве имен, вам нужно будет указать его тоже, иначе add_action()/add_filter() не смогут его найти - вот так: add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );

Пример класса
Примечания:
- Инициализируйте класс только один раз
- Вызывайте на приоритете 0, чтобы можно было использовать тот же хук с приоритетом по умолчанию позже
- Оберните это в
! class_exists
, чтобы избежать повторного вызова и поместите инициализацию внутрь
- Сделайте функцию
init
и переменную классаstatic
- Вызывайте конструктор изнутри вашего init, когда вызываете класс
new self
.
Вот пример
if ( ! class_exists( 'WPSESampleClass' ) )
{
// Инициализируем класс на приоритете 0, чтобы не добавлять приоритет внутри класса (по умолчанию = 10)
add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );
class WPSESampleClass
{
/**
* Объект класса
*/
static private $class = null;
public static function init()
{
if ( null === self::$class )
self :: $class = new self;
return self :: $class;
}
public function __construct()
{
// выполняем действия, например, добавляем вызовы хуков:
add_action( 'init', array( $this, 'cb_fn_name' ) );
}
public function cb_fn_name()
{
// выполняем действия
}
} // END Class WPSESampleClass
} // endif;
PHP 5+
Пожалуйста, не используйте &
. Мы уже давно перешли с PHP4. :)

Вы можете инициировать события в своем классе без необходимости загружать его изначально. Это удобно, если вы не хотите загружать весь класс заранее, но вам нужно получить доступ к фильтрам и действиям WordPress.
Вот очень простой пример:
<?php
class MyClass
{
public static function init()
{
add_filter( 'the_content', array('MyClass', 'myMethod') );
}
public static function myMethod($content)
{
$content = $content . 'Работаем без загрузки объекта';
}
}
MyClass::init();
Это очень упрощенная версия ответа Kaiser, но она наглядно демонстрирует принцип работы. Может быть полезно для тех, кто впервые сталкивается с таким подходом.
Другие методы по-прежнему могут инициировать объект при необходимости. Лично я использую этот метод, чтобы позволить опциональным частям моего плагина подключать скрипты, но инициировать объект только при AJAX-запросе.

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 }
}// конец класса
}// конец условия
if (class_exists("AllInOneWoo")){
$all_in_one_woo = new AllInOneWoo(); // Создание экземпляра класса
}

Ответ 2025
Вы уже сейчас можете сделать это с помощью __invoke
магического метода. Этот метод вызывается, когда вы пытаетесь использовать класс как функцию.
class MyHandler {
public function __construct(){ /* ... */ }
public function __invoke(){
// здесь можно выполнить любые нужные действия
}
}
add_action('admin_init', new MyHandler);

Как правило, вы не добавляете целый класс к хуку. Хуки add_action()
/add_filter()
ожидают функции обратного вызова, которые могут быть указаны внутри класса.
Допустим, у вас есть функция init()
внутри вашего класса, которую вы хотите привязать к хуку WordPress init
.
Поместите ваш вызов add_action()
внутри класса и укажите функцию обратного вызова следующим образом:
add_action( 'init', array( $this, 'init' ) );
(Примечание: я предполагаю, что ваш класс правильно пространственно именован; в противном случае обязательно укажите пространство имён для ваших функций обратного вызова.)

А что если текущий класс ещё не был инициализирован? Я пытался использовать add_action, чтобы фактически выполнить инициализацию, и мне не приходилось бы добавлять $var = new MyClass(); заранее в другом месте.

Вы можете сделать это, передав имя класса вместо созданного объекта:
add_action( 'init', array( 'MyClass', '__construct' ) );
(Теоретически, ваше другое решение тоже должно работать
$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );
Не уверен наверняка, почему не работает. Возможно, если вы не вызываете по ссылке?)

Первый вариант не работает. Просто делает страницу пустой. Второй работает, но только потому, что класс уже был инициализирован в первой строке. Это вроде как сводит на нет смысл добавления действия, потому что класс уже инициализирован. Но это не делает то, что я пытаюсь сделать, а именно инициализировать класс через действие. В реальном коде, над которым я работаю, действие не 'admin_init', а пользовательское действие внутри другого класса. Я не хочу, чтобы функция 'MyClass' инициализировалась, если действие в другом классе отсутствует. Извините, если что-то упускаю; учусь по ходу дела.

Да, я ошибся. Это работает только если вы вызываете статический метод init
. Смотрите http://wordpress.stackexchange.com/a/48093/14052

Пример статического метода
class FrmTransLiteEntriesController {
/**
* Включить детали платежа в боковую панель записи.
*
* @param stdClass $entry
* @return void
*/
public static function sidebar_list( $entry ) {
add_action( 'frm_entry_shared_sidebar_middle', __CLASS__ . '::show_sidebar_list' );
}
public static function show_sidebar_list( $entry ) {
}
}
