Autoloading e Namespace nei Plugin e Temi WordPress: È Possibile?

30 ago 2012, 23:00:45
Visualizzazioni: 29.2K
Voti: 83

Qualcuno ha mai utilizzato l'autoloading e/o i namespace PHP all'interno di un plugin o tema?

Quali sono le vostre opinioni sull'utilizzo? Ci sono rischi? Problematiche?

Nota: i namespace sono disponibili solo da PHP 5.3 in poi. Assumiamo, per questa domanda, che si stia lavorando con server che si sa avere PHP 5.3 o versioni superiori.

0
Tutte le risposte alla domanda 3
0
100

Ok, ho lavorato a due grandi progetti in cui avevo il controllo sufficiente sul server per utilizzare i namespace e fare affidamento sull'autoloading.

Prima cosa. L'autoloading è fantastico. Non preoccuparsi dei require è una cosa relativamente positiva.

Ecco un loader che ho utilizzato in alcuni progetti. Prima verifica che la classe sia nel namespace corrente, poi esce se non lo è. Da lì in poi è solo questione di manipolazione delle stringhe per trovare la classe.

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

Si potrebbe facilmente adattare questo codice per un uso senza namespace. Supponendo che si prefissino in modo uniforme le classi del plugin/tema, si potrebbe semplicemente testare quel prefisso. Poi usare gli underscore nel nome della classe come segnaposto per i separatori di directory. Se si utilizzano molte classi, probabilmente si vorrà usare un qualche tipo di autoloader con classmap.

Namespace e Hook

Il sistema di hook di WordPress funziona utilizzando call_user_func (e call_user_func_array), che prende i nomi delle funzioni come stringhe e le chiama quando viene effettuata la chiamata alla funzione do_action (e, successivamente, call_user_func).

Con i namespace, ciò significa che sarà necessario passare nomi di funzioni completamente qualificati che includano il namespace negli hook.

<?php
namespace WPSE\SomeNameSpace;

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

Probabilmente sarebbe meglio fare un uso generoso della costante magica __NAMESPACE__ se si vuole fare così.

<?php
namespace WPSE\SomeNameSpace;

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

Se si inseriscono sempre gli hook nelle classi, è più facile. La creazione standard di un'istanza di una classe e tutti gli hook nel costruttore con $this funziona bene.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

    function loaded()
    {
        // questo funziona!
    }
}

Se si utilizzano metodi statici come vorrei fare io, sarà necessario passare il nome della classe completamente qualificato come primo argomento dell'array. È un sacco di lavoro, quindi si può semplicemente usare la costante magica __CLASS__ o get_class.

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

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

    public static function loaded()
    {
        // questo funziona!
    }
}

Utilizzo delle Classi Core

La risoluzione dei nomi di classe in PHP è un po' complicata. Se si intendono usare classi core di WP (WP_Widget nell'esempio sotto) bisogna fornire le dichiarazioni use.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Oppure si può usare il nome della classe completamente qualificato - sostanzialmente prefissandolo con un backslash.

<?php
namespace WPSE\SomeNameSpace;

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

Define

Questo è più un discorso generale di PHP, ma mi ha colpito, quindi eccolo qui.

Potresti voler definire cose che utilizzerai spesso, come il percorso del tuo plugin. Usare l'istruzione define posiziona le cose nel namespace root a meno che non si passi esplicitamente il namespace nel primo argomento di define.

<?php
namespace WPSE\SomeNameSpace;

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

// nel namespace corrente
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

Con PHP 5.3+ si può anche usare la parola chiave const al livello root di un file. Le const sono sempre nel namespace corrente, ma sono meno flessibili di una chiamata define.

<?php
namespace WPSE\SomeNameSpace;

// nel namespace corrente
const MY_CONST = 1;

// questo non funzionerà!
const MY_PATH = plugin_dir_path(__FILE__);

Sentiti libero di aggiungere qualsiasi altro suggerimento che potresti avere!

27 set 2012 23:08:44
4
20

Ecco una risposta del 2017.

L'autoloading è fantastico. I namespace sono fantastici.

Anche se puoi implementarlo da solo, nel 2017 ha più senso usare il magnifico e onnipresente Composer per gestire i requisiti PHP. Composer supporta sia l'autoloading PSR-0 che PSR-4, ma il primo è deprecato dal 2014, quindi usa PSR-4. Riduce la complessità delle tue directory.

Noi manteniamo ogni nostro plugin/tema nel proprio repository Github, ognuno con il proprio file composer.json e file composer.lock.

Ecco la struttura delle directory che usiamo per i nostri plugin. (Non abbiamo davvero un plugin chiamato awesome-plugin, ma dovremmo.)

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/*

Se fornisci un file composer.json appropriato, Composer gestisce qui i namespace e l'autoloading.

{
    "name": "awesome-company/awesome-plugin",
    "description": "Plugin WordPress per il sito AwesomeCompany, che fornisce funzionalità fantastiche.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

Quando esegui composer install, crea la directory vendor e il file vendor/autoload.php, che caricherà automaticamente tutti i file con namespace in src/ e qualsiasi altra libreria di cui potresti aver bisogno.

Poi all'inizio del tuo file principale del plugin (che per noi è awesome-plugin.php), dopo i metadati del plugin, devi semplicemente inserire:

// Autoloading di Composer.
require_once __DIR__ . '/vendor/autoload.php';

...

Funzionalità Bonus

Non è una necessità, ma usiamo il boilerplate WordPress Bedrock per usare Composer fin dall'inizio. Poi possiamo usare Composer per assemblare i plugin di cui abbiamo bisogno tramite Composer, incluso il tuo plugin che hai scritto sopra. Inoltre, grazie a WPackagist, puoi richiedere qualsiasi altro plugin da Wordpress.org (vedi esempio di cool-theme e cool-plugin sotto).

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "Boilerplate WordPress con strumenti di sviluppo moderni, configurazione più semplice e una struttura di cartelle migliorata",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Dice a Composer di cercare il nostro Awesome Plugin proprietario qui.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // Il nostro plugin!
    "wpackagist-plugin/cool-plugin": "dev-trunk",       // Plugin di qualcun altro
    "wpackagist-theme/cool-theme": "dev-trunk",         // Tema di qualcun altro
    "composer/installers": "~1.2.0",     // Default di Bedrock
    "vlucas/phpdotenv": "^2.0.1",        // Default di Bedrock
    "johnpbloch/wordpress": "4.7.5",     // Default di Bedrock
    "oscarotero/env": "^1.0",            // Default di Bedrock
    "roots/wp-password-bcrypt": "1.0.0"  // Default di Bedrock
  },
  "extra": {
    // Questa è la magia che posiziona i pacchetti con il TYPE corretto nel percorso corretto. 
    "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"
    ]
  }
}

Nota 1: I commenti non sono legali in JSON, ma ho annotato il file sopra per maggiore chiarezza.

Nota 2: Ho tagliato alcune parti del file boilerplate di Bedrock per brevità.

Nota 3: Questo è il motivo per cui il campo type nel primo file composer.json è significativo. Composer lo posiziona automaticamente nella directory web/app/plugins.

7 giu 2017 03:34:02
Commenti

Apprezzo la tua risposta, molto utile! Ma sono curioso riguardo al "bootstrap.php" a cui ti riferisci. Cosa contiene? :)

INT INT
22 set 2018 23:15:53

Avere un file bootstrap.php è una scelta stilistica che faccio nella maggior parte dei miei progetti, dentro o fuori WP. Il mio bootstrapper normalmente controlla solo impostazioni e variabili d'ambiente; il suo scopo principale è assicurarsi che il mio plugin abbia sempre tutto ciò di cui ha bisogno per funzionare, indipendentemente dal fatto che venga eseguito all'interno di WP o come applicazione PHP standalone.

haz haz
24 set 2018 01:43:25

Ciao, dove devo eseguire ´composer install´?

LΞИIИ LΞИIИ
27 apr 2020 08:13:09

@LeninZapata Dovresti eseguire composer install nella directory contenente il file composer.json. Ad esempio, se cloni Bedrock (https://github.com/roots/bedrock), poi entra nella tua directory bedrock ed eseguilo lì.

haz haz
6 mag 2020 09:43:54
0

Utilizzo l'autoloading (poiché il mio plugin ha molte classi - in parte perché include Twig) e non ho mai avuto problemi segnalati (il plugin è installato > 20.000 volte).

Se sei sicuro che non avrai mai bisogno di utilizzare un'installazione PHP che non supporta i namespace, allora non ci sono problemi (~70% degli attuali blog WordPress non supporta i namespace). Alcune cose da tenere a mente:

Mi sembra di ricordare che i namespace non siano case sensitive in PHP normale, ma lo siano quando si utilizza FastCGI PHP su IIS - questo può causare mal di testa se testi su Linux e non noti una lettera minuscola fuori posto.

Inoltre, anche se sei sicuro che il codice che stai attualmente sviluppando verrà utilizzato solo su PHP > 5.3.0, non potrai riutilizzare alcun codice con progetti che non hanno questo privilegio - questo è il motivo principale per cui non ho utilizzato i namespace in progetti interni. Ho scoperto che i namespace non aggiungono così tanto rispetto al possibile mal di testa di dover rimuovere la dipendenza da essi.

3 set 2012 17:07:31