referensi add_action kelas

38

Apakah mungkin untuk mereferensikan kelas alih-alih fungsi di 'add_action'? Sepertinya tidak bisa memahaminya. Berikut ini hanya contoh dasar dari fungsi yang dimaksud.

add_action( 'admin_init', 'MyClass' );
class MyClass {
     function __construct() {
          .. This is where stuff gets done ..
     }
}

Jadi ya, itu tidak berhasil. Saya juga sudah mencoba:

$var = new MyClass();
add_action( 'admin_init', array( &$var ) );

Dan:

$var = new MyClass();
add_action( 'admin_init', array( &$var, '__construct' ) );

Dan juga:

add_action( 'admin_init', 'MyClass::__construct' );

Apakah ada yang bisa saya lakukan ini tanpa harus membuat fungsi terpisah yang memuat kelas? Saya ingin dapat menjalankan konstruktor kelas melalui add_action. Itu saja yang perlu dimuat untuk mendapatkan bola bergulir.

Matthew Ruddy
sumber

Jawaban:

69

Tidak, Anda tidak dapat 'menginisialisasi' atau membuat instance kelas melalui hook, tidak secara langsung. Beberapa kode tambahan selalu diperlukan (dan itu bukan hal yang diinginkan untuk dapat melakukan itu, karena Anda membuka sekaleng cacing untuk Anda sendiri.

Inilah cara yang lebih baik untuk melakukannya:

class MyClass {
     function __construct() {
          add_action( 'admin_init',array( $this, 'getStuffDone' ) );
     }
     function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
$var = new MyClass();

Tentu saja orang dapat membuat kelas antarmuka untuk menyederhanakannya untuk kasus umum lebih jauh:

class IGetStuffDone {
    function IGetStuffDone(){
        add_action( 'admin_init',array( $this, 'getStuffDone' ) );
    }
    public abstract function getStuffDone();
}

Perhatikan bahwa sebagai antarmuka, Anda tidak dapat membuat objek jenis ini secara langsung, tetapi Anda bisa membuat sub-kelas, membiarkan Anda mengatakan:

class CDoingThings extends IGetStuffDone {
    function getStuffDone(){
        // doing things
    }
}
$var = new CDoingThings();

Yang kemudian akan secara otomatis menambahkan semua kait, Anda hanya perlu mendefinisikan apa yang sebenarnya dilakukan dalam subkelas dan kemudian membuatnya!

Pada Konstruktor

Saya tidak akan menambahkan konstruktor sebagai fungsi pengait, ini praktik yang buruk, dan dapat menyebabkan banyak peristiwa yang tidak biasa. Juga di sebagian besar bahasa, konstruktor mengembalikan objek yang sedang dipakai, jadi jika kait Anda perlu mengembalikan sesuatu seperti dalam filter, itu tidak akan mengembalikan variabel yang difilter seperti yang Anda inginkan, tetapi sebaliknya akan mengembalikan objek kelas.

Memanggil konstruktor atau destruktor adalah praktik pemrograman yang sangat, sangat, sangat buruk, apa pun bahasa Anda, dan tidak boleh dilakukan.

Konstruktor juga harus membangun objek, untuk menginisialisasi mereka siap untuk digunakan, bukan untuk pekerjaan yang sebenarnya. Pekerjaan yang harus dilakukan oleh objek harus dalam fungsi yang terpisah.

Metode kelas statis, dan tidak perlu instantiate / inisialisasi sama sekali

Jika metode kelas Anda adalah metode kelas statis, Anda bisa memberikan nama kelas dalam tanda kutip daripada $thisseperti yang ditunjukkan di bawah ini:

class MyClass {
     public static function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
add_action( 'admin_init', array('MyClass','getStuffDone' ) );

Penutupan & PHP 5.3

Sayangnya, Anda tidak dapat menghindari garis yang membuat kelas baru. Satu-satunya solusi untuk melewatkannya akan melibatkan kode pelat boiler yang masih memiliki garis itu, dan akan membutuhkan PHP 5.3+ misalnya:

add_action('admin_init',function(){
    $var = new MyClass();
    $var->getStuffDone();
});

Pada titik mana Anda dapat melewati kelas, dan cukup gunakan fungsi:

add_action('admin_init',function(){
    // do stuff
});

Namun perlu diingat Anda sekarang telah memperkenalkan momok fungsi anonim. Tidak ada cara untuk menghapus tindakan di atas menggunakan remove_action, dan ini dapat dan memang menyebabkan rasa sakit yang luar biasa bagi pengembang yang harus bekerja dengan kode orang lain.

Di Ampersands

Anda mungkin melihat tindakan yang digunakan seperti ini:

array( &$this, 'getStuffDone' );

Ini buruk . &ditambahkan kembali di PHP 4 ketika objek dilewatkan sebagai nilai, bukan sebagai referensi. PHP 4 berumur lebih dari satu dekade, dan belum didukung oleh WordPress dalam waktu yang lama.

Tidak ada alasan untuk digunakan &thissaat menambahkan kait dan filter, dan menghapus referensi tidak akan menyebabkan masalah, dan bahkan dapat meningkatkan kompatibilitas dengan versi PHP yang akan datang

Gunakan ini sebagai gantinya:

array( $this, 'getStuffDone' );
Tom J Nowell
sumber
Baik. Terima kasih dari semua itu; benar-benar belajar sedikit. Saya benar-benar hanya merasa nyaman di PHP berbasis kelas sekarang. Ini adalah apa yang telah saya lakukan, dan itu berhasil, tetapi bisakah Anda memberi tahu saya jika ini merupakan praktik buruk / salah dengan cara apa pun? Saya telah menginisiasi kelas di dalam fungsi statis, di dalam kelas itu sendiri. Kemudian direferensikan fungsi statis di add_action. Lihat tautan ini: pastebin.com/0idyPwwY
Matthew Ruddy
ya Anda bisa melakukannya dengan cara itu, meskipun saya bisa menghindari menggunakan $classsebagai nama variabel Anda, kata-kata itu cenderung dicadangkan. Saya pikir Anda akan keluar dari jalan Anda untuk menghindari mengatakan sesuatu yang serupa dengan $x = new Y();dalam lingkup global, dan Anda menambahkan kompleksitas di mana tidak ada yang diperlukan. Upaya Anda untuk mengurangi jumlah kode yang ditulis telah melibatkan penulisan lebih banyak kode!
Tom J Nowell
Saya akan tunjukkan dalam semua kasus di atas, Anda akan lebih baik menggunakan fungsi daripada kelas, karena kelas itu akan tetap dibuang dan melayani tujuan yang sama. Buang-buang sumber daya. Ingat, ada biaya untuk membuat dan menghancurkan objek, Anda ingin beberapa objek, dan Anda ingin mereka berumur panjang
Tom J Nowell
Poin bagus. Mengubah cara saya melihatnya. Saya pikir saya akan menyebutnya dalam ruang lingkup global, menghindari kode tambahan.
Matthew Ruddy
1
Saya ingin menambahkan bahwa jika Anda telah menempatkan kelas Anda di namespace, Anda harus menambahkannya juga atau add_action () / add_filter () tidak akan menemukannya - seperti ini:add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
jschrab
10

Kelas contoh

Catatan:

  • Init kelas hanya sekali
    • Panggil prioritas 0, sehingga Anda dapat menggunakan hook yang sama dengan prioritas default nanti
    • Membungkusnya dalam ! class_existsuntuk menghindari memanggilnya dua kali dan menempatkan pemanggil init di dalam
  • Buat initfungsi dan kelas varstatic
  • Panggil konstruktor dari dalam init Anda, ketika Anda memanggil kelas new self.

Ini sebuah contoh

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+

Tolong , tinggalkan &. Kami sudah melampaui php4. :)

kaisar
sumber
2
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();
}
Zakir Sajib
sumber
Harap edit jawaban Anda , dan tambahkan penjelasan: mengapa itu bisa menyelesaikan masalah?
fuxia
0

Anda dapat memicu peristiwa di kelas Anda tanpa harus memuatnya terlebih dahulu . Ini berguna jika Anda tidak ingin memuat kelas penuh di muka, tetapi perlu mengakses filter dan tindakan WordPress.

Berikut ini contoh yang sangat sederhana

<?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();

Ini adalah versi yang sangat sederhana dari jawaban Kaiser tetapi menunjukkan secara sederhana perilakunya. Mungkin berguna bagi mereka yang melihat gaya ini untuk pertama kalinya.

Metode lain masih dapat memulai objek jika diperlukan. Saya pribadi menggunakan metode ini untuk memungkinkan bagian opsional plugin saya untuk membuat skrip, tetapi hanya memicu objek pada permintaan AJAX.

John Reid
sumber
0

Ini bekerja untuk saya:

class foo
{
    public static function bar()
    {
        echo 'it works!';
    }
}

add_action('foo_bar', array('foo', 'bar'));
Jonathan
sumber
0

Secara umum, Anda tidak akan menambahkan seluruh kelas ke hook. The add_action()/ add_filter()kait berharap callback fungsi , yang dapat dirujuk dari dalam kelas .

Katakanlah Anda memiliki init()fungsi di dalam kelas Anda, yang ingin Anda kaitkan ke initkait WordPress .

Masukkan add_action()panggilan Anda di dalam kelas Anda, dan kemudian identifikasi panggilan balik seperti itu:

add_action( 'init', array( $this, 'init' ) );

(Catatan: Saya berasumsi bahwa kelas Anda menggunakan namespace dengan benar; jika tidak, pastikan untuk namespace fungsi panggilan balik Anda.)

Chip Bennett
sumber
Bagaimana jika kelas saat ini belum dimulai? Saya mencoba menggunakan add_action untuk benar-benar memulai, jadi saya tidak perlu menambahkan $ var = new MyClass (); sebelumnya di tempat lain.
Matthew Ruddy
Bisakah Anda tidak hanya menulis panggilan balik untuk membuat instance kelas Anda di init(atau di mana pun Anda membutuhkannya)?
Chip Bennett
0

Anda harus dapat melakukannya dengan meneruskan nama kelas alih-alih objek yang dipakai:

add_action( 'init', array( 'MyClass', '__construct' ) );

(Secara teori, solusi Anda yang lain harus bekerja juga

$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );

Tidak yakin mengapa tidak. Mungkin jika Anda tidak menelepon dengan referensi?)

Boone Gorges
sumber
Yang pertama tidak berhasil. Jadikan halaman kosong. Karya kedua, tetapi hanya karena kelas telah diinisiasi di baris pertama. Ini semacam kekalahan tujuan menambahkan tindakan, karena kelas telah dimulai. Tapi itu tidak melakukan apa yang saya coba lakukan, yang memulai kelas melalui tindakan. Dalam kode aktual yang saya kerjakan, tindakan tersebut bukan 'admin_init' tetapi tindakan khusus di dalam kelas lain. Saya tidak ingin fungsi 'MyClass' dimulai jika tindakan di kelas lain tidak ada. Maaf jika saya melewatkan sesuatu; belajar sambil jalan
Matthew Ruddy
Ya, saya salah. Itu hanya berfungsi jika Anda memanggil initmetode statis . Lihat wordpress.stackexchange.com/a/48093/14052
Boone Gorges