Özgün soru sorulduktan tam olarak 2 yıl sonra buraya gelindiğinde, belirtmek istediğim birkaç şey var . (Benden bir çok şeyi işaret etmemi istemeyin ).
Uygun kanca
Bir eklenti sınıfını başlatmak için uygun kanca kullanılmalıdır. Bunun için genel bir kural yoktur, çünkü sınıfın ne yaptığına bağlıdır.
Çok erken bir kancanın kullanılması "plugins_loaded"
çoğu zaman anlamsızdır, çünkü böyle bir kanca admin, frontend ve AJAX'ın talepleri için ateşlenir, ancak çoğu zaman, daha sonraki bir kanca çok daha iyidir, çünkü sadece gerektiğinde eklenti sınıflarını başlatmayı sağlar.
Örneğin, şablonlar için bir şeyler yapan bir sınıftan başlatılabilir "template_redirect"
.
Genel olarak konuşursak, bir sınıfın "wp_loaded"
ateşlenmeden önce başlatılması gerektiğine çok nadir rastlanır .
Tanrı Sınıfı Yok
Eski cevaplarda örnek olarak kullanılan tüm sınıfların çoğu, "Prefix_Example_Plugin"
ya da "My_Plugin"
... adında bir sınıf kullanır . Bu, muhtemelen eklenti için bir ana sınıf olduğunu gösterir .
Peki, bir eklenti tek bir sınıf tarafından yapılmadıkça (bu durumda eklenti adından sonra isimlendirmek kesinlikle mantıklı), bütün eklentiyi yöneten bir sınıf oluşturmak için (örneğin, bir eklentinin ihtiyaç duyduğu tüm kancaları eklemek veya diğer tüm eklenti sınıflarını başlatmak) ) bir tanrı nesnesine örnek olarak kötü bir uygulama olarak kabul edilebilir .
Nesneye yönelik programlama kodunda , "S" nin "Tek sorumluluk ilkesi" anlamına geldiği yerde, SOLID olma eğiliminde olmalıdır .
Her sınıfın tek bir şey yapması gerektiği anlamına gelir. WordPress eklenti geliştirmesinde, geliştiricilerin bir ana eklenti sınıfını başlatmak için tek bir kanca kullanmaktan kaçınmaları gerektiği , ancak sınıf sorumluluğuna göre farklı sınıfları başlatmak için farklı kancalar kullanılması gerektiği anlamına gelir.
Yapıcıdaki kancalardan kaçının
Bu argüman burada diğer cevaplarda da açıklanmıştır, ancak bu kavramı vurgulamak ve bu diğer cevabı , ünite testi amacıyla oldukça geniş bir şekilde açıklandığı yere bağlamak istiyorum .
Neredeyse 2015: PHP 5.2 zombiler içindir
14 Ağustos 2014'ten beri PHP 5.3 kullanım ömrü sona erdi . Kesinlikle öldü. PHP 5.4 tüm 2015 için desteklenecek, yazdığım şu anda bir yıl daha demek oluyor.
Ancak, WordPress hala PHP 5.2'yi desteklemektedir, ancak hiç kimse, özellikle de OOP ise, o sürümü destekleyen tek bir kod satırı yazmamalıdır.
Farklı sebepler var:
- PHP 5.2 uzun zaman önce ölmüş, bunun için hiçbir güvenlik düzeltmesi yapılmamıştır, bu güvenli olmadığı anlamına gelir.
- PHP 5.3, PHP'ye birçok özellik ekledi, adsız işlevler ve ad alanları über alles
- PHP'nin yeni sürümleri çok daha hızlı . PHP ücretsizdir. Güncellemek ücretsizdir. Neden daha hızlı, daha güvenli bir sürümünü ücretsiz olarak kullanabiliyorsanız, neden daha yavaş ve güvensiz bir sürüm kullanıyorsunuz?
PHP 5.4+ kodunu kullanmak istemiyorsanız, en az 5.3+ kullanın
Örnek
Bu noktada, şu ana kadar söylediklerime dayanarak eski cevapları gözden geçirme zamanı.
Artık 5.2 ile ilgilenmek zorunda değilsek, isim alanları kullanabiliriz ve kullanmalıyız.
Daha iyi açıklamak tek bir sorumluluk ilkesinin aşkına, örnek 3 sınıfları, does birini kullanacak şey önyüzü üzerinde, arka uçta bir tane ve her iki durumda da kullanılan üçüncü.
Yönetici sınıfı:
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Ön sınıf:
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Araçlar arayüzü:
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
Ve diğer ikisi tarafından kullanılan bir Araçlar sınıfı:
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
Bu dersleri alarak uygun kancalar kullanarak onları başlatabilirim. Gibi bir şey:
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 );
} );
Bağımlılık İnversiyonu ve Bağımlılık Enjeksiyonu
Yukarıdaki örnekte, yukarıda belirtilenleri uygulayarak farklı kancalarda farklı sınıfları başlatmak için ad alanları ve adsız işlevler kullandım.
Ad alanlarının herhangi bir önek olmadan adlandırılmış sınıflar oluşturmasına nasıl izin verdiğine dikkat edin.
Yukarıda dolaylı olarak belirtilen başka bir konsept uyguladım: Bağımlılık Enjeksiyonu , Bağımlılık İnversiyon Prensibi olan “D” olan SOLID kısaltmasında uygulamak için bir yöntem .
Tools
Onlar örneklenen zaman sınıf bu yolla o sorumluluğu ayırmak mümkündür, böylece diğer iki sınıfta "enjekte" dır.
Ek olarak AdminStuff
ve FrontStuff
sınıflar , uygulayan bir sınıfa ihtiyaç duyduklarını bildirmek için tür ipucunu kullanır ToolsInterface
.
Bu şekilde kodumuzu kullanan kendimiz veya kullanıcılarımız aynı arabirimin farklı uygulamalarını kullanabilir, kodumuzu somut bir sınıfa değil bir soyutlamaya bağlarlar: tam olarak Bağımlılık İnversiyon Prensibi'nin konusu budur.
Bununla birlikte, yukarıdaki örnek daha da iyileştirilebilir. Bakalım nasıl.
Otomatik Yükleyici
Daha iyi okunabilir cepten kod yazmak için iyi bir yol değildir karıştırmak diğer koduyla türleri (Arayüzler, Sınıflar) tanımını ve kendi dosyasındaki her tür koymak.
Bu kural aynı zamanda PSR-1 kodlama standartlarından biridir 1 .
Bununla birlikte, bunu yaparken, bir sınıfı kullanmadan önce onu içeren dosyayı zorunlu kılmak gerekir.
Bu çok zor olabilir, ancak PHP , gerektiğinde sınıfı otomatik olarak yüklemek için kendi adına göre bir dosyayı yükleyen bir geri çağırma kullanarak yardımcı işlevler sağlar .
Ad alanlarını kullanmak çok kolaydır, çünkü şimdi klasör yapısını ad alanı yapısıyla eşleştirmek mümkündür.
Bu sadece mümkün değil, aynı zamanda başka bir PSR standardıdır (veya daha iyi 2: PSR-0 şimdi kullanımdan kaldırılmış ve PSR-4 ).
Bu standartlara uyarak, özel bir otomatik yükleyiciyi kodlamak zorunda kalmadan, otomatik yükü işleyen farklı araçlardan yararlanmak mümkündür.
WordPress kodlama standartlarının dosyaları adlandırmak için farklı kuralları olduğunu söylemeliyim .
Yani WordPress çekirdek için kod yazarken, geliştiriciler WP kurallarına uymak zorunda, ama özel kod yazarken bir geliştirici bir seçimdir, ama PSR standart kullanılarak zaten yazılı araçları kullanmak daha kolaydır 2 .
Global Erişim, Kayıt ve Servis Belirleme Kalıpları.
WordPress'te eklenti sınıflarını başlatırken en büyük sorunlardan biri, kodun çeşitli bölümlerinden bunlara nasıl erişileceğidir.
WordPress'in kendisi global yaklaşımı kullanır : değişkenler global kapsamda saklanır ve bu da her yerde erişilebilir olmasını sağlar. Her WP geliştiricisi sözcüğü global
kariyerine binlerce kez yazar.
Bu aynı zamanda yukarıdaki örnek için kullandığım yaklaşım, ama kötülük .
Bu cevap zaten nedenini daha fazla açıklamama izin vermek için çok uzun, ancak SERP'teki "küresel değişkenler kötülük" için ilk sonuçları okumak iyi bir başlangıç noktası.
Ancak küresel değişkenlerden kaçınmak nasıl mümkün olabilir?
Farklı yollar var.
Buradaki eski cevapların bazıları statik örnek yaklaşımını kullanır .
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
Kolay ve oldukça iyi, ancak erişmek istediğimiz her sınıf için modeli uygulamaya zorluyor.
Üstelik, çoğu zaman bu yaklaşım tanrı sınıfı meselesine girmenin yolunu ortaya koyar, çünkü geliştiriciler bu yöntemi kullanarak bir ana sınıfa erişilebilir hale getirir ve ardından diğer tüm derslere erişmek için onu kullanır.
Bir tanrı sınıfının ne kadar kötü olduğunu zaten açıkladım, bu yüzden statik örnek yaklaşımı bir eklentinin yalnızca bir veya iki sınıfı erişilebilir hale getirmesi gerektiğinde gitmek için iyi bir yoldur.
Bu, sadece birkaç sınıfa sahip eklentiler için kullanılabileceği anlamına gelmez, aslında, bağımlılık enjeksiyon prensibi düzgün kullanıldığında, küresel olarak erişilebilir bir sayıya ihtiyaç duymadan, oldukça karmaşık uygulamalar oluşturmak mümkündür. Nesnelerin
Bununla birlikte, bazen eklentilerin bazı sınıfları erişilebilir kılmaları gerekir ve bu durumda statik örnek yaklaşımı ezicidir.
Bir başka olası yaklaşım kayıt düzenini kullanmaktır .
Bu onun çok basit bir uygulamasıdır:
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;
}
}
Bu sınıfı kullanarak, nesneleri bir kayıt defteri nesnesinde bir kimliğiyle depolamak mümkündür, böylece bir kayıt defterine erişebilmek, tüm nesnelere erişebilmek mümkündür. Elbette bir nesne ilk defa yaratıldığında, kayıt defterine eklenmesi gerekir.
Örnek:
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' ) );
Yukarıdaki örnek, kullanışlı olmanın kayıt defterinin küresel olarak erişilebilir olması gerektiğini açıkça ortaya koymaktadır. Tek kayıt defteri için genel bir değişken çok kötü değildir , ancak genel olmayan saflaştırıcılar için bir kayıt defteri için statik örnek yaklaşımını veya belki de statik değişkenli bir işlevi uygulamak mümkündür:
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
Fonksiyon ilk çağrıldığında kayıt defterini başlatır, daha sonraki çağrılar için onu döndürür.
Bir sınıfı genel olarak erişilebilir kılmak için başka bir WordPress'e özgü yöntem, bir nesne örneğini bir filtreden döndürmektir. Bunun gibi bir şey:
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
Bundan sonra her yerde kayıt defteri gereklidir:
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
Kullanılabilecek bir başka model, servis bulma modelidir . Kayıt düzenine benzer, ancak hizmet konumlayıcılar bağımlılık enjeksiyonunu kullanarak çeşitli sınıflara geçirilir.
Bu kalıpla ilgili ana sorun, kodun korunmasını ve okunmasını zorlaştıran sınıf bağımlılıklarının gizlenmesidir.
DI Konteynerler
Kayıt veya servis bulucuyu global olarak erişilebilir kılmak için kullanılan yöntem ne olursa olsun, nesnelerin orada depolanması gerekir ve saklanmadan önce bunların başlatılması gerekir.
Oldukça fazla sınıfın olduğu ve çoğunun birkaç bağımlılığı olduğu karmaşık uygulamalarda, sınıflandırma yapmak için çok fazla kod gerekir, bu nedenle hata olasılığı artar: Var olmayan kodda hata olmaz.
Son yıllarda, PHP geliştiricilerinin nesnelerin örneklerini kolayca başlatmasına ve saklamasına , bağımlılıklarını otomatik olarak çözmesine yardımcı olan bazı PHP kütüphaneleri ortaya çıktı .
Bu kütüphaneler Bağımlılık Enjeksiyon Konteynerleri olarak bilinir, çünkü bağımlılıkları çözen sınıfları somutlaştırabilirler ve ayrıca bir kayıt defteri nesnesine benzer şekilde hareket ederek nesneleri depolayabilir ve gerektiğinde geri getirebilirler.
Genellikle, DI kapları kullanırken, geliştiriciler uygulamanın her sınıfı için bağımlılıkları ayarlamak zorunda kalırlar ve daha sonra kodda bir sınıfa ilk defa ihtiyaç duyulduklarında uygun bağımlılıklar ile başlatılırlar ve aynı örnek sonraki taleplerde tekrar tekrar geri gönderilir. .
Bazı DI kapları ayrıca bağımlılıkları konfigürasyon olmadan otomatik olarak keşfedebilirler ancak PHP yansıması kullanırlar .
Bazı iyi bilinen DI Konteynerleri:
Ve bircok digerleri.
Sadece birkaç sınıf ve sınıf içeren basit eklentiler için pek fazla bağımlılığın bulunmadığını, DI kapsayıcılarını kullanmaya değmeyeceğini belirtmek istiyorum: statik örnek yöntemi veya genel erişilebilir bir kayıt defteri iyi çözümlerdir, ancak karmaşık eklentiler için bir DI konteynerinin yararı belirginleşir.
Tabii ki, DI konteyner nesnelerinin bile uygulamada kullanılmak için erişilebilir olması gerekir ve bu amaçla yukarıda görülen yöntemlerden birini, genel değişken, statik örnek değişkeni, nesneyi filtre yoluyla geri döndürmek vb. Kullanmak mümkündür.
Besteci
DI konteynerini kullanmak genellikle üçüncü taraf kodunu kullanmak anlamına gelir. Günümüzde PHP'de harici bir lib kullanmamız gerektiğinde (sadece DI konteynerlerini değil, uygulamanın parçası olmayan herhangi bir kodu), basitçe indirerek ve uygulama klasörümüze koymak iyi bir uygulama olarak kabul edilmez. Başka bir kod parçasının yazarı olsak bile.
Bir uygulama kodunun dış bağımlılıklardan ayrılması, daha iyi bir organizasyonun, daha iyi güvenilirliğin ve kodun daha iyi akıl sağlığının işaretidir .
Besteci , PHP bağımlılığını yönetmek için PHP topluluğunda fiili bir standarttır. WP topluluğunda da ana akım olmaktan çok uzak , her PHP ve WordPress geliştiricisinin kullanmıyorsa en azından bilmesi gereken bir araçtır.
Bu cevap daha fazla tartışmaya izin vermek için zaten kitap boyutundadır ve ayrıca Composer'ı burada tartışmak muhtemelen konu dışıdır, sadece tamamlanma adına söz edilmiştir.
Daha fazla bilgi için Besteci sitesini ziyaret edin ve ayrıca @Rarst tarafından önerilen bu minisite bir okumaya değer .
1 PSR, PHP Framework Birlikte Çalışma Grubu tarafından yayımlanan PHP standart kurallarıdır.
2 Besteci (bu cevapta bahsedilecek bir kütüphane) diğer şeylerin yanı sıra bir otomatik yükleyici yardımcı programı da içerir.