Una guida alla riflessione in PHP (e come si gioca negli unit test)
Nelle ultime settimane ho scritto di unit test per i membri del sito (e qualcosa che ho intenzione di continuare a fare nel prossimo post). È qualcosa che penso; se scrivi codice lato server, dovresti farlo.
Certo, è più facile per me dirlo che farlo, quindi anche se cerco di assicurarmi di fare un buon lavoro, c’è sempre spazio per me per migliorare. Lo dico più come un controllo di istinto personale che altro, quindi divago.
Uno dei concetti che emergono spesso durante i test è l’idea di testare i valori degli attributi privati. Ad esempio, supponiamo che tu abbia un setter, ma non hai necessariamente un getter per quel particolare valore.
È facile dire "Beh, allora devi scrivere un getter", ma non è sempre così. Voglio dire, cosa succede se memorizzi alcune informazioni all’interno della classe che non devono essere esposte a classi di terze parti?
Come possiamo quindi scrivere test su quel tipo di dati quando vogliamo accedervi ma non abbiamo la capacità di farlo e non vogliamo compromettere l’integrità del nostro lavoro?
È qui che entra in gioco la riflessione.
Riflessione in PHP
Nello specifico, per capire come ispezionare il valore di una determinata variabile dall’esterno verso l’interno, è necessario saper utilizzare la riflessione.
Fortunatamente, PHP ci fornisce una potente API per farlo, e probabilmente vale la pena approfondire i dettagli in un altro post. Ma, per questo, rimarrò con le basi.
Una definizione di lavoro
Innanzitutto, uno sguardo a cosa significa riflessione. Il manuale PHP lo definisce come :
La classe ReflectionClass riporta informazioni su una classe.
Ma penso che valga la pena avere qualcosa di un po’ più solido. Andiamo con qualcosa del genere almeno per questo post:
La riflessione è il modo in cui un programma controlla se stesso e si modifica durante l’esecuzione.
Forse non è eccezionale; forse no.
Ma servirà allo scopo questo post.
Leggere un valore attraverso la riflessione
Supponiamo che per questo post tu abbia una classe namespace su AcmePluginAPIClient e abbia una proprietà chiamata username. Daremo un’occhiata a come potrebbe essere un’implementazione molto semplice di questo in seguito.
Naturalmente, sarebbe molto più arricchito in un vero e proprio plugin.
Diciamo, però, che vuoi impostare il valore dell’attributo e poi leggerne il valore. L’avvertenza è che la proprietà è contrassegnata come privata e non c’è modo di leggerla dall’esterno.
È qui che la riflessione torna utile. Cioè, possiamo usare una parte del programma per guardare se stesso e riferire ciò che vede. (Riflessione, capito? È come quando vogliamo sapere cosa succede a noi stessi e nessun altro è in giro, quindi ci guardiamo allo specchio e vediamo cosa c’è lì.)
Per fare ciò, devi fare cinque cose:
- Crea un’istanza della classe che vuoi testare,
- Imposta il valore della variabile,
- Prendi un’istanza di ReflectionClass per la classe che vogliamo testare,
- Imposta la sua proprietà su accessibile,
- Leggi il valore.
Quindi ecco una serie di informazioni che forniranno i passaggi necessari per fare esattamente questo.
1 Istanziare la classe
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
// Other functions for class implementation...
}
2 Impostare il valore
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
public function setUsername($username)
{
$this->username = $username;
}
// Other functions for class implementation...
}
3 Istanziare oggetti riflessi
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
// More to come...
}
}
4. Impostare la proprietà, contrassegnarla come accessibile
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
$usernameProperty = new ReflectionObject($client);
$usernameProperty->setAccessible(true);
// More to come...
}
}
5 Leggi il valore
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
// Grab a reference to the private property by making it accessible.
$usernameProperty = new ReflectionObject($client);
$usernameProperty->setAccessible(true);
// And finally, read it's value.
$usernameValue = $usernameProperty->getValue($client);
}
}
E questo è il Primer di base sulla riflessione
A questo punto, questo dovrebbe darti alcune informazioni di base su cos’è Reflection, come usarlo e perché è utile soprattutto nel caso di unit test.
Questo è uno di quei concetti che possono diventare più complessi perché l’API di riflessione di PHP è piuttosto potente (ma relativamente facile da capire). Quando lo accoppi con i test unitari, però, ci sono molte cose che possono essere fatte.
La mia spina spudorata
Detto questo, se sei interessato ad apprendere i dettagli per questo tipo di cose, non esitare a controllare l’ area riservata ai membri del sito. Sto costruendo, ogni settimana, un arretrato di cose per aiutarci a concentrarci sull’adozione di pratiche migliori come sviluppatori di WordPress.
Unit Testing, Reflection e altro ancora sono solo l’ultima parte di esso.
