add_action ссылается на класс
7 ответ
- голосов
-
- 2012-04-05
Нет,вы не можете «инициализировать» или создать экземпляр класса через ловушку,а не напрямую. Всегда требуется дополнительный код (а это нежелательно,поскольку вы открываете для себя банку с червями.
Вот лучший способ сделать это:
class MyClass { function __construct() { add_action( 'admin_init', [ $this, 'getStuffDone' ] ); } function getStuffDone() { // .. This is where stuff gets done .. } } $var = new MyClass();
Конечно,можно было бы создать класс интерфейса,чтобы еще больше упростить его для общего случая:
class IGetStuffDone { function IGetStuffDone(){ add_action( 'admin_init', [ $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', [ __NAMESPACE__ . '\MyClass','getStuffDone' ] );
Обратите внимание на использование
__NAMESPACE__
,которое требуется,если ваш класс находится внутри пространства имен.< sizesЗащита
К сожалению,вы не можете избежать строки,создающей новый класс. Единственное другое решение,чтобы пропустить его,- это код котельной плиты,в котором все еще есть эта строка,например:
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Используйте вместо этого:
[ $this, 'getStuffDone' ]
No, you cannot 'initialise' or instantiate the class through a hook, not directly. Some additional code is always required ( and it is not a desirable thing to be able to do that, as you're opening a can of worms for yourself.
Here is a better way of doing it:
class MyClass { function __construct() { add_action( 'admin_init', [ $this, 'getStuffDone' ] ); } function getStuffDone() { // .. This is where stuff gets done .. } } $var = new MyClass();
Of course one could create an interface class to simplify it for the general case even further:
class IGetStuffDone { function IGetStuffDone(){ add_action( 'admin_init', [ $this, 'getStuffDone' ] ); } public abstract function getStuffDone(); }
Note that as an interface, you can't create an object of this type directly, but you could create a sub-class, letting you say:
class CDoingThings extends IGetStuffDone { function getStuffDone(){ // doing things } } $var = new CDoingThings();
Which would then automatically add all the hooks, you just need to define what exactly is being done in a subclass and then create it!
On Constructors
I wouldn't add a constructor as a hook function, it's bad practice, and can lead ot a lot of unusual events. Also in most languages a constructor returns the object that is being instantiated, so if your hook needs to return something like in a filter, it will not return the filtered variable as you want, but instead it will return the class object.
Directly calling a constructor or a destructor is very, very, very bad programming practice, no matter which language you're in, and should never be done.
Constructors should also construct objects, to initialise them ready for use, not for actual work. Work to be done by the object should be in a separate function.
Static class methods, and not needing to instantiate/initialise at all
If your class method is a static class method, you can pass the name of the class in quotes rather than
$this
as shown below:class MyClass { public static function getStuffDone() { // .. This is where stuff gets done .. } } add_action( 'admin_init', [ __NAMESPACE__ . '\MyClass','getStuffDone' ] );
Note the use of
__NAMESPACE__
which is required if your class is inside a namespace.Closures
Sadly you cannot avoid the line creating the new class. The only other solution to skipping it would involve boiler plate code that still has that line e.g.:
add_action( 'admin_init', function() { $var = new MyClass(); $var->getStuffDone(); } );
At which point you may as well skip the class, and just use a function:
add_action( 'admin_init', function() { // do stuff } );
But keep in mind you have now introduced the spectre of anonymous functions. There is no way to remove the above action using
remove_action
, and this can and does cause great pain for developers who have to work with other peoples code.On Ampersands
You may see actions used like this:
array( &$this, 'getStuffDone' )
This is bad.
&
was added back in PHP 4 when objects were passed as values, not as references. PHP 4 is more than a decade old, and hasn't been supported by WordPress in a very long time.There is no reason to use
&this
when adding hooks and filters, and removing the reference will cause no issues, and may even improve compatibility with future versions of PHPUse this instead:
[ $this, 'getStuffDone' ]
-
Хорошо.Спасибо вам за все это;действительно узнал совсем немного.Я действительно только сейчас чувствую себя комфортно в PHP на основе классов.Это то,что я сделал,и это работает,но не могли бы вы сказать мне,является ли это плохой практикой/неправильным в любом случае?Я инициировал класс внутри статической функции,внутри самого класса.Затем обратился к статической функции в add_action.См. Эту ссылку: http://pastebin.com/0idyPwwYOk. Thank you from all of that; really learnt quite a bit. I'm really only getting comfortable in class based PHP now. This is what I have done, and it works, but could you tell me if it is bad practice/incorrect in any way? I've initiated the class inside a static function, within the class itself. Then referenced the static function in the add_action. See this link: http://pastebin.com/0idyPwwY
- 0
- 2012-04-05
- Matthew Ruddy
-
да,вы могли бы сделать это таким образом,хотя я мог бы избежать использования `$ class` в качестве имени вашей переменной,эти слова,как правило,зарезервированы.Я думаю,вы изо всех сил стараетесь не говорить чего-то похожего на `$ x=new Y ();` в глобальной области видимости,и вы добавляете сложность там,где в этом нет необходимости.Ваша попытка уменьшить объем написанного кода потребовала написания большего количества кода!yes you could do it that way, though I could avoid using `$class` as your variable name, those words tend to be reserved. I think you're going way out of your way to avoid saying something similar to `$x = new Y();` in the global scope, and you're adding complexity where none is necessary. Your attempt to reduce the amount of code written has involved writing more code!
- 0
- 2012-04-05
- Tom J Nowell
-
Я бы отметил,что во всех вышеперечисленных случаях лучше использовать функцию,а не класс,поскольку этот класс в любом случае будет отброшен и служит той же цели.Это пустая трата ресурсов.Помните,что создание и уничтожение объекта требует определенных затрат,вам нужно несколько объектов и вы хотите,чтобы они оставались долгоживущими.I'd point out in all of the above cases, you would be better off using a function rather than a class, as that class will be discarded anyway and serves the same purpose. It's a waste of resources. Remember, there is a cost to creating and destroying an object, you want few objects, and you want them to be long lived
- 0
- 2012-04-05
- Tom J Nowell
-
Хорошая точка зрения.Изменил то,как я на это смотрел.Я думаю,что вместо этого назову его в глобальной области видимости,избегая лишнего кода.Good point. Changed the way I've been looking at it. I think I'll call it in the global scope instead, avoiding the extra code.
- 0
- 2012-04-05
- Matthew Ruddy
-
Я хотел бы добавить,что если вы поместили свой класс в пространство имен,вам придется добавить и его,иначе add_action ()/add_filter () его не найдет - вот так: `` add_action ('admin_init',array ('MyNamespace \ MyClass','getStuffDone')); ``I would like to add that if you happen to have put your class in a namespace, you will have to add that too or add_action()/add_filter() won't find it - like this: ```add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );```
- 1
- 2018-04-27
- jschrab
-
- 2012-04-05
Пример класса
Примечания:
- Инициировать класс только один раз
- Вызовите приоритет 0,чтобы позже можно было использовать тот же самый ловушку с приоритетом по умолчанию.
- Оберните его в
! 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. :)Example class
Notes:
- Init the class only once
- Call on priority 0, so you can use the same hook with the default priority later
- Wrap it up in a
! class_exists
to avoid calling it twice and place the init caller inside
- Make the
init
function and the class varstatic
- Call the constructor from inside your init, when you call the class
new self
.
Here's an example
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+
Please, leave the
&
out. We're already beyond php4. :) -
- 2018-03-04
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(); }
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(); }
-
Пожалуйста,** [отредактируйте] свой ответ ** и добавьте пояснение: ** почему ** это может решить проблему?Please **[edit] your answer**, and add an explanation: **why** could that solve the problem?
- 0
- 2018-03-04
- fuxia
-
-
- 2012-04-05
Вообще говоря,вы не добавляете в ловушку целый класс .Перехватчики
add_action()
/add_filter()
ожидают функций обратного вызова,на которые можно ссылаться из внутрикласс .Допустим,у вас есть функция
init()
внутри вашего класса,которую вы хотите подключить к хуку WordPressinit
.Поместите вызов
add_action()
внутри класса,а затем определите обратный вызов следующим образом:add_action( 'init', array( $this, 'init' ) );
(Примечание: я предполагаю,что ваш класс имеет правильное пространство имен; в противном случае обязательно укажите пространство имен для ваших функций обратного вызова.)
Generally speaking, you wouldn't add an entire class to a hook. The
add_action()
/add_filter()
hooks expect callback functions, which can be referenced from within a class.Let's say that you have an
init()
function inside your class, that you want to hook into the WordPressinit
hook.Put your
add_action()
call inside your class, and then identify the callback like so:add_action( 'init', array( $this, 'init' ) );
(Note: I'm assuming your class is properly namespaced; otherwise, be sure to namespace your callback functions.)
-
Что делать,если текущий класс еще не был инициирован?Я пытался использовать add_action для фактического запуска,поэтому мне не нужно добавлять $ var=new MyClass ();заранее в другом месте.What about if the current class hasn't already been initiated? I was trying to use add_action to actually initiate, so I don't have to add $var = new MyClass(); beforehand elsewhere.
- 0
- 2012-04-05
- Matthew Ruddy
-
Можете ли вы просто написать обратный вызов для создания экземпляра вашего класса в `init` (или там,где вам это нужно)?Can you not just write a callback to instantiate your class at `init` (or wherever you need it)?
- 0
- 2012-04-05
- Chip Bennett
-
- 2012-04-05
Вы можете сделать это,передав имя класса вместо созданного объекта:
add_action( 'init', array( 'MyClass', '__construct' ) );
(Теоретически другое решение тоже должно работать
$var = new MyClass(); add_action( 'admin_init', array( $var, '__construct' ) );
Не понимаю,почему это не так.Может если по ссылке не позвонить?)
You should be able to do it by passing the class name instead of the instantiated object:
add_action( 'init', array( 'MyClass', '__construct' ) );
(In theory, your other solution should work too
$var = new MyClass(); add_action( 'admin_init', array( $var, '__construct' ) );
Not sure off the head why it doesn't. Maybe if you don't call by reference?)
-
Первый не работает.Просто делает страницу пустой.Второй работает,но только потому,что класс был инициирован в первой строке.Это своего рода поражение цели добавления действия,потому что класс уже был инициирован.Но он не делает того,что я пытаюсь сделать,а именно инициировать класс через действие.В фактическом коде,над которым я работаю,действие не «admin_init»,а настраиваемое действие в другом классе.Я не хочу,чтобы функция MyClass запускалась,если действия в другом классе нет.Извините,если я что-то упускаю;учусь на ходуFirst one doesn't work. Just makes the page go blank. The second works, but only because the class has been initiated in the first line. It sort of defeats the purpose of adding the action, because the class has already been initiated. But it doesn't do what I'm trying to do, which is initiate the class through the action. In the actual code I'm working on, the action isn't 'admin_init' but a custom action within another class. I don't want the function 'MyClass' to be initiated if the action in the other class isn't there. Sorry if I'm missing something; learning as I go
- 0
- 2012-04-05
- Matthew Ruddy
-
Да,я ошибался.Это работает,только если вы вызываете статический методinit.См. Http://wordpress.stackexchange.com/a/48093/14052Yeah, I was wrong. That only works if you're calling a static `init` method. See http://wordpress.stackexchange.com/a/48093/14052
- 0
- 2012-04-05
- Boone Gorges
-
- 2014-12-22
Вы можете инициировать события в своем классе без необходимости загружать его изначально .Это удобно,если вы не хотите загружать полный класс заранее,но вам нужен доступ к фильтрам и действиям 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.
You can trigger events in your class without the need to load it initially. This is handy if you don't want to load the full class up front, but need to access the WordPress filters and actions.
Here's a very simple example
<?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();
This is a very simplified version of Kaiser's answer but shows in simple terms the behaviour. Might be handy for those looking at this style for the first time.
Other methods can still initiate the object if required. I'm personally using this method to allow optional parts of my plugin to enqueue scripts, but only triggering the object on an AJAX request.
Можно ли ссылаться на класс вместо функции в add_action?Кажется,не могу понять.Вот лишь базовый пример рассматриваемой функции.
Так что да,это не сработает.Я также пробовал:
И:
А также:
Могу ли я сделать это без создания отдельной функции,загружающей класс?Я хотел бы иметь возможность просто запустить конструктор классов через add_action.Это все,что нужно загрузить,чтобы начать работу.