Referință la o clasă în add_action
Este posibil să referențiez o clasă în loc de o funcție în 'add_action'? Nu reușesc să-mi dau seama cum. Iată un exemplu simplu al funcției în cauză.
add_action( 'admin_init', 'MyClass' );
class MyClass {
function __construct() {
.. Aici se întâmplă lucrurile importante ..
}
}
Deci, da, asta nu funcționează. Am încercat și:
$var = new MyClass();
add_action( 'admin_init', array( &$var ) );
Și:
$var = new MyClass();
add_action( 'admin_init', array( &$var, '__construct' ) );
De asemenea:
add_action( 'admin_init', 'MyClass::__construct' );
Există vreo modalitate de a face asta fără a crea o funcție separată care să încarce clasa? Aș dori să pot rula pur și simplu constructorul clasei prin add_action. Asta e tot ce trebuie încărcat pentru a începe procesul.

Nu, nu este posibil să 'inițializezi' sau să instanțiezi clasa prin intermediul unui hook, nu direct.
Este întotdeauna necesar cod suplimentar (și nu este un lucru de dorit să poți face asta, deoarece deschizi o cutie a Pandorei pentru tine însuți.
Iată câteva metode mai bune de a face acest lucru:
class MyClass {
function __construct() {
add_action( 'admin_init', [ $this, 'getStuffDone' ] );
}
function getStuffDone() {
// .. Aici se întâmplă lucrurile ..
}
}
$var = new MyClass();
Desigur, s-ar putea crea o clasă de interfață pentru a simplifica și mai mult cazul general:
class IGetStuffDone {
function IGetStuffDone(){
add_action( 'admin_init', [ $this, 'getStuffDone' ] );
}
public abstract function getStuffDone();
}
Rețineți că, fiind o interfață, nu puteți crea un obiect de acest tip direct, dar puteți crea o subclasă, permițându-vă să spuneți:
class CDoingThings extends IGetStuffDone {
function getStuffDone(){
// facând lucruri
}
}
$var = new CDoingThings();
Ceea ce ar adăuga automat toate hook-urile, trebuie doar să definiți ce anume se întâmplă într-o subclasă și apoi să o creați!
Despre Constructori
Nu aș adăuga un constructor ca funcție de hook, este o practică proastă și poate duce la multe evenimente neobișnuite. De asemenea, în majoritatea limbajelor, un constructor returnează obiectul care este instanțiat, așa că dacă hook-ul dvs. trebuie să returneze ceva, ca într-un filtru, nu va returna variabila filtrată așa cum doriți, ci va returna obiectul clasei.
Apelarea directă a unui constructor sau a unui destructor este o practică de programare foarte, foarte, foarte proastă, indiferent de limbajul în care lucrați și nu ar trebui făcută niciodată.
Constructorii ar trebui, de asemenea, să construiască obiecte, să le inițializeze pentru utilizare, nu pentru muncă efectivă. Munca care trebuie efectuată de obiect ar trebui să fie într-o funcție separată.
Metode statice de clasă și fără necesitatea de a instanția/inițializa deloc
Dacă metoda dvs. de clasă este o metodă statică de clasă, puteți trece numele clasei între ghilimele în loc de $this
, așa cum este arătat mai jos:
class MyClass {
public static function getStuffDone() {
// .. Aici se întâmplă lucrurile ..
}
}
add_action( 'admin_init', [ __NAMESPACE__ . '\MyClass','getStuffDone' ] );
Rețineți utilizarea __NAMESPACE__
care este necesară dacă clasa dvs. se află într-un namespace.
Closures
Din păcate, nu puteți evita linia care creează noua clasă. Singura altă soluție pentru a o omite ar implica cod boilerplate care totuși are acea linie, de exemplu:
add_action( 'admin_init', function() {
$var = new MyClass();
$var->getStuffDone();
} );
În acest moment, ați putea sări peste clasă și să utilizați doar o funcție:
add_action( 'admin_init', function() {
// faci lucruri
} );
Dar țineți cont că ați introdus spectrul funcțiilor anonime. Nu există nicio modalitate de a elimina acțiunea de mai sus folosind remove_action
, iar acest lucru poate și provoacă mari probleme dezvoltatorilor care trebuie să lucreze cu codul altor persoane.
Despre Ampersand
S-ar putea să vedeți acțiuni folosite astfel:
array( &$this, 'getStuffDone' )
Aceasta este o practică proastă. &
a fost adăugat în PHP 4 când obiectele erau transmise ca valori, nu ca referințe. PHP 4 are mai mult de un deceniu și nu este suportat de WordPress de foarte mult timp.
Nu există niciun motiv să utilizați &this
atunci când adăugați hook-uri și filtre, iar eliminarea referinței nu va cauza probleme și poate chiar îmbunătăți compatibilitatea cu versiunile viitoare ale PHP.
Utilizați în schimb:
[ $this, 'getStuffDone' ]

Ok. Mulțumesc pentru toate acestea; am învățat destul de multe. Abia acum încep să mă simt confortabil cu PHP bazat pe clase. Iată ce am făcut, și funcționează, dar poți să-mi spui dacă este o practică proastă/greșită în vreun fel? Am inițiat clasa în interiorul unei funcții statice, în cadrul clasei în sine. Apoi am referențiat funcția statică în add_action. Vezi aici linkul: http://pastebin.com/0idyPwwY

da, ai putea face astfel, deși aș evita să folosești $class
ca nume de variabilă, aceste cuvinte tind să fie rezervate. Cred că te complici mult pentru a evita să scrii ceva similar cu $x = new Y();
în scope-ul global, și adaugi complexitate unde nu este necesară. Încercarea ta de a reduce cantitatea de cod scris a implicat scrierea mai mult cod!

Aș menționa că în toate cazurile de mai sus, ar fi mai bine să folosești o funcție decât o clasă, deoarece acea clasă va fi eliminată oricum și servește același scop. Este o risipă de resurse. Amintește-ți, există un cost pentru crearea și distrugerea unui obiect, vrei să ai puține obiecte și să fie longevive

Bun punct de vedere. Am schimbat modul în care m-am uitat la asta. Cred că o voi apela în scope-ul global, evitând codul suplimentar.

Aș dori să adaug că dacă ai plasat clasa într-un namespace, va trebui să-l adaugi și pe acesta, altfel add_action()/add_filter() nu o vor găsi - astfel: add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );

Exemplu de clasă
Note:
- Inițializează clasa doar o dată
- Apelează la prioritatea 0, astfel încât să poți folosi același hook cu prioritatea implicită mai târziu
- Înfășoară-o într-un
! class_exists
pentru a evita apelarea de două ori și plasează inițializarea în interior
- Fă funcția
init
și variabila de clasăstatic
- Apelează constructorul din interiorul funcției init, când apelezi clasa
new self
.
Iată un exemplu
if ( ! class_exists( 'WPSESampleClass' ) )
{
// Inițializează clasa la prioritatea 0 pentru a evita adăugarea de prioritate în interiorul clasei ca implicit = 10
add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );
class WPSESampleClass
{
/**
* Obiectul Clasei
*/
static private $class = null;
public static function init()
{
if ( null === self::$class )
self :: $class = new self;
return self :: $class;
}
public function __construct()
{
// faci lucruri precum adăugarea de acțiuni:
add_action( 'init', array( $this, 'cb_fn_name' ) );
}
public function cb_fn_name()
{
// faci lucruri
}
} // Sfârșitul Clasei WPSESampleClass
} // endif;
Php 5+
Te rog, lasă &
afară. Suntem deja dincolo de php4. :)

Puteți declanșa evenimente în clasa dvs. fără a fi nevoie să o încărcați inițial. Acest lucru este util dacă nu doriți să încărcați întreaga clasă din start, dar aveți nevoie să accesați filtrele și acțiunile WordPress.
Iată un exemplu foarte simplu
<?php
class MyClass
{
public static function init()
{
add_filter( 'the_content', array('MyClass', 'myMethod') );
}
public static function myMethod($content)
{
$content = $content . 'Funcționează fără a încărca obiectul';
}
}
MyClass::init();
Aceasta este o versiune foarte simplificată a răspunsului lui Kaiser, dar arată în termeni simpli comportamentul. Poate fi util pentru cei care abordează acest stil pentru prima dată.
Alte metode pot totuși iniția obiectul dacă este necesar. Personal, folosesc această metodă pentru a permite părților optionale ale plugin-ului meu să încarce scripturi, dar declanșând obiectul doar la o cerere 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 }
}// sfârșitul clasei
}// sfârșitul condiției
if (class_exists("AllInOneWoo")){
$all_in_one_woo = new AllInOneWoo();
}

Răspuns 2025
Puteți face asta acum cu ajutorul __invoke
magic method. Această metodă este apelată atunci când încercați să apelați clasa ca pe o funcție.
class MyHandler {
public function __construct(){ /* ... */ }
public function __invoke(){
// orice operațiuni doriți să efectuați aici
}
}
add_action('admin_init', new MyHandler);

Deși cred că acesta este un răspuns ingenios, ar trebui să ne gândim dacă aceasta este într-adevăr cea mai bună abordare pentru cazul nostru specific. Personal, nu îmi pot imagina o situație în care această metodă ar fi mai ușor de înțeles sau "mai bună" decât oricare dintre alternativele prezentate în acest subiect.

În general, nu ai adăuga o întreagă clasă unui hook. Hook-urile add_action()
/add_filter()
se așteaptă la funcții callback, care pot fi referențiate dintr-o clasă.
Să presupunem că ai o funcție init()
în interiorul clasei tale, pe care vrei să o conectezi la hook-ul WordPress init
.
Pune apelul add_action()
în interiorul clasei tale, apoi identifică callback-ul astfel:
add_action( 'init', array( $this, 'init' ) );
(Notă: Presupun că clasa ta are un namespace corect; în caz contrar, asigură-te că funcțiile tale callback au namespace.)

Și dacă clasa curentă nu a fost deja inițializată? Încercam să folosesc add_action pentru a o iniția efectiv, ca să nu fiu nevoit să adaug $var = new MyClass(); în prealabil altundeva.

Ar trebui să poți face acest lucru prin transmiterea numelui clasei în locul obiectului instanțiat:
add_action( 'init', array( 'MyClass', '__construct' ) );
(Teoretic, cealaltă soluție ar trebui să funcționeze și ea
$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );
Nu sunt sigur de ce nu funcționează exact. Poate dacă nu apelezi prin referință?)

Prima variantă nu funcționează. Pur și simplu face ca pagina să devină goală. A doua funcționează, dar doar pentru că clasa a fost inițializată în prima linie. Într-un fel, își pierde scopul de a adăuga acțiunea, deoarece clasa a fost deja inițializată. Dar nu face ceea ce încerc să realizez, adică să inițializez clasa prin acțiune. În codul real la care lucrez, acțiunea nu este 'admin_init' ci o acțiune personalizată în cadrul unei alte clase. Nu vreau ca funcția 'MyClass' să fie inițializată dacă acțiunea din cealaltă clasă nu există. Scuze dacă omit ceva; învăț pe parcurs.

Da, am greșit. Asta funcționează doar dacă apelezi o metodă statică init
. Vezi http://wordpress.stackexchange.com/a/48093/14052

Exemplu de metodă statică
class FrmTransLiteEntriesController {
/**
* Include detalii de plată în bara laterală a intrării.
*
* @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 ) {
}
}
