Symfony - Concetti avanzati
In questo capitolo, impareremo alcuni concetti avanzati nel framework Symfony.
Cache HTTP
La memorizzazione nella cache in un'applicazione Web migliora le prestazioni. Ad esempio, i prodotti caldi in un'applicazione web del carrello degli acquisti possono essere memorizzati nella cache per un periodo di tempo limitato, in modo che possano essere presentati al cliente in modo rapido senza toccare il database. Di seguito sono riportati alcuni componenti di base di Cache.
Elemento cache
Cache Item è una singola unità di informazioni memorizzata come coppia chiave / valore. Ilkey dovrebbe essere stringa e valuepuò essere qualsiasi oggetto PHP. Gli oggetti PHP vengono memorizzati come stringa dalla serializzazione e riconvertiti in oggetti durante la lettura degli elementi.
Adattatore cache
Cache Adapter è il meccanismo effettivo per archiviare l'articolo in un negozio. L'archivio può essere una memoria, un file system, un database, un redis, ecc. Il componente cache fornisce un fileAdapterInterfaceattraverso il quale un adattatore può memorizzare l'elemento della cache in un archivio back-end. Sono disponibili molti adattatori cache incorporati. Alcuni di loro sono i seguenti:
Adattatore cache di array: gli elementi della cache vengono archiviati nell'array PHP.
Adattatore per la cache del file system: gli elementi della cache vengono archiviati nei file.
Adattatore cache file PHP: gli elementi della cache vengono archiviati come file php.
Adattatore cache APCu: gli elementi della cache vengono archiviati nella memoria condivisa utilizzando l'estensione PHP APCu.
Redis Cache Adapter: gli elementi della cache vengono archiviati nel server Redis.
PDO e Doctrine DBAL Cache Adapter - Gli elementi della cache vengono memorizzati nel database.
Chain Cache Adapter: combina più adattatori cache a scopo di replica.
Adattatore cache proxy: gli elementi della cache vengono archiviati utilizzando un adattatore di terze parti, che implementa CacheItemPoolInterface.
Pool di cache
Cache Pool è un repository logico di elementi della cache. I pool di cache vengono implementati dagli adattatori della cache.
Applicazione semplice
Creiamo una semplice applicazione per comprendere il concetto di cache.
Step 1 - Crea una nuova applicazione, cache-example.
cd /path/to/app
mkdir cache-example
cd cache-example
Step 2 - Installa il componente cache.
composer require symfony/cache
Step 3 - Crea un adattatore per file system.
require __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cache = new FilesystemAdapter();
Step 4 - Crea un elemento della cache utilizzando getItem e setmetodo di adattatore. getItem recupera l'elemento della cache utilizzando la sua chiave. se la chiave non è persistente, crea un nuovo elemento. Il metodo set memorizza i dati effettivi.
$usercache = $cache->getitem('item.users');
$usercache->set(['jon', 'peter']);
$cache->save($usercache);
Step 5 - Accedi all'elemento della cache utilizzando getItem, isHit e getmetodo. isHit informa la disponibilità dell'elemento cache e il metodo get fornisce i dati effettivi.
$userCache = $cache->getItem('item.users');
if(!$userCache->isHit()) {
echo "item.users is not available";
} else {
$users = $userCache->get();
var_dump($users);
}
Step 6 - Elimina l'elemento della cache utilizzando deleteItem metodo.
$cache->deleteItem('item.users');
L'elenco completo del codice è il seguente.
<?php
require __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cache = new FilesystemAdapter();
$usercache = $cache->getitem('item.users');
$usercache->set(['jon', 'peter']);
$cache->save($usercache);
$userCache = $cache->getItem('item.users');
if(!$userCache->isHit()) {
echo "item.users is not available";
} else {
$users = $userCache->get();
var_dump($users);
}
$cache->deleteItem('item.users');
?>
Risultato
array(2) {
[0]=>
string(3) "jon"
[1]=>
string(5) "peter"
}
Debug
Il debug è una delle attività più frequenti durante lo sviluppo di un'applicazione. Symfony fornisce un componente separato per facilitare il processo di debugging. Possiamo abilitare gli strumenti di debug di Symfony semplicemente chiamando il fileenable metodo della classe Debug.
use Symfony\Component\Debug\Debug
Debug::enable()
Symfony fornisce due classi, ErrorHandler e ExceptionHandlera scopo di debug. Mentre ErrorHandler rileva gli errori PHP e li converte in eccezioni, ErrorException o FatalErrorException, ExceptionHandler rileva le eccezioni PHP non rilevate e le converte in utili risposte PHP. ErrorHandler e ExceptionHandler sono disabilitati per impostazione predefinita. Possiamo abilitarlo utilizzando il metodo di registrazione.
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
ErrorHandler::register();
ExceptionHandler::register();
In un'applicazione web Symfony, il debug environmentè fornito da DebugBundle. Registra il pacchetto in AppKernelregisterBundles metodo per abilitarlo.
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
}
Profiler
Lo sviluppo di un'applicazione richiede uno strumento di profilazione di livello mondiale. Lo strumento di profilazione raccoglie tutte le informazioni di runtime su un'applicazione come il tempo di esecuzione, il tempo di esecuzione dei singoli moduli, il tempo impiegato da un'attività del database, l'utilizzo della memoria, ecc. Un'applicazione web necessita di molte più informazioni come l'ora della richiesta, tempo impiegato per creare una risposta, ecc. oltre alle metriche di cui sopra.
Symfony abilita tutte queste informazioni in un'applicazione web per impostazione predefinita. Symfony fornisce un bundle separato per la profilazione web chiamatoWebProfilerBundle. Il bundle del profiler Web può essere abilitato in un'applicazione Web registrando il bundle nel metodo registerBundles di AppKernel.
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
}
Il componente del profilo web può essere configurato in web_profile section del file di configurazione dell'applicazione, app/config/config.xml
web_profiler:
toolbar: false
position: bottom
L'applicazione Symfony mostra i dati profilati in fondo alla pagina come una sezione distinta.
Symfony fornisce anche un modo semplice per aggiungere dettagli personalizzati sulla pagina nei dati del profilo usando DataCollectorInterface interfacee modello di ramoscello. In breve, Symfony consente a uno sviluppatore web di creare un'applicazione di livello mondiale fornendo un ottimo framework di profiling con relativa facilità.
Sicurezza
Come discusso in precedenza, Symfony fornisce un solido framework di sicurezza attraverso il suo componente di sicurezza. La componente sicurezza è suddivisa in quattro sottocomponenti come segue.
- symfony / security-core - Funzionalità di sicurezza principali.
- symfony / security-http - Funzionalità di sicurezza integrata nel protocollo HTTP.
- symfony / security-csrf - Protezione contro la falsificazione di richieste cross-site in un'applicazione web.
- symfony / security-acl - Framework di sicurezza basato su elenchi di controllo di accesso avanzato.
Autenticazione e autorizzazione semplici
Impariamo il concetto di autenticazione e autorizzazione utilizzando una semplice applicazione demo.
Step 1 - Crea una nuova applicazione web securitydemo utilizzando il seguente comando.
symfony new securitydemo
Step 2- Abilitare la funzione di sicurezza nell'applicazione utilizzando il file di configurazione della sicurezza. La configurazione relativa alla sicurezza viene inserita in un file separato,security.yml. La configurazione predefinita è la seguente.
security:
providers:
in_memory:
memory: ~
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
#http_basic: ~
#form_login: ~
La configurazione predefinita abilita il provider di sicurezza basato sulla memoria e l'accesso anonimo a tutte le pagine. La sezione firewall esclude i file che corrispondono al pattern,^/(_(profiler|wdt)|css|images|js)/dal framework di sicurezza. Il modello predefinito include fogli di stile, immagini e JavaScript (oltre a strumenti di sviluppo come il profiler).
Step 3 - Abilita il sistema di autenticazione della sicurezza basato su HTTP aggiungendo l'opzione http_basic nella sezione principale come segue.
security:
# ...
firewalls:
# ...
main:
anonymous: ~
http_basic: ~
#form_login: ~
Step 4- Aggiungi alcuni utenti nella sezione del provider di memoria. Inoltre, aggiungi ruoli per gli utenti.
security:
providers:
in_memory:
memory:
users:
myuser:
password: user
roles: 'ROLE_USER'
myadmin:
password: admin
roles: 'ROLE_ADMIN'
Abbiamo aggiunto due utenti, utente nel ruolo ROLE_USER e admin nel ruolo ROLE_ADMIN.
Step 5- Aggiungi l'encoder per ottenere i dettagli completi dell'utente attualmente connesso. Lo scopo del codificatore è ottenere i dettagli completi dell'oggetto utente corrente dalla richiesta web.
security:
# ...
encoders:
Symfony\Component\Security\Core\User\User: bcrypt
# ...
Symfony fornisce un'interfaccia, UserInterface per ottenere i dettagli dell'utente come nome utente, ruoli, password, ecc. Dobbiamo implementare l'interfaccia secondo i nostri requisiti e configurarla nella sezione codificatore.
Ad esempio, si consideri che i dettagli dell'utente sono nel database. Quindi, dobbiamo creare una nuova classe User e implementare i metodi UserInterface per ottenere i dettagli dell'utente dal database. Una volta che i dati sono disponibili, il sistema di sicurezza li utilizza per consentire / negare all'utente. Symfony fornisce un'implementazione utente predefinita per il provider di memoria. L'algoritmo viene utilizzato per decrittografare la password utente.
Step 6 - Crittografa la password utente utilizzando bcryptalgoritmo e posizionarlo nel file di configurazione. Da quando abbiamo usatobcryptalgoritmo, l'oggetto Utente tenta di decrittografare la password specificata nel file di configurazione e quindi cerca di abbinarla alla password inserita dall'utente. L'applicazione console Symfony fornisce un semplice comando per crittografare la password.
php bin/console security:encode-password admin
Symfony Password Encoder Utility
================================
------------------ -----------------------------------
Key Value
------------------ ------------------------------------
Encoder used Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder
Encoded password
$2y$12$0Hy6/.MNxWdFcCRDdstHU.hT5j3Mg1tqBunMLIUYkz6..IucpaPNO
------------------ ------------------------------------
! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt.
[OK] Password encoding succeeded
Step 7 - Utilizzare il comando per generare la password crittografata e aggiornarla nel file di configurazione.
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/security.html
security:
# http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
in_memory:
memory:
users:
user:
password: $2y$13$WsGWNufreEnVK1InBXL2cO/U7WftvfNvH
Vb/IJBH6JiYoDwVN4zoi
roles: 'ROLE_USER'
admin:
password: $2y$13$jQNdIeoNV1BKVbpnBuhKRuOL01NeMK
F7nEqEi/Mqlzgts0njK3toy
roles: 'ROLE_ADMIN'
encoders:
Symfony\Component\Security\Core\User\User: bcrypt
firewalls:
# disables authentication for assets and the profiler,
# adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
# activate different ways to authenticate
# http://symfony.com/doc/current/security.html#a-co
nfiguring-howyour-users-will-authenticate
http_basic: ~
# http://symfony.com/doc/current/cookbook/security/
form_login_setup.html
#form_login: ~
Step 8- Ora, applica la sicurezza a qualche sezione dell'applicazione. Ad esempio, limitare la sezione di amministrazione agli utenti nel ruolo, ROLE_ADMIN.
security:
# ...
firewalls:
# ...
default:
# ...
access_control:
# require ROLE_ADMIN for /admin*
- { path: ^/admin, roles: 'ROLE_ADMIN' }
Step 9 - Aggiungi una pagina di amministrazione in DefaultController come segue.
/**
* @Route("/admin")
*/
public function adminLandingAction() {
return new Response('<html><body>This is admin section.</body></html>');
}
Step 10- Infine, accedi alla pagina di amministrazione per verificare la configurazione della sicurezza in un browser. Il browser chiederà il nome utente e la password e consentirà solo agli utenti configurati.
Risultato
Flusso di lavoro
Il flusso di lavoro è un concetto avanzato utilizzato in molte applicazioni aziendali. In un'applicazione di e-commerce, il processo di consegna del prodotto è un flusso di lavoro. Il prodotto viene prima fatturato (creazione dell'ordine), acquistato dal negozio e imballato (imballaggio / pronto per la spedizione) e spedito all'utente. In caso di problemi, il prodotto ritorna dall'utente e l'ordine viene annullato. L'ordine del flusso d'azione è molto importante. Ad esempio, non possiamo fornire un prodotto senza fatturazione.
Il componente Symfony fornisce un modo orientato agli oggetti per definire e gestire un flusso di lavoro. Viene chiamata ogni fase di un processoplace e viene chiamata l'azione richiesta per spostarsi da un luogo all'altro transition. La raccolta di luoghi e la transizione per creare un flusso di lavoro è chiamata aWorkflow definition.
Cerchiamo di comprendere il concetto di flusso di lavoro creando una semplice applicazione per la gestione delle ferie.
Step 1 - Crea una nuova applicazione, workflow-example.
cd /path/to/dev
mkdir workflow-example
cd workflow-example
composer require symfony/workflow
Step 2 - Crea una nuova classe, Leave avendo applied_by, leave_on e status attributi.
class Leave {
public $applied_by;
public $leave_on;
public $status;
}
In questo caso, apply_by si riferisce ai dipendenti che vogliono ferie. leave_on si riferisce alla data del congedo. lo stato si riferisce allo stato di congedo.
Step 3 - La gestione delle ferie ha quattro posizioni, applicate, in_process e approvate / rifiutate.
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
$builder = new DefinitionBuilder();
$builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']);
Qui abbiamo creato una nuova definizione usando DefinitionBuilder e ha aggiunto luoghi utilizzando addPlaces metodo.
Step 4 - Definire le azioni necessarie per spostarsi da un luogo a un altro.
$builder->addTransition(new Transition('to_process', 'applied', 'in_process'));
$builder->addTransition(new Transition('approve', 'in_process', 'approved'));
$builder->addTransition(new Transition('reject', 'in_process', 'rejected'));
Qui abbiamo tre transizioni, to_process, approve e reject. La transizione to_process accetta l'applicazione di chiusura e sposta il punto da applicato a in_process. approva transizione approva la richiesta di ferie e sposta il luogo in approvato. Allo stesso modo, Rifiuta transizione rifiuta la domanda di permesso e sposta il luogo in rifiutato. Abbiamo creato tutte le transizioni utilizzando il metodo addTransition.
Step 5 - Costruisci la definizione utilizzando il metodo build.
$definition = $builder->build();
Step 6 - Facoltativamente, la definizione può essere scaricata come formato graphviz dot, che può essere convertito in file immagine a scopo di riferimento.
$dumper = new GraphvizDumper();
echo $dumper->dump($definition);
Step 7 - Creare un negozio di marcatura, che viene utilizzato per memorizzare i luoghi / lo stato corrente dell'oggetto.
$marking = new SingleStateMarkingStore('status');
Qui abbiamo usato SingleStateMarkingStoreclass per creare il segno e contrassegna lo stato corrente nella proprietà status dell'oggetto. Nel nostro esempio, l'oggetto è Lascia oggetto.
Step 8 - Creare il flusso di lavoro utilizzando la definizione e la marcatura.
$leaveWorkflow = new Workflow($definition, $marking);
Qui abbiamo usato Workflow class per creare il flusso di lavoro.
Step 9 - Aggiungere il flusso di lavoro nel registro del framework del flusso di lavoro utilizzando Registry classe.
$registry = new Registry();
$registry->add($leaveWorkflow, Leave::class);
Step 10 - Infine, utilizza il flusso di lavoro per scoprire se una determinata transizione viene applicata utilizzando can metodo e, in caso affermativo, applyla transizione utilizzando il metodo apply. Quando viene applicata una transizione, lo stato dell'oggetto si sposta da un punto a un altro.
$workflow = $registry->get($leave);
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n";
echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n";
$workflow->apply($leave, 'to_process');
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n";
echo $leave->status . "\r\n";
$workflow->apply($leave, 'approve');
echo $leave->status . "\r\n";
La codifica completa è la seguente:
<?php
require __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
class Leave {
public $applied_by;
public $leave_on;
public $status;
}
$builder = new DefinitionBuilder();
$builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']);
$builder->addTransition(new Transition('to_process', 'applied', 'in_process'));
$builder->addTransition(new Transition('approve', 'in_process', 'approved'));
$builder->addTransition(new Transition('reject', 'in_process', 'rejected'));
$definition = $builder->build();
// $dumper = new GraphvizDumper();
// echo $dumper->dump($definition);
$marking = new SingleStateMarkingStore('status');
$leaveWorkflow = new Workflow($definition, $marking);
$registry = new Registry();
$registry->add($leaveWorkflow, Leave::class);
$leave = new Leave();
$leave->applied_by = "Jon";
$leave->leave_on = "1998-12-12";
$leave->status = 'applied';
$workflow = $registry->get($leave);
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n";
echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n";
$workflow->apply($leave, 'to_process');
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n";
echo $leave->status . "\r\n";
$workflow->apply($leave, 'approve');
echo $leave->status . "\r\n";
?>
Risultato
Can we approve the leave now?
Can we approve the start process now? 1
Can we approve the leave now? 1
in_process
approved