Вопрос или проблема
Можно ли ссылаться на класс вместо функции в ‘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' ]
Пример класса
Заметки:
- Инициализируйте класс только один раз
- Вызывайте его на приоритете 0, чтобы вы могли использовать тот же хук с приоритетом по умолчанию позже
- Заключите его в
! class_exists
, чтобы избежать повторного вызова, и поместите инициализацию внутрь
- Сделайте функцию
init
и переменную классаstatic
- Вызывайте конструктор внутри вашей инициализации, когда вы вызываете класс
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:
add_action( 'init', array( $this, 'cb_fn_name' ) );
}
public function cb_fn_name()
{
// выполнить действия
}
} // Конец класса WPSESampleClass
} // endif;
PHP 5+
Пожалуйста, оставьте &
в стороне. Мы уже использовали PHP 5 и выше. 🙂
Вы можете запускать события в своем классе без необходимости загружать его по умолчанию. Это удобно, если вы не хотите загружать весь класс сразу, но вам нужно получить доступ к фильтрам и действиям 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’s, но она показывает поведение в простых терминах. Может быть полезно для тех, кто смотрит на этот стиль в первый раз.
Другие методы по-прежнему могут инициализировать объект при необходимости. Я лично использую этот метод, чтобы разрешить необязательные части моего плагина подключать скрипты, но только при вызове объекта при 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
if (class_exists("AllInOneWoo")){
$all_in_one_woo = new AllInOneWoo();
}
Это работает для меня:
class foo
{
public static function bar()
{
echo 'работает!';
}
}
add_action('foo_bar', array('foo', 'bar'));
Ответ 2025 года
Вы можете абсолютно сделать это сейчас с помощью магического метода __invoke
. Этот метод вызывается, когда вы пытаетесь вызвать класс как функцию.
class MyHandler {
public function __construct(){ /* ... */ }
public function __invoke(){
// что бы вы хотели здесь сделать
}
}
add_action('admin_init', new MyHandler);
Вообще говоря, вы не добавляли бы целый класс к хуку. Хуки add_action()
/add_filter()
ожидают колбэк функции, которые могут быть вызваны из внутри класса.
Предположим, у вас есть функция init()
внутри вашего класса, которую вы хотите подключить к хуку init
в WordPress.
Поместите вызов add_action()
внутрь вашего класса и идентифицируйте колбэк следующим образом:
add_action( 'init', array( $this, 'init' ) );
(Примечание: я предполагаю, что ваш класс правильно имеет пространство имен; в противном случае обязательно используйте пространство имен для ваших колбэк-функций.)
Вы должны быть в состоянии сделать это, передав имя класса вместо созданного объекта:
add_action( 'init', array( 'MyClass', '__construct' ) );
(Теоретически, ваше другое решение также должно работать
$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );
Не уверен, почему это не работает. Может быть, если вы не вызываете по ссылке?)
Пример статического метода
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 ) {
}
}
Ответ или решение
Вопрос о том, можно ли ссылаться на класс вместо функции в функции add_action
, касается распространённой задачи интеграции объектно-ориентированного программирования с функциональными API, такими как WordPress hooks. Рассмотрим, как можно грамотно использовать методы классов в системе хуков WordPress.
Теория
В основе WordPress лежит модулярная система для добавления функциональности через хуки: add_action
и add_filter
. Эти функции предназначены для того, чтобы позволить разработчикам добавлять или изменять поведение WordPress без изменения его исходного кода.
WordPress, ожидая вызов функции, принимает разнообразные формы коллбэков. Это может быть имя функции как строка, массив, содержащий объект и имя метода, или даже анонимная функция (closure). Один из подходов, популяризируемых в сообществе WordPress, — использование методов классов в качестве коллбэков.
Пример
Давайте рассмотрим типичный пример использования метода класса с add_action
. Предположим, у нас есть класс MyClass
, содержащий метод myMethod
, который нужно связать с хуком WordPress:
class MyClass {
public function __construct() {
// Конструирование объекта может выполнять другие задачи, но не вызывать метод действия.
}
public function myMethod() {
// Реализация логики, связанной с хуком.
echo "This method is hooked into WordPress!";
}
}
$instance = new MyClass();
add_action('init', [$instance, 'myMethod']);
Применение
В приведённом выше примере конструктор класса MyClass
отвечает за инициализацию объекта, но реализация конкретной логики выносится в метод myMethod
, который подключается к хуку init
. Это обеспечивает разделение логики создания объекта и выполнения действий, которые зависят от состояния WordPress.
Плюсы:
-
Читаемость и поддерживаемость: Такой подход сохраняет код компактным и модульным. Логику можно переиспользовать, что особенно важно в масштабных проектах.
-
Инкапсуляция: Метод
myMethod
изолирован в пределах класса, что уменьшает риск побочных эффектов. -
Расширяемость: Такой дизайн позволяет легко наследовать от класса и переопределять методы, подстраивая поведение в дочерних классах.
Альтернативные способы
- Статические методы: Если нет необходимости в хранении состояния объекта, можно использовать статические методы.
class MyClass {
public static function myMethod() {
echo "Static method called!";
}
}
add_action('init', ['MyClass', 'myMethod']);
- Использование __invoke: Начиная с PHP 5.3, можно использовать магический метод
__invoke
, который позволяет обращаться к экземпляру класса как к функции.
class MyClass {
public function __construct() {
// Конструктор, возможно, подготавливает какие-то данные.
}
public function __invoke() {
echo "Invoked!";
}
}
add_action('init', new MyClass());
Заключение
С точки зрения профессиональной разработки, использование класса и методов как частей системы хуков WordPress приносит множество преимуществ. Оно улучшает структуризацию кода, способствует его лёгкому сопровождению, и делает проект более гибким для расширений. Ключевой совет — всегда стараться отделять бизнес-логику от конструкции и инициализации объекта; методы класса должны максимально фокусироваться на выполнении своих узких задач, что соответствует принципам ООП и современной разработки.