Referință la o clasă în add_action

5 apr. 2012, 16:44:22
Vizualizări: 92K
Voturi: 58

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.

0
Toate răspunsurile la întrebare 9
6
97

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' ]
5 apr. 2012 17:16:22
Comentarii

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

Matthew Ruddy Matthew Ruddy
5 apr. 2012 17:35:30

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!

Tom J Nowell Tom J Nowell
5 apr. 2012 17:45:59

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

Tom J Nowell Tom J Nowell
5 apr. 2012 17:47:28

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.

Matthew Ruddy Matthew Ruddy
5 apr. 2012 18:09:41

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' ) );

jschrab jschrab
28 apr. 2018 01:01:39

Ce se întâmplă dacă add_action() ar fi în interiorul construct() în aceeași clasă?

MMTDesigner MMTDesigner
19 iun. 2021 15:45:20
Arată celelalte 1 comentarii
0
12

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. :)

5 apr. 2012 17:16:16
0

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.

22 dec. 2014 16:02:08
1
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();
}
4 mar. 2018 10:14:23
Comentarii

Te rog [editează] răspunsul tău și adaugă o explicație: de ce ar putea rezolva această problemă?

fuxia fuxia
4 mar. 2018 16:06:06
0

Aceasta funcționează pentru mine:

class foo
{
    public static function bar()
    {
        echo 'funcționează!';
    }
}

add_action('foo_bar', array('foo', 'bar'));
17 mar. 2015 20:25:38
1

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);
5 mar. 2025 22:22:13
Comentarii

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.

Howdy_McGee Howdy_McGee
5 mar. 2025 22:31:07
2

Î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.)

5 apr. 2012 17:03:00
Comentarii

Ș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.

Matthew Ruddy Matthew Ruddy
5 apr. 2012 17:08:41

Nu poți pur și simplu să scrii un callback pentru a instantia clasa ta la init (sau oriunde ai nevoie de ea)?

Chip Bennett Chip Bennett
5 apr. 2012 18:55:28
2

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ță?)

5 apr. 2012 17:09:54
Comentarii

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.

Matthew Ruddy Matthew Ruddy
5 apr. 2012 17:15:26

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

Boone Gorges Boone Gorges
5 apr. 2012 18:17:59
0

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 ) {
    }
}
16 sept. 2023 10:25:33