Autoloading & Namespaces di WordPress Plugin & Tema: Bisakah Bekerja?

70

Adakah yang menggunakan autoloading dan / atau ruang nama PHP di dalam plugin atau tema?

Pikiran untuk menggunakannya? Ada salahnya? Perangkap?

Catatan: ruang nama hanya PHP 5.3+. Asumsikan, untuk pertanyaan ini, Anda tahu Anda akan berurusan dengan server yang Anda tahu memiliki PHP 5.3 atau lebih tinggi.

chrisguitarguy
sumber

Jawaban:

88

Oke, saya punya dua proyek besar di mana saya sudah mengendalikan server cukup untuk namespace dan mengandalkan autoloading.

Pertama. Autoloading luar biasa. Tidak perlu khawatir tentang kebutuhan adalah hal yang relatif baik.

Inilah loader yang telah saya gunakan pada beberapa proyek. Cek untuk memastikan kelas berada di namespace saat ini terlebih dahulu, kemudian menebus jika tidak. Dari sana hanya beberapa manipulasi string untuk menemukan kelas.

<?php
spl_autoload_register(__NAMESPACE__ . '\\autoload');
function autoload($cls)
{
    $cls = ltrim($cls, '\\');
    if(strpos($cls, __NAMESPACE__) !== 0)
        return;

    $cls = str_replace(__NAMESPACE__, '', $cls);

    $path = PLUGIN_PATH_PATH . 'inc' . 
        str_replace('\\', DIRECTORY_SEPARATOR, $cls) . '.php';

    require_once($path);
}

Orang dapat dengan mudah menyesuaikan ini untuk digunakan tanpa ruang nama. Anggap Anda awalan kelas plugin / tema Anda secara seragam, Anda bisa menguji awalan itu. Kemudian gunakan garis bawah pada nama kelas untuk sebagai placeholder untuk pemisah direktori. Jika Anda menggunakan banyak kelas, Anda mungkin ingin menggunakan semacam autoloader classmap.

Ruang nama dan Kait

Sistem kait WordPress bekerja dengan menggunakan call_user_func(dan call_user_func_array), yang menggunakan nama fungsi sebagai string dan memanggil mereka saat panggilan fungsi do_action(dan selanjutnya call_user_func) dibuat.

Dengan ruang nama, itu berarti Anda harus memberikan nama fungsi yang sepenuhnya memenuhi syarat yang menyertakan namespace ke dalam kait.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', 'WPSE\\SomeNameSpace\\the_function');
function the_function()
{
   return 'did stuff';
}

Mungkin akan lebih baik untuk menggunakan __NAMESPACE__sihir konstan secara liberal jika Anda ingin melakukan ini.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', __NAMESPACE__ . '\\the_function');
function the_function()
{
   return 'did stuff';
}

Jika Anda selalu memasukkan kait ke dalam kelas, itu lebih mudah. Standar membuat instance kelas dan semua kait di konstruktor dengan $thisberfungsi dengan baik.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

class Plugin
{
    function __construct()
    {
        add_action('plugins_loaded', array($this, 'loaded'));
    }

    function loaded()
    {
        // this works!
    }
}

Jika Anda menggunakan metode statis seperti yang ingin saya lakukan, Anda harus memberikan nama kelas yang sepenuhnya memenuhi syarat sebagai argumen pertama array. Itu banyak pekerjaan, jadi Anda bisa menggunakan __CLASS__konstanta sihir atau get_class.

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

class Plugin
{
    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'loaded'));
        // OR: add_action('plugins_loaded', array(get_class(), 'loaded'));
    }

    public static function loaded()
    {
        // this works!
    }
}

Menggunakan Kelas Inti

Resolusi classname PHP agak miring. Jika Anda akan menggunakan kelas inti WP ( WP_Widgetdalam contoh di bawah), Anda harus memberikan usepernyataan.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Atau Anda dapat menggunakan nama kelas yang sepenuhnya memenuhi syarat - pada dasarnya hanya awalan dengan backslash.

<?php
namespace WPSE\SomeNameSpace;

class MyWidget extends \WP_Widget
{
   // ...
}

Mendefinisikan

Ini adalah PHP yang lebih umum, tetapi ini menggigit saya, jadi ini dia.

Anda mungkin ingin mendefinisikan hal-hal yang akan sering Anda gunakan, seperti jalur ke plugin Anda. Menggunakan pernyataan define menempatkan sesuatu di root namespace kecuali Anda secara eksplisit melewatkan namespace ke argumen pertama define.

<?php
namespace WPSE\SomeNameSpace;

// root namespace
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// in the current namespace
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

Anda juga dapat menggunakan constkata kunci pada level root file dengan PHP 5.3 plus. constss selalu ada dalam namespace saat ini, tetapi kurang fleksibel dibandingkan definepanggilan.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

// this won't work!
const MY_PATH = plugin_dir_path(__FILE__);

Silahkan menambahkan tips lain yang mungkin Anda miliki!

chrisguitarguy
sumber
16

Inilah jawaban 2017.

Autoloading luar biasa. Namespacing luar biasa.

Meskipun Anda dapat membuatnya sendiri, pada tahun 2017 masuk akal untuk menggunakan Komposer yang luar biasa dan ada di mana-mana untuk menangani persyaratan PHP Anda. Komposer mendukung autoloading PSR-0 dan PSR-4 , tetapi yang lama sudah tidak digunakan lagi sejak 2014, jadi gunakan PSR-4. Ini mengurangi kompleksitas direktori Anda.

Kami menyimpan setiap plugin / tema kami di repositori Github masing-masing, masing-masing dengan composer.jsonfile dan composer.lockfile mereka sendiri .

Berikut struktur direktori yang kami gunakan untuk plugin kami. (Kami tidak benar-benar memiliki plugin yang disebut awesome-plugin, tetapi kami harus.)

plugins/awesome-plugin/bootstrap.php
plugins/awesome-plugin/composer.json
plugins/awesome-plugin/composer.lock
plugins/awesome-plugin/awesome-plugin.php
plugins/awesome-plugin/src/*

plugins/awesome-plugin/vendor/autoload.php
plugins/awesome-plugin/vendor/*

Jika Anda memberikan composer.jsonfile yang sesuai , Komposer menangani penamaan-nama dan autoloading di sini.

{
    "name": "awesome-company/awesome-plugin",
    "description": "Wordpress plugin for AwesomeCompany website, providing awesome functionality.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

Ketika Anda menjalankan composer install, itu menciptakan vendordirektori, dan vendor/autoload.phpfile, yang akan memuat secara otomatis semua file spasi nama Anda src/, dan perpustakaan lain yang mungkin Anda perlukan.

Kemudian di bagian atas file plugin utama Anda (yang bagi kami adalah awesome-plugin.php), setelah metadata plugin Anda, Anda hanya perlu:

// Composer autoloading.
require_once __DIR__ . '/vendor/autoload.php';

...

Fitur Bonus

Bukan keharusan, tapi kami menggunakan pelat batu Wordpress Bedrock untuk menggunakan Komposer sejak awal. Kemudian kita dapat menggunakan Komposer untuk merakit plugin yang kita butuhkan melalui Komposer, termasuk plugin Anda sendiri yang Anda tulis di atas. Selain itu, berkat WPackagist , Anda dapat meminta plugin lainnya dari Wordpress.org (lihat contoh cool-themedan cool-pluginbawah).

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Tells Composer to look for our proprietary Awesome Plugin here.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // Our plugin!
    "wpackagist-plugin/cool-plugin": "dev-trunk",       // Someone else' plugin
    "wpackagist-theme/cool-theme": "dev-trunk",         // Someone else' theme
    "composer/installers": "~1.2.0",     // Bedrock default
    "vlucas/phpdotenv": "^2.0.1",        // Bedrock default
    "johnpbloch/wordpress": "4.7.5",     // Bedrock default
    "oscarotero/env": "^1.0",            // Bedrock default
    "roots/wp-password-bcrypt": "1.0.0"  // Bedrock default
  },
  "extra": {
    // This is the magic that drops packages with the correct TYPE in the correct location. 
    "installer-paths": {
      "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"],
      "web/app/plugins/{$name}/": ["type:wordpress-plugin"],
      "web/app/themes/{$name}/": ["type:wordpress-theme"]
    },
    "wordpress-install-dir": "web/wp"
  },
  "scripts": {
    "test": [
      "vendor/bin/phpcs"
    ]
  }
}

Catatan 1: Komentar tidak sah di JSON, tetapi saya telah memberi anotasi pada file di atas untuk kejelasan lebih lanjut.

Catatan 2: Saya telah memotong beberapa bit file bedrock boilerplate untuk singkatnya.

Catatan 3: Inilah sebabnya mengapa typebidang dalam composer.jsonfile pertama adalah signifikan. Komposer secara otomatis memasukkannya ke web/app/pluginsdirektori.

haz
sumber
Hargai jawaban Anda, sangat membantu! Tapi saya ingin tahu tentang "bootstrap.php" yang Anda maksud. Apa isinya? :)
INT
1
Memiliki file bootstrap.php adalah gaya yang saya lakukan di sebagian besar proyek saya, di dalam atau di luar WP. Bootstrapper saya biasanya hanya memeriksa variabel pengaturan dan lingkungan; tujuan utamanya adalah untuk memastikan plugin saya selalu memiliki apa yang perlu dijalankan, terlepas dari apakah itu dijalankan dari dalam WP atau sebagai aplikasi PHP mandiri.
haz
4

Saya menggunakan autoloading (karena plugin saya memiliki banyak kelas - sebagian karena termasuk Twig), tidak pernah ada masalah yang menarik perhatian saya (plugin terpasang> 20.000 kali).

Jika Anda yakin bahwa Anda tidak akan pernah perlu menggunakan instalasi php yang tidak mendukung ruang nama maka sekali lagi Anda baik-baik saja (~ 70% dari blog wordpress saat ini tidak mendukung ruang nama). Beberapa hal yang perlu diperhatikan:

Saya sepertinya ingat bahwa ruang nama tidak peka huruf besar-kecil di php biasa tetapi ketika menggunakan fastcgi php di iis - ini menyebabkan beberapa sakit kepala jika Anda menguji di linux dan tidak melihat huruf kecil yang nakal.

Bahkan jika Anda yakin kode yang sedang Anda kembangkan hanya akan digunakan pada> 5.3.0 Anda tidak akan dapat menggunakan kembali kode apa pun dengan proyek yang tidak memiliki kemewahan itu - itulah alasan utama mengapa saya belum ruang nama yang digunakan pada proyek internal. Saya telah menemukan bahwa ruang nama benar-benar tidak menambahkan bahwa banyak bila dibandingkan dengan sakit kepala mungkin harus menghapus ketergantungan pada mereka.

Daniel Chatfield
sumber