Tiba di sini tepat 2 tahun setelah pertanyaan awal diajukan, ada beberapa hal yang ingin saya tunjukkan. (Jangan tanya saya untuk menunjukkan banyak hal , selamanya).
Kait yang tepat
Untuk membuat instantiate kelas plugin, hook yang tepat harus digunakan. Tidak ada aturan umum untuk itu, karena itu tergantung pada apa yang dilakukan kelas.
Menggunakan hook yang sangat awal seperti "plugins_loaded"
sering tidak masuk akal karena hook seperti itu dipecat untuk permintaan admin, frontend dan AJAX, tetapi sangat sering hook yang lebih baru jauh lebih baik karena memungkinkan untuk instantiate kelas plugin hanya jika diperlukan.
Misalnya kelas yang melakukan hal-hal untuk templat dapat digunakan di "template_redirect"
.
Secara umum sangat jarang suatu kelas perlu dipakai sebelum "wp_loaded"
dipecat.
Tidak Ada Kelas Dewa
Sebagian besar dari semua kelas yang digunakan sebagai contoh dalam jawaban yang lebih lama menggunakan kelas bernama like "Prefix_Example_Plugin"
atau "My_Plugin"
... Ini menunjukkan bahwa mungkin ada kelas utama untuk plugin.
Ya, kecuali sebuah plugin dibuat oleh satu kelas tunggal (dalam hal ini penamaan setelah nama plugin benar-benar masuk akal), untuk membuat kelas yang mengelola seluruh plugin (misalnya menambahkan semua kait yang dibutuhkan plugin atau membuat instance semua kelas plugin lainnya ) dapat dianggap sebagai praktik buruk, sebagai contoh objek dewa .
Dalam kode pemrograman berorientasi objek harus cenderung SOLID di mana "S" berdiri untuk "Prinsip tanggung jawab tunggal" .
Ini berarti bahwa setiap kelas harus melakukan satu hal. Dalam pengembangan plugin WordPress itu berarti bahwa pengembang harus menghindari untuk menggunakan kait tunggal untuk membuat instantiate kelas plugin utama , tetapi kait yang berbeda harus digunakan untuk instantiate kelas yang berbeda, sesuai dengan tanggung jawab kelas.
Hindari kait di konstruktor
Argumen ini telah diperkenalkan dalam jawaban lain di sini, namun saya ingin berkomentar konsep ini dan menghubungkan jawaban lain ini di mana telah dijelaskan secara luas dalam bidang pengujian unit.
Hampir 2015: PHP 5.2 untuk zombie
Sejak 14 Agustus 2014, PHP 5.3 mencapai akhir hayatnya . Sudah pasti mati. PHP 5.4 akan didukung untuk tahun 2015, artinya untuk satu tahun lagi saat ini saya sedang menulis.
Namun, WordPress masih mendukung PHP 5.2, tetapi tidak ada yang harus menulis satu baris kode pun yang mendukung versi itu, terutama jika kode itu OOP.
Ada beberapa alasan berbeda:
- PHP 5.2 sudah lama mati, tidak ada perbaikan keamanan yang dirilis untuk itu, itu berarti tidak aman
- PHP 5.3 menambahkan banyak fitur ke PHP, fungsi anonim , dan ruang nama über alles
- versi PHP yang lebih baru jauh lebih cepat . PHP gratis. Memperbarui itu gratis. Mengapa menggunakan versi yang lebih lambat dan tidak aman jika Anda dapat menggunakan yang lebih cepat, lebih aman secara gratis?
Jika Anda tidak ingin menggunakan kode PHP 5.4+, gunakan setidaknya 5.3+
Contoh
Pada titik ini saatnya untuk meninjau jawaban yang lebih lama berdasarkan apa yang saya katakan sampai di sini.
Setelah kita tidak perlu peduli lagi dengan 5.2, kita bisa dan harus menggunakan ruang nama.
Demi lebih menjelaskan prinsip tanggung jawab tunggal, contoh saya akan menggunakan 3 kelas, yang melakukan sesuatu di frontend, satu di backend dan yang ketiga digunakan dalam kedua kasus.
Kelas admin:
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Kelas frontend:
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Antarmuka alat:
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
Dan kelas Tools, digunakan oleh dua lainnya:
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
Dengan kelas ini, saya bisa instantiate mereka menggunakan kait yang tepat. Sesuatu seperti:
require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';
add_action( 'admin_init', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $admin_stuff; // this is not ideal, reason is explained below
$admin_stuff = new GM\WPSE\Example\AdminStuff( $tools );
} );
add_action( 'template_redirect', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $front_stuff; // this is not ideal, reason is explained below
$front_stuff = new GM\WPSE\Example\FrontStuff( $tools );
} );
Pembalikan Ketergantungan & Injeksi Ketergantungan
Dalam contoh di atas saya menggunakan ruang nama dan fungsi anonim untuk membuat instance kelas yang berbeda pada kait yang berbeda, mempraktikkan apa yang saya katakan di atas.
Perhatikan bagaimana ruang nama memungkinkan untuk membuat kelas bernama tanpa awalan.
Saya menerapkan konsep lain yang secara tidak langsung disebutkan di atas: Injeksi Ketergantungan , itu adalah salah satu metode untuk menerapkan Prinsip Pembalikan Ketergantungan , "D" dalam akronim SOLID.
The Tools
kelas "disuntikkan" di dua kelas lainnya ketika mereka dipakai, sehingga dengan cara ini adalah mungkin untuk memisahkan tanggung jawab.
Selain itu, AdminStuff
dan FrontStuff
kelas menggunakan tipe petunjuk untuk menyatakan mereka membutuhkan kelas yang mengimplementasikan ToolsInterface
.
Dengan cara ini kami atau pengguna yang menggunakan kode kami dapat menggunakan implementasi berbeda dari antarmuka yang sama, membuat kode kami tidak digabungkan ke kelas konkret tetapi ke abstraksi: itulah prinsip Ketergantungan Inversi.
Namun, contoh di atas dapat lebih ditingkatkan. Mari kita lihat caranya.
Pemuat otomatis
Cara yang baik untuk menulis kode OOP yang dapat dibaca lebih baik adalah tidak mencampur definisi tipe (Antarmuka, Kelas) dengan kode lain, dan menempatkan setiap jenis dalam file sendiri.
Aturan ini juga merupakan salah satu standar pengkodean PSR-1 1 .
Namun, untuk melakukannya, sebelum dapat menggunakan suatu kelas, seseorang harus memerlukan file yang berisi itu.
Ini mungkin luar biasa, tetapi PHP menyediakan fungsi utilitas untuk memuat kelas secara otomatis saat diperlukan, menggunakan panggilan balik yang memuat file berdasarkan namanya.
Menggunakan ruang nama menjadi sangat mudah, karena sekarang dimungkinkan untuk mencocokkan struktur folder dengan struktur namespace.
Itu tidak hanya mungkin, tetapi juga standar PSR lain (atau lebih baik 2: PSR-0 sekarang sudah tidak digunakan lagi, dan PSR-4 ).
Dengan mengikuti standar itu, dimungkinkan untuk menggunakan alat yang berbeda yang menangani pengisian otomatis, tanpa harus membuat kode autoloader khusus.
Saya harus mengatakan bahwa standar pengkodean WordPress memiliki aturan berbeda untuk penamaan file.
Jadi ketika menulis kode untuk inti WordPress, pengembang harus mengikuti aturan WP, tetapi ketika menulis kode khusus itu adalah pilihan pengembang, tetapi menggunakan standar PSR lebih mudah menggunakan alat yang sudah ditulis 2 .
Pola Akses Global, Registri, dan Penentu Lokasi Layanan.
Salah satu masalah terbesar ketika membuat instance kelas plugin di WordPress, adalah bagaimana mengaksesnya dari berbagai bagian kode.
WordPress sendiri menggunakan pendekatan global : variabel disimpan dalam lingkup global, membuatnya dapat diakses di mana saja. Setiap pengembang WP mengetik kata global
ribuan kali dalam karier mereka.
Ini juga pendekatan yang saya gunakan untuk contoh di atas, tetapi itu jahat .
Jawaban ini sudah terlalu lama untuk memungkinkan saya untuk menjelaskan lebih lanjut mengapa, tetapi membaca hasil pertama di SERP untuk "variabel global jahat" adalah titik awal yang baik.
Tetapi bagaimana mungkin untuk menghindari variabel global?
Ada beberapa cara berbeda.
Beberapa jawaban lama di sini menggunakan pendekatan contoh statis .
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
Ini mudah dan cukup baik, tetapi memaksa untuk menerapkan pola untuk setiap kelas yang ingin kita akses.
Selain itu, banyak kali pendekatan ini menempatkan pada cara untuk jatuh dalam masalah kelas dewa, karena pengembang membuat diakses kelas utama menggunakan metode ini, dan kemudian menggunakannya untuk mengakses semua kelas lainnya.
Saya sudah menjelaskan betapa buruknya kelas dewa, jadi pendekatan instance statis adalah cara yang baik untuk dilakukan ketika sebuah plugin hanya perlu membuat satu atau dua kelas dapat diakses.
Ini tidak berarti bahwa itu dapat digunakan hanya untuk plugin yang hanya memiliki beberapa kelas, pada kenyataannya, ketika prinsip injeksi dependensi digunakan dengan benar, dimungkinkan untuk membuat aplikasi yang cukup kompleks tanpa perlu membuat secara global dapat diakses dalam jumlah besar benda.
Namun, kadang-kadang plugin perlu membuat beberapa kelas dapat diakses , dan dalam hal ini pendekatan instance statis sangat banyak.
Pendekatan lain yang mungkin adalah dengan menggunakan pola registri .
Ini adalah implementasi yang sangat sederhana:
namespace GM\WPSE\Example;
class Registry {
private $storage = array();
function add( $id, $class ) {
$this->storage[$id] = $class;
}
function get( $id ) {
return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
}
}
Menggunakan kelas ini dimungkinkan untuk menyimpan objek dalam objek registri dengan id, jadi memiliki akses ke registri dimungkinkan untuk memiliki akses ke semua objek. Tentu saja ketika sebuah objek dibuat untuk pertama kali, ia perlu ditambahkan ke dalam registri.
Contoh:
global $registry;
if ( is_null( $registry->get( 'tools' ) ) ) {
$tools = new GM\WPSE\Example\Tools;
$registry->add( 'tools', $tools );
}
if ( is_null( $registry->get( 'front' ) ) ) {
$front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );
$registry->add( 'front', front_stuff );
}
add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );
Contoh di atas menjelaskan bahwa agar berguna, registri harus dapat diakses secara global. Variabel global untuk registri tunggal tidak terlalu buruk, namun untuk puritan non-global dimungkinkan untuk menerapkan pendekatan contoh statis untuk registri, atau mungkin fungsi dengan variabel statis:
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
Pertama kali fungsi dipanggil itu akan instantiate registri, pada panggilan berikutnya hanya akan mengembalikannya.
Metode khusus WordPress lainnya untuk membuat kelas dapat diakses secara global adalah mengembalikan instance objek dari filter. Sesuatu seperti ini:
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
Setelah itu di mana-mana registri diperlukan:
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
Pola lain yang dapat digunakan adalah pola locator layanan . Ini mirip dengan pola registri, tetapi pelacak layanan dilewatkan ke berbagai kelas menggunakan injeksi ketergantungan.
Masalah utama dengan pola ini adalah menyembunyikan dependensi kelas yang membuat kode lebih sulit untuk dipelihara dan dibaca.
Wadah DI
Tidak masalah metode yang digunakan untuk membuat pendaftar registri atau layanan dapat diakses secara global, benda-benda harus disimpan di sana, dan sebelum disimpan, mereka harus dibuat instantiated.
Dalam aplikasi yang kompleks, di mana terdapat cukup banyak kelas dan banyak dari mereka memiliki beberapa dependensi, kelas instantiating membutuhkan banyak kode, sehingga kemungkinan bug meningkat: kode yang tidak ada tidak dapat memiliki bug.
Dalam beberapa tahun terakhir, muncul beberapa perpustakaan PHP yang membantu pengembang PHP untuk dengan mudah membuat instance dan menyimpan instance objek, secara otomatis menyelesaikan dependensi mereka.
Perpustakaan ini dikenal sebagai Kontainer Injeksi Ketergantungan karena mereka mampu membuat instance kelas menyelesaikan dependensi dan juga untuk menyimpan objek dan mengembalikannya saat diperlukan, bertindak serupa dengan objek registri.
Biasanya, ketika menggunakan wadah DI, pengembang harus mengatur dependensi untuk setiap kelas aplikasi, dan kemudian pertama kali kelas diperlukan dalam kode itu instantiated dengan dependensi yang tepat dan instance yang sama dikembalikan lagi dan lagi pada permintaan berikutnya .
Beberapa kontainer DI juga mampu menemukan dependensi secara otomatis tanpa konfigurasi, tetapi menggunakan refleksi PHP .
Beberapa wadah DI yang terkenal adalah:
dan banyak lagi.
Saya ingin menunjukkan bahwa untuk plugin sederhana, yang hanya melibatkan beberapa kelas dan kelas tidak memiliki banyak dependensi, mungkin tidak layak menggunakan wadah DI: metode contoh statis atau registri yang dapat diakses global adalah solusi yang baik, tetapi untuk plugin kompleks manfaat wadah DI menjadi jelas.
Tentu saja, bahkan objek kontainer DI harus dapat diakses untuk digunakan dalam aplikasi dan untuk tujuan itu dimungkinkan untuk menggunakan salah satu metode yang terlihat di atas, variabel global, variabel instance statis, objek kembali melalui filter dan sebagainya.
Komposer
Untuk menggunakan wadah DI sering berarti menggunakan kode pihak ke-3. Saat ini, di PHP, ketika kita perlu menggunakan lib eksternal (jadi tidak hanya wadah DI, tetapi kode apa pun yang bukan bagian dari aplikasi), cukup mengunduhnya dan meletakkannya di folder aplikasi kita tidak dianggap sebagai praktik yang baik. Bahkan jika kita adalah penulis potongan kode lainnya.
Memisahkan kode aplikasi dari dependensi eksternal adalah tanda organisasi yang lebih baik, keandalan yang lebih baik, dan kewarasan kode yang lebih baik.
Komposer , adalah standar de-facto di komunitas PHP untuk mengelola dependensi PHP. Jauh untuk menjadi arus utama di komunitas WP juga, itu adalah alat yang setiap pengembang PHP dan WordPress setidaknya harus tahu, jika tidak digunakan.
Jawaban ini sudah sebesar buku untuk memungkinkan diskusi lebih lanjut, dan juga membahas Komposer di sini mungkin di luar topik, itu hanya disebutkan demi kelengkapan.
Untuk informasi lebih lanjut kunjungi situs Komposer dan itu juga layak memberikan membaca untuk ini Minisite dikuratori oleh @Rarst .
1 PSR adalah aturan standar PHP yang dirilis oleh PHP Framework Interop Group
2 Komposer (pustaka yang akan disebutkan dalam jawaban ini) antara lain juga berisi utilitas autoloader.