Symfony - Erweiterte Konzepte

In diesem Kapitel lernen wir einige fortgeschrittene Konzepte im Symfony-Framework kennen.

HTTP-Cache

Das Zwischenspeichern in einer Webanwendung verbessert die Leistung. Beispielsweise können heiße Produkte in einer Warenkorb-Webanwendung für eine begrenzte Zeit zwischengespeichert werden, so dass sie dem Kunden schnell präsentiert werden können, ohne auf die Datenbank zuzugreifen. Im Folgenden finden Sie einige grundlegende Komponenten des Cache.

Cache-Element

Cache-Element ist eine einzelne Informationseinheit, die als Schlüssel / Wert-Paar gespeichert ist. Daskey sollte string und sein valuekann ein beliebiges PHP-Objekt sein. PHP-Objekte werden durch Serialisierung als Zeichenfolge gespeichert und beim Lesen der Elemente wieder in Objekte konvertiert.

Cache-Adapter

Der Cache-Adapter ist der eigentliche Mechanismus zum Speichern des Elements in einem Geschäft. Der Speicher kann ein Speicher, ein Dateisystem, eine Datenbank, Redis usw. sein. Die Cache-Komponente bietet eineAdapterInterfaceüber die ein Adapter Cache-Elemente in einem Back-End-Speicher speichern kann. Es sind viele integrierte Cache-Adapter verfügbar. Nur wenige von ihnen sind wie folgt:

  • Array-Cache-Adapter - Cache-Elemente werden im PHP-Array gespeichert.

  • Dateisystem-Cache-Adapter - Cache-Elemente werden in Dateien gespeichert.

  • PHP-Dateien Cache-Adapter - Cache-Elemente werden als PHP-Dateien gespeichert.

  • APCu-Cache-Adapter - Cache-Elemente werden mithilfe der PHP-APCu-Erweiterung im gemeinsam genutzten Speicher gespeichert.

  • Redis-Cache-Adapter - Cache-Elemente werden auf dem Redis-Server gespeichert.

  • PDO- und Doctrine DBAL-Cache-Adapter - Cache-Elemente werden in der Datenbank gespeichert.

  • Ketten-Cache-Adapter - Kombiniert mehrere Cache-Adapter für Replikationszwecke.

  • Proxy-Cache-Adapter - Cache-Elemente werden mithilfe eines Adapters eines Drittanbieters gespeichert, der CacheItemPoolInterface implementiert.

Cache-Pool

Der Cache-Pool ist ein logisches Repository für Cache-Elemente. Cache-Pools werden von Cache-Adaptern implementiert.

Einfache Anwendung

Lassen Sie uns eine einfache Anwendung erstellen, um das Cache-Konzept zu verstehen.

Step 1 - Erstellen Sie eine neue Anwendung, cache-example.

cd /path/to/app 
mkdir cache-example 
cd cache-example

Step 2 - Installieren Sie die Cache-Komponente.

composer require symfony/cache

Step 3 - Erstellen Sie einen Dateisystemadapter.

require __DIR__ . '/vendor/autoload.php';  
use Symfony\Component\Cache\Adapter\FilesystemAdapter;  
$cache = new FilesystemAdapter();

Step 4 - Erstellen Sie ein Cache-Element mit getItem und setMethode des Adapters. getItem ruft das Cache-Element mit seinem Schlüssel ab. Wenn der Schlüssel nicht dauerhaft ist, wird ein neues Element erstellt. Die set-Methode speichert die tatsächlichen Daten.

$usercache = $cache->getitem('item.users'); 
$usercache->set(['jon', 'peter']); 
$cache->save($usercache);

Step 5 - Greifen Sie mit auf das Cache-Element zu getItem, isHit und getMethode. isHit informiert über die Verfügbarkeit des Cache-Elements und die get-Methode liefert die tatsächlichen Daten.

$userCache = $cache->getItem('item.users'); 
if(!$userCache->isHit()) { 
   echo "item.users is not available"; 
} else { 
   $users = $userCache->get(); 
   var_dump($users); 
}

Step 6 - Löschen Sie das Cache-Element mit deleteItem Methode.

$cache->deleteItem('item.users');

Die vollständige Codeliste lautet wie folgt.

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

Ergebnis

array(2) { 
   [0]=> 
   string(3) "jon" 
   [1]=> 
   string(5) "peter" 
}

Debuggen

Das Debuggen ist eine der häufigsten Aktivitäten bei der Entwicklung einer Anwendung. Symfony bietet eine separate Komponente, um das Debuggen zu vereinfachen. Wir können Symfony-Debugging-Tools aktivieren, indem wir einfach die aufrufenenable Methode der Debug-Klasse.

use Symfony\Component\Debug\Debug  
Debug::enable()

Symfony bietet zwei Klassen: ErrorHandler und ExceptionHandlerzum Debuggen. Während ErrorHandler PHP-Fehler abfängt und in Ausnahmen, ErrorException oder FatalErrorException, konvertiert, fängt ExceptionHandler nicht erfasste PHP-Ausnahmen ab und konvertiert sie in nützliche PHP-Antworten. ErrorHandler und ExceptionHandler sind standardmäßig deaktiviert. Wir können es mit der Registermethode aktivieren.

use Symfony\Component\Debug\ErrorHandler; 
use Symfony\Component\Debug\ExceptionHandler;  
ErrorHandler::register(); 
ExceptionHandler::register();

In einer Symfony-Webanwendung wird die debug environmentwird von DebugBundle bereitgestellt. Registrieren Sie das Bundle bei AppKernelregisterBundles Methode, um es zu aktivieren.

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 
}

Profiler

Die Entwicklung einer Anwendung erfordert ein erstklassiges Profiling-Tool. Das Profiling-Tool sammelt alle Laufzeitinformationen zu einer Anwendung, z. B. Ausführungszeit, Ausführungszeit einzelner Module, Zeitaufwand für eine Datenbankaktivität, Speichernutzung usw. Eine Webanwendung benötigt wesentlich mehr Informationen wie den Zeitpunkt der Anforderung. Zeitaufwand für die Erstellung einer Antwort usw. zusätzlich zu den oben genannten Metriken.

Symfony aktiviert standardmäßig alle diese Informationen in einer Webanwendung. Symfony bietet ein separates Bundle für die Erstellung von Webprofilen anWebProfilerBundle. Das Webprofiler-Bundle kann in einer Webanwendung aktiviert werden, indem das Bundle in der registerBundles-Methode des AppKernel registriert wird.

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 
}

Die Webprofilkomponente kann unter konfiguriert werden web_profile section der Anwendungskonfigurationsdatei, app/config/config.xml

web_profiler: 
   toolbar:      false 
   position:     bottom

Die Symfony-Anwendung zeigt die Profildaten am Ende der Seite als separaten Abschnitt an.

Symfony bietet auch eine einfache Möglichkeit, benutzerdefinierte Details zu der Seite in den Profildaten mit hinzuzufügen DataCollectorInterface interfaceund Zweigschablone. Kurz gesagt, mit Symfony kann ein Webentwickler eine erstklassige Anwendung erstellen, indem er relativ einfach ein hervorragendes Profilierungsframework bereitstellt.

Sicherheit

Wie bereits erwähnt, bietet Symfony über seine Sicherheitskomponente ein robustes Sicherheitsframework. Die Sicherheitskomponente ist wie folgt in vier Unterkomponenten unterteilt.

  • symfony / security-core - Kernsicherheitsfunktionalität.
  • symfony / security-http - Integrierte Sicherheitsfunktion im HTTP-Protokoll.
  • symfony / security-csrf - Schutz vor standortübergreifender Fälschung von Anforderungen in einer Webanwendung.
  • symfony / security-acl - Erweitertes, auf Zugriffssteuerungslisten basierendes Sicherheitsframework.

Einfache Authentifizierung und Autorisierung

Lassen Sie uns das Konzept der Authentifizierung und Autorisierung mithilfe einer einfachen Demo-Anwendung lernen.

Step 1 - Erstellen Sie eine neue Webanwendung securitydemo mit dem folgenden Befehl.

symfony new securitydemo

Step 2- Aktivieren Sie die Sicherheitsfunktion in der Anwendung mithilfe der Sicherheitskonfigurationsdatei. Die sicherheitsrelevante Konfiguration wird in einer separaten Datei abgelegt.security.yml. Die Standardkonfiguration lautet wie folgt.

security: 
   providers: 
      in_memory: 
         memory: ~ 
   firewalls: 
      dev: 
         pattern: ^/(_(profiler|wdt)|css|images|js)/ 
         security: false  
   main: 
      anonymous: ~ 
      #http_basic: ~ 
      #form_login: ~

Die Standardkonfiguration ermöglicht den speicherbasierten Sicherheitsanbieter und den anonymen Zugriff auf alle Seiten. Der Firewall-Abschnitt schließt die Dateien aus, die dem Muster entsprechen.^/(_(profiler|wdt)|css|images|js)/aus dem Sicherheitsrahmen. Das Standardmuster enthält Stylesheets, Bilder und JavaScripts (sowie Entwicklungswerkzeuge wie den Profiler).

Step 3 - Aktivieren Sie das HTTP-basierte Sicherheitsauthentifizierungssystem, indem Sie im Hauptabschnitt die Option http_basic wie folgt hinzufügen.

security: 
   # ...  
   firewalls: 
      # ...  
      main: 
         anonymous: ~ 
         http_basic: ~ 
         #form_login: ~

Step 4- Fügen Sie einige Benutzer im Abschnitt Speicheranbieter hinzu. Fügen Sie außerdem Rollen für die Benutzer hinzu.

security: 
   providers: 
      in_memory: 
         memory: 
            users: 
               myuser: 
                  password: user 
                  roles: 'ROLE_USER' 
                     myadmin: 
                        password: admin 
                        roles: 'ROLE_ADMIN'

Wir haben zwei Benutzer hinzugefügt, Benutzer in der Rolle ROLE_USER und Administrator in der Rolle ROLE_ADMIN.

Step 5- Fügen Sie den Encoder hinzu, um vollständige Details des aktuell angemeldeten Benutzers zu erhalten. Der Zweck des Encoders besteht darin, vollständige Details des aktuellen Benutzerobjekts aus der Webanforderung abzurufen.

security: 
   # ... 
   encoders: 
      Symfony\Component\Security\Core\User\User: bcrypt 
      # ...

Symfony bietet eine Schnittstelle, UserInterface Um Benutzerdetails wie Benutzername, Rollen, Passwort usw. abzurufen, müssen wir die Schnittstelle gemäß unserer Anforderung implementieren und im Abschnitt Encoder konfigurieren.

Angenommen, die Benutzerdetails befinden sich in der Datenbank. Anschließend müssen wir eine neue Benutzerklasse erstellen und UserInterface-Methoden implementieren, um die Benutzerdetails aus der Datenbank abzurufen. Sobald die Daten verfügbar sind, verwendet das Sicherheitssystem sie, um den Benutzer zuzulassen / zu verweigern. Symfony bietet eine Standardbenutzerimplementierung für den Speicheranbieter. Der Algorithmus wird verwendet, um das Benutzerkennwort zu entschlüsseln.

Step 6 - Verschlüsseln Sie das Benutzerpasswort mit bcryptAlgorithmus und platzieren Sie es in der Konfigurationsdatei. Da haben wir benutztbcryptAlgorithmus, Benutzerobjekt versucht, das in der Konfigurationsdatei angegebene Kennwort zu entschlüsseln und versucht dann, mit dem vom Benutzer eingegebenen Kennwort übereinzustimmen. Die Symfony-Konsolenanwendung bietet einen einfachen Befehl zum Verschlüsseln des Kennworts.

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 - Verwenden Sie den Befehl, um das verschlüsselte Kennwort zu generieren und in der Konfigurationsdatei zu aktualisieren.

# 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- Wenden Sie jetzt die Sicherheit auf einen Teil der Anwendung an. Beschränken Sie beispielsweise den Administratorbereich auf die Benutzer in der Rolle ROLE_ADMIN.

security: 
   # ... 
      firewalls: 
         # ... 
      default: 
         # ...  
      access_control: 
         # require ROLE_ADMIN for /admin* 
         - { path: ^/admin, roles: 'ROLE_ADMIN' }

Step 9 - Fügen Sie eine Admin-Seite in DefaultController wie folgt hinzu.

/** 
   * @Route("/admin") 
*/ 
public function adminLandingAction() { 
   return new Response('<html><body>This is admin section.</body></html>'); 
}

Step 10- Rufen Sie abschließend die Administrationsseite auf, um die Sicherheitskonfiguration in einem Browser zu überprüfen. Der Browser fragt nach Benutzername und Passwort und lässt nur konfigurierte Benutzer zu.

Ergebnis

Arbeitsablauf

Workflow ist ein fortschrittliches Konzept, das in vielen Unternehmensanwendungen verwendet wird. In einer E-Commerce-Anwendung ist der Produktlieferungsprozess ein Workflow. Das Produkt wird zuerst in Rechnung gestellt (Auftragserstellung), aus dem Geschäft bezogen und verpackt (Verpackung / Versandbereit) und an den Benutzer versandt. Wenn es ein Problem gibt, kehrt das Produkt vom Benutzer zurück und die Bestellung wird zurückgesetzt. Die Reihenfolge des Aktionsflusses ist sehr wichtig. Zum Beispiel können wir ein Produkt nicht ohne Abrechnung liefern.

Die Symfony-Komponente bietet eine objektorientierte Möglichkeit zum Definieren und Verwalten eines Workflows. Jeder Schritt in einem Prozess wird aufgerufenplace und die Aktion, die erforderlich ist, um von einem Ort zum anderen zu gelangen, wird aufgerufen transition. Die Sammlung von Orten und der Übergang zum Erstellen eines Workflows wird als a bezeichnetWorkflow definition.

Lassen Sie uns das Konzept des Workflows verstehen, indem Sie eine einfache Anwendung für das Urlaubsmanagement erstellen.

Step 1 - Erstellen Sie eine neue Anwendung, workflow-example.

cd /path/to/dev 
mkdir workflow-example 

cd workflow-example 
composer require symfony/workflow

Step 2 - Erstellen Sie eine neue Klasse, Leave haben applied_by, leave_on und status Attribute.

class Leave { 
   public $applied_by; 
   public $leave_on;  
   public $status; 
}

Applied_by bezieht sich hier auf die Mitarbeiter, die Urlaub wünschen. Leave_on bezieht sich auf das Datum des Urlaubs. Status bezieht sich auf den Urlaubsstatus.

Step 3 - Die Urlaubsverwaltung hat vier Stellen: beantragt, bearbeitet und genehmigt / abgelehnt.

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']);

Hier haben wir eine neue Definition mit erstellt DefinitionBuilder und Orte mit hinzugefügt addPlaces Methode.

Step 4 - Definieren Sie die Aktionen, die erforderlich sind, um von einem Ort an einen anderen zu wechseln.

$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'));

Hier haben wir drei Übergänge, to_process, approve und reject. Der Übergang to_process akzeptiert den Urlaubsantrag und verschiebt den Ort von angewendet auf in_process. Übergang genehmigen genehmigt den Urlaubsantrag und verschiebt den Ort auf genehmigt. In ähnlicher Weise lehnt der Übergang ablehnen den Urlaubsantrag ab und verschiebt den Ort auf abgelehnt. Wir haben alle Übergänge mit der Methode addTransition erstellt.

Step 5 - Erstellen Sie die Definition mit der Erstellungsmethode.

$definition = $builder->build();

Step 6 - Optional kann die Definition als graphviz-Punktformat ausgegeben werden, das zu Referenzzwecken in eine Bilddatei konvertiert werden kann.

$dumper = new GraphvizDumper(); 
echo $dumper->dump($definition);

Step 7 - Erstellen Sie einen Markierungsspeicher, in dem die aktuellen Orte / der aktuelle Status des Objekts gespeichert werden.

$marking = new SingleStateMarkingStore('status');

Hier haben wir verwendet SingleStateMarkingStoreKlasse zum Erstellen der Marke und markiert den aktuellen Status in der Statuseigenschaft des Objekts. In unserem Beispiel ist das Objekt Objekt verlassen.

Step 8 - Erstellen Sie den Workflow mithilfe von Definition und Markierung.

$leaveWorkflow =    new Workflow($definition, $marking);

Hier haben wir verwendet Workflow Klasse zum Erstellen des Workflows.

Step 9 - Fügen Sie den Workflow mithilfe von in die Registrierung des Workflow-Frameworks ein Registry Klasse.

$registry = new Registry(); 
$registry->add($leaveWorkflow, Leave::class);

Step 10 - Verwenden Sie abschließend den Workflow, um herauszufinden, ob ein bestimmter Übergang mit angewendet wird can Methode und wenn ja, applyder Übergang mit der Apply-Methode. Wenn ein Übergang angewendet wird, verschiebt sich der Status des Objekts von einem Ort zum anderen.

$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";

Die vollständige Codierung lautet wie folgt:

<?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";  
?>

Ergebnis

Can we approve the leave now?  
Can we approve the start process now? 1 
Can we approve the leave now? 1 
in_process 
approved