Come affrontare lo sviluppo moderno di WordPress (parte 2)

Pubblicato: 2022-03-11

WordPress è la tecnologia del sito più utilizzata al mondo e con buone ragioni. Eppure il codice legacy nel suo nucleo è un pasticcio e questo problema si riversa sugli sviluppatori di terze parti. Alcuni sviluppatori prendono questo come una scusa per tagliare gli angoli nel proprio codice PHP di WordPress, ma questo approccio è più costoso a lungo termine per tutte le modifiche tranne le più banali.

Nella parte 1 della nostra serie in due parti, ci siamo concentrati sul progetto generale e sugli strumenti del flusso di lavoro, seguiti dallo sviluppo front-end. Ora è il momento di prendere il toro per le corna e lottare con PHP: in particolare, come seguire le migliori pratiche quando si lavora con il codice WordPress back-end. Potresti pensare a questo come a un tutorial PHP/WordPress, ma un po' più avanzato, partendo dal presupposto che tu abbia già eseguito alcune personalizzazioni del back-end di WordPress.

Quindi, quali moderni principi di progettazione del software e funzionalità PHP ti daranno il miglior valore per il tuo tempo? Di seguito sono riportate 10 pratiche di sviluppo WordPress e PHP che consiglio vivamente.

Best practice per lo sviluppo di WordPress moderno n. 1: segui "Separazione delle preoccupazioni"

La separazione delle preoccupazioni significa che parti del codice PHP di WordPress con funzionalità o scopi diversi non dovrebbero essere mescolate insieme. Invece, dovrebbero essere organizzati in sezioni o moduli distinti, che si scambiano i dati attraverso un'interfaccia definita. ( Un'interfaccia è un insieme di parametri definiti che un modulo prende come input e ciò che restituisce in output.) Un termine strettamente correlato è il principio di responsabilità singola : ogni modulo di codice (o funzione) dovrebbe essere responsabile di una sola cosa.

L'obiettivo finale di seguire questi principi è produrre codice che sia modulare e quindi manutenibile, estensibile e riutilizzabile.

È stato un bel boccone, quindi diamo un'occhiata a un esempio di alcuni WordPress PHP (dal core di WordPress) che mette tutto ingarbugliato insieme. Questo stile di codifica è spesso chiamato "codice spaghetti" perché comprenderne il funzionamento interno è quasi impossibile. Il brano seguente è stato redatto per brevità; tuttavia, lo stile e la formattazione originali sono stati preservati.

 $id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>

Prima di tutto, è del tutto incomprensibile. E mi piace che l'unico commento sia End foreach , che è completamente ridondante. Abbiamo le query del database, l'elaborazione dei risultati delle query, l'elaborazione aggiuntiva incorporata nell'HTML (c'è un if / else nidificato lì se non l'avevi notato), l'escape dell'output e il template HTML sono tutti mescolati insieme. Un altro problema è il parametro $id proveniente direttamente dalla $_REQUEST globale invece di passare un parametro effettivo a una funzione.

Guardando questo, è perfettamente comprensibile il motivo per cui il core di WordPress è rimasto per lo più lo stesso per anni. Il refactoring di questo tipo di codice, soprattutto mantenendo il comportamento esistente, è un compito davvero epico che nessuno vorrebbe fare.

Allora come lo facciamo correttamente? Bene, una cosa da ricordare è che non esiste un vero modo. Abbiamo menzionato le qualità di cui sopra per cui dovremmo lottare: abbiamo bisogno del codice PHP personalizzato di WordPress per essere manutenibile e modulare. Diamo un'occhiata a come possiamo dividere il codice sopra in moduli.

  • Le query SQL dovrebbero essere in un modulo separato, ovviamente. WordPress ha già una classe WP_Query ben astratta che dovrebbe essere usata come esempio.
  • Tutto l'HTML va in un modello. Tratteremo i modelli PHP più avanti.
  • Il codice PHP rimanente dovrebbe essere racchiuso in una funzione, più funzioni se il codice è troppo lungo o complesso per una funzione. I parametri come $id vengono passati tramite argomenti di funzione.

Questa è una riscrittura molto semplificata dell'esempio sopra:

 function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }

Best practice n. 2 per lo sviluppo di WordPress moderno: evitare le variabili globali

WordPress ha troppe variabili globali. Perché le variabili globali sono cattive? Rendono il codice PHP di WordPress difficile da seguire e rendono inaffidabile lo stato dell'applicazione. Qualsiasi pezzo di codice PHP, e questo significa qualsiasi plug-in installato in WordPress, può leggere e scrivere variabili globali, quindi non vi è alcuna garanzia che contengano dati validi. Anche cercare di capire quali variabili globali vengono utilizzate in qualcosa come il Loop non è un compito banale.

Diamo un'occhiata a questo da un punto di vista pratico. Questo esempio viene da WooCommerce. Probabilmente ogni sviluppatore di WordPress sa di cosa si tratta: il Loop:

 <?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>

Lo snippet sopra mostra un modello di prodotto. Come fa a sapere quale prodotto mostrare, dato che non ci sono parametri passati a wc_get_template_part ? Osservando il modello, vediamo che inizia con global $product; , quindi è qui che viene archiviato l'oggetto prodotto corrente.

Ora immagina di avere una pagina del catalogo che ricerca e filtra i prodotti e di voler mostrare un popup di "dettagli del prodotto" rimanendo sulla stessa pagina. Dietro le quinte, lo script front-end esegue una richiesta AJAX per ottenere quel particolare modello di prodotto. Non possiamo semplicemente chiamare wc_get_template_part('content', 'single-product') perché non usa parametri, quindi dobbiamo impostare un paio di variabili globali affinché funzioni.

Casi d'uso più complessi implicherebbero più di un modello, hook attivati ​​in quei modelli e plug-in di terze parti che aggiungono i loro callback a quegli hook. Può degenerare rapidamente. Non abbiamo modo di sapere su quale stato globale si basano quei richiami. I plugin di terze parti sono liberi di modificare qualsiasi variabile globale nei loro callback. Invece di usare il sistema, iniziamo a combattere con il sistema, incontrando strani bug che provengono da uno stato globale inaffidabile.

Non sarebbe più sensato passare quell'ID prodotto come parametro? Quindi potremmo riutilizzare quel modello senza preoccuparci di incasinare le variabili globali utilizzate da WordPress.

Best practice n. 3 per lo sviluppo di WordPress moderno: utilizzare la programmazione orientata agli oggetti (OOP)

La modularità porta al concetto di oggetti e alla programmazione orientata agli oggetti. Al livello molto semplice, OOP è un modo di organizzare il codice. Le funzioni e le variabili sono raggruppate in classi e sono chiamate rispettivamente metodi e proprietà di classe. Il Manuale dei plugin di WordPress consiglia di utilizzare OOP per organizzare il codice PHP personalizzato di WordPress.

Un principio importante in OOP è limitare l'accesso a metodi e proprietà o, in termini PHP, denotarli come private ​​o protected , in modo che solo altri metodi di classe possano accedervi e modificarli. Un termine OOP per questo è incapsulamento : i dati sono incapsulati all'interno della classe e l'unico modo per modificare quei dati è utilizzare i metodi di classe forniti.

Ciò rende il debug e la manutenzione del codice molto più semplice rispetto a quando si utilizzano variabili globali che possono essere modificate in qualsiasi punto dell'intera base di codice. Considera la variabile post globale di WordPress. Puoi accedervi ovunque nel tuo codice e molte funzionalità dipendono dal suo utilizzo. E se potessi limitare le modifiche alle sole funzioni principali di WordPress, ma la lettura sarebbe consentita a chiunque? Nascondere o incapsulare la variabile post globale in una classe e costruire un'interfaccia attorno ad essa lo renderebbe possibile.

Questa è solo una descrizione molto semplice di OOP e di come può essere utilizzata nello sviluppo moderno di WordPress. Per ulteriori approfondimenti, consiglio vivamente l'e-book di Carl Alexander, Scopri la programmazione orientata agli oggetti utilizzando WordPress, che ha il contenuto più completo e utile disponibile sull'argomento OOP in WordPress.

È importante ricordare che OOP non è un proiettile d'argento: codice errato può essere scritto con OOP facilmente come con qualsiasi altro paradigma di programmazione.


Entriamo in alcuni consigli specifici sull'utilizzo di PHP per lo sviluppo di WordPress.

Best Practice PHP moderno n. 1: target PHP 7.0+

L'uso delle moderne funzionalità di PHP richiede, beh, una versione moderna di PHP. Semplicemente non c'è motivo per supportare le versioni di PHP inferiori a 7.0. Anche il core di WordPress richiederà PHP 7.0 già alla fine del 2019.

Tuttavia, è buona norma controllare la versione minima per evitare la "schermata bianca della morte" in ambienti incompatibili. Il frammento di codice seguente mostra l'utilizzo di un'intestazione del plug-in per dichiarare una versione minima di PHP con una condizione di guardia nel codice.

 <?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here

Best practice PHP moderna n. 2: adottare gli standard del settore PHP (Guida allo stile di codifica PSR-2)

I PSR sono raccomandazioni pubblicate dal PHP Framework Interop Group. Sono gli standard del settore de facto in qualsiasi flusso di lavoro PHP moderno e si può tranquillamente affermare che la comunità PHP nel suo insieme segue questi standard. PSR-2 è una raccomandazione che descrive lo stile di codifica. Framework PHP popolari come Symfony e Laravel seguono PSR-2.

Perché dovresti usare PSR-2 invece dello standard di codifica di WordPress? Principalmente perché gli standard di WordPress sono obsoleti e non utilizzano nessuna delle nuove funzionalità del linguaggio. Questo è comprensibile perché il core di WordPress deve seguire i propri standard. Doveva supportare PHP 5.2 fino a poco tempo fa e PSR-2 non è compatibile con PHP 5.2.

Potrebbe non essere ovvio, ma non è necessario utilizzare gli standard di codifica di WordPress a meno che tu non ti impegni a fondo. Non ci sarebbero problemi con l'invio di un plug-in che segue lo standard PSR-2 alla directory dei plug-in di WordPress. In effetti, ci sono degli ottimi argomenti per farlo.

Best practice PHP moderna n. 3: utilizzare un motore di modelli PHP

PHP non è un motore di modelli. È iniziato come uno, ma poi si è evoluto in un linguaggio di programmazione completo e non c'è motivo di continuare a usarlo per la creazione di modelli. I due motori di template più popolari per PHP sono Twig e Blade, usati rispettivamente da Symfony e Laravel. Questo articolo utilizzerà Twig come motore di modelli di esempio; tuttavia, Blade ha caratteristiche e funzionalità comparabili. Ti imploro di esaminare entrambi e decidere tu stesso quale ti si addice meglio.

L'esempio seguente confronta un template PHP e il suo template Twig corrispondente. La visualizzazione e l'escape dell'output è particolarmente dettagliata nell'esempio PHP:

 foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach

In Twig, questo è più conciso e leggibile:

 {% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}

I principali vantaggi di Twig sono:

  • Sintassi leggibile e concisa
  • Fuga automatica dell'uscita
  • Estensione del modello tramite ereditarietà e blocchi

Per quanto riguarda le prestazioni, Twig compila in modelli PHP e non ha praticamente alcun sovraccarico. Twig ha solo un sottoinsieme di costrutti del linguaggio PHP limitato alla sola creazione di modelli. Ciò costringe gli sviluppatori a rimuovere la logica aziendale dai modelli, imponendo così la separazione delle preoccupazioni.

Esiste anche Twig per WordPress. Si chiama Timber ed è un ottimo modo per iniziare a creare modelli migliori. Il tema iniziale Timber è un perfetto esempio di organizzazione dei temi nel modo OOP.

Best practice PHP moderna n. 4: utilizzare Composer

Composer è un gestore delle dipendenze per PHP. È uno strumento che consente di dichiarare le librerie utilizzate da un progetto e quindi automatizzarne download, installazioni e aggiornamenti. Quindi devi solo includere il file di caricamento automatico vendor/autoload.php di Composer invece di richiedere manualmente ogni libreria.

I plugin e i temi di WordPress spesso non utilizzano librerie di terze parti. Ciò è in parte dovuto al fatto che WordPress ha un'API estesa che soddisfa quasi tutte le esigenze e in parte a causa di possibili conflitti di versione. Considera due plugin che richiedono la stessa libreria PHP ma versioni diverse di essa. Il plug-in che viene eseguito per primo ottiene la versione corretta e anche il secondo plug-in ottiene quella versione. Questa è molto probabilmente un'altra situazione di schermo bianco della morte.

Per evitare conflitti, la gestione delle dipendenze dovrebbe essere utilizzata a livello di applicazione, ovvero il sito WordPress nel suo insieme. Questo è ciò che fa Roots (Bedrock, più specificamente). Se utilizzato a livello di pacchetto (plugin o tema), Composer può causare conflitti quando si utilizzano librerie di terze parti. È un problema noto. L'unica soluzione che esiste finora è rinominare gli spazi dei nomi di quella libreria esterna in qualcosa di unico, e non è un compito banale.

C'è ancora un uso per Composer, però: il caricamento automatico delle tue classi. Ma prima di andare oltre con il caricamento automatico, dobbiamo aggiornarci con gli spazi dei nomi PHP.

Best practice PHP moderna n. 5: utilizzare gli spazi dei nomi

Essendo il core di WordPress un progetto legacy, utilizza uno spazio dei nomi globale o, detto diversamente, nessun spazio dei nomi. Qualsiasi classe o funzione dichiarata globalmente (ovvero non all'interno di un'altra classe o funzione) è visibile ovunque nell'intera codebase. I loro nomi devono essere univoci non solo all'interno della tua base di codice, ma per tutti i plugin e i temi che vengono utilizzati ora o potrebbero essere utilizzati in futuro.

La collisione dei nomi (ad es., dichiarare una funzione con un nome già esistente) di solito porta allo schermo bianco della morte e non lo vogliamo. Il codice di WordPress consiglia di anteporre a tutte le tue funzioni e classi qualcosa di unico. Quindi, invece di avere semplici nomi di classe come Order , otteniamo qualcosa come Akrte_Awesome_Plugin_Order dove "Akrte" è il prefisso univoco che ho appena inventato.

Gli spazi dei nomi possono essere visti come gruppi, o cartelle, se utilizziamo un'analogia del filesystem, che aiutano a organizzare il codice ed evitare la collisione dei nomi. Puoi avere spazi dei nomi complessi separati da una barra, proprio come le cartelle nidificate. (Gli spazi dei nomi PHP utilizzano in particolare una barra rovesciata.)

Queste parti dello spazio dei nomi sono chiamate sottospazi dei nomi. La nostra classe di esempio Akrte_Awesome_Plugin_Order sarebbe Akrte\Awesome_Plugin\Order se eseguita utilizzando gli spazi dei nomi. Qui Akrte e Awesome_Plugin sono parti dello spazio dei nomi (o sottospazi dei nomi) e Order è il nome della classe. Quindi puoi aggiungere un'istruzione use e utilizzare solo il nome della classe in seguito. Sembra decisamente meglio:

 use Akrte\Awesome_Plugin\Order; $a = new Order;

Ovviamente, gli spazi dei nomi dovrebbero essere univoci; quindi, dovremmo assegnare al primo sottospazio dei nomi "root" un nome univoco, che di solito è un nome del fornitore. Ad esempio, la classe WC_REST_Order_Notes_V2_Controller potrebbe essere rifatto con spazi dei nomi come questo:

 namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}

La codebase di WooCommerce al giorno d'oggi usa gli spazi dei nomi; ad esempio, nell'API REST di WooCommerce versione 4.

Best practice PHP moderna n. 6: utilizzare un caricatore automatico

Nella maggior parte dei flussi di lavoro PHP, il modo consueto per collegare i file PHP è utilizzare le istruzioni require o include . Man mano che un progetto cresce, ottieni dozzine di istruzioni require nel tuo file di plugin principale. Un caricatore automatico automatizza l'inclusione di file e lo fa solo se necessario. Tecnicamente è una funzione che require un file sa contenente una classe o una funzione quando viene incontrata per la prima volta nel codice. Non è più necessario aggiungere manualmente istruzioni require .

Spesso c'è anche un significativo aumento delle prestazioni poiché un caricatore automatico carica solo i moduli utilizzati in una particolare richiesta. Senza un caricatore automatico, l'intera base di codice è inclusa anche se una richiesta utilizza solo, ad esempio, il 10 percento del codice.

Una funzione di caricamento automatico deve sapere in quali file risiedono le classi e le funzioni. E c'è uno standard PHP-FIG, PSR-4, per questo.

Dice che una parte di uno spazio dei nomi, il prefisso, è designata per corrispondere a una cartella di base. I sotto-spazi dei nomi che lo seguono corrispondono alle cartelle all'interno della cartella di base. Infine, il nome della classe corrisponde al nome del file. Una classe di esempio Akrte\AwesomePlugin\Models\Order richiederebbe la struttura delle cartelle di seguito:

 /awesome-plugin awesome-plugin.php /includes /Models Order.php

Il prefisso dello spazio dei nomi Akrte\AwesomePlugin\ corrisponde alla cartella includes come specificato nella configurazione del caricatore automatico discussa di seguito. Il sotto-spazio dei nomi Models ha una cartella Models corrispondente e la classe Order è contenuta in Order.php .

Fortunatamente, non è necessario implementare da soli una funzione di caricamento automatico. Il compositore può creare un caricatore automatico per te:

  1. Installa il compositore
  2. Crea un file composer.json nella cartella principale del tuo progetto. Dovrebbe contenere queste righe:
 { "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
  1. Esegui composer install .
  2. Includi vendor/autoload.php nella parte superiore del tuo file PHP del plugin principale in questo modo:
 <?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff

Oltre agli spazi dei nomi, l'ultima codebase di WooCommerce utilizza anche un caricatore automatico Composer.


Con questi principi di progettazione PHP coperti, è tempo di ricollegare tutte le nostre lezioni PHP alla personalizzazione del back-end di WordPress con un'ultima raccomandazione.

Best practice n. 4 per lo sviluppo di WordPress moderno: prendi in considerazione l'utilizzo dello stack Roots

Roots è il flusso di lavoro di sviluppo di WordPress moderno più completo che ci sia. Tuttavia, direi che non dovrebbe essere necessariamente utilizzato in ogni progetto WordPress, perché:

  • Le radici devono essere utilizzate dall'inizio. Il motivo più comune, davvero. Il refactoring di un progetto esistente sarebbe troppo costoso.
  • È supponente. Bene quando sei d'accordo, male quando non lo sei. Ad esempio, potresti preferire altri modi per organizzare il tuo tema. Anche i progetti con opinioni richiedono tempo per imparare "a modo loro".
  • Non tutti lo sanno. Lo sviluppatore che verrà dopo di te per mantenere il sito che hai creato con lo stack Roots potrebbe non avere idea di cosa sia e chiedersi cosa sia successo con le cartelle di WordPress. Dovremmo pensare ai nostri colleghi sviluppatori di WordPress.

In generale, vorresti comprendere appieno tutti i vantaggi e gli svantaggi di qualsiasi progetto fortemente supponente prima di impegnarti a utilizzarlo.

Principi moderni di PHP e software: rendere robusto lo sviluppo del back-end di WordPress

È abbastanza ovvio che non esiste un vero modo di scrivere software. I concetti, come la separazione delle preoccupazioni, sono vecchi di decenni; tuttavia, cosa significhi praticamente è sempre stato contestato. Prendi, ad esempio, CSS. All'inizio, lo abbiamo inserito come style in HTML, quindi abbiamo deciso che i fogli CSS separati sono ciò che riguarda la separazione delle preoccupazioni.

Avanti veloce di un decennio: le app JavaScript odierne utilizzano i componenti come implementazione di separazione degli interessi. Gli sviluppatori front-end gravitano verso CSS-in-JS, e ciò significa sostanzialmente incorporare nuovamente CSS in HTML (beh, non è così semplice, ma hai un'idea). Il cerchio è completo!

Le best practice riguardano da sempre il miglioramento dell'esperienza degli sviluppatori:

I programmi devono essere scritti per essere letti dalle persone e solo incidentalmente per l'esecuzione delle macchine.

Abelson & Sussman, Struttura e interpretazione dei programmi per computer

Alcune delle pratiche in questo tutorial di PHP WordPress sono veloci e facili da implementare nel tuo progetto. Ad esempio, caricatore automatico: fallo una volta per progetto e divertiti. D'altra parte, le nuove idee di architettura del software richiedono tempo, pratica e numerose iterazioni per essere a portata di mano ea proprio agio. Le ricompense sono molto maggiori, però. Non solo sarai più efficiente in quello che fai, ma ti divertirai anche di più. E il godimento regolare del lavoro che fai per i clienti è forse l'unico modo in cui può essere sostenibile.