PHP/Tutorials/Einführung in Symfony
Warum ist Symfony besser, als einfach eine Datei zu öffnen und Vanilla-PHP ohne Framework zu schreiben?
Wenn Sie noch nie ein PHP-Framework verwendet haben, mit der Model-View-Controller (MVC)-Philosophie nicht vertraut sind oder sich einfach nur fragen, was der ganze Hype um Symfony ist, ist dieser Artikel für Sie. Überzeugen Sie sich selbst davon, dass Sie mit Symfony schneller und besser Software entwickeln können als mit purem PHP.
In diesem Artikel werden Sie eine einfache Anwendung in purem PHP schreiben und diese dann umgestalten, um sie besser zu organisieren. In einer Zeitreise sehen Sie, warum sich die Webentwicklung in den letzten Jahren zu dem entwickelt hat, was sie heute ist.
Am Ende werden Sie sehen, wie Symfony Sie von alltäglichen Aufgaben befreien und Ihnen die Kontrolle über Ihren Code zurückgeben kann.
Inhaltsverzeichnis
Ein einfaches Blog in purem PHP
In diesem Artikel werden Sie die Token-Blog-Anwendung nur mit PHP erstellen. Zunächst erstellen Sie eine einzelne Seite, die die in der Datenbank persistierten Blog-Einträge anzeigt. Das Schreiben in purem PHP ist schnell und schmutzig:
<?php
// index.php
$connection = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
$result = $connection->query('SELECT id, title FROM post');
?><!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = $result->fetch(PDO::FETCH_ASSOC)): ?>
<li>
<a href="/show.php?id=<?= htmlspecialchars(urlencode($row['id'])) ?>">
<?= htmlspecialchars($row['title']) ?>
</a>
</li>
<?php endwhile ?>
</ul>
</body>
</html>
Das ist schnell zu schreiben, schnell auszuführen und, wenn Ihre Anwendung wächst, unmöglich zu warten. Es gibt mehrere Probleme, die gelöst werden müssen:
- Keine Fehlerprüfung: Was ist, wenn die Verbindung zur Datenbank fehlschlägt?
- Schlechte Organisation: Wenn die Anwendung wächst, wird diese einzelne Datei zunehmend unpflegbar. Wo sollte man den Code für die Bearbeitung einer Formulareingabe einfügen? Wie können Sie Daten validieren? Wohin sollte der Code für das Versenden von E-Mails gehen?
- Schwierig wiederzuverwendender Code: Da sich alles in einer Datei befindet, gibt es keine Möglichkeit, irgendeinen Teil der Anwendung für andere "Seiten" des Blogs wiederzuverwenden.
Isolierung der Darstellung
Ihr Code wird sofort übersichtlicher, wenn die Geschäftslogik der Anwendung von dem Code getrennt wird, der die Darstellung in HTML (Darstellungslogik) vorbereitet:
// index.php
$connection = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
$result = $connection->query('SELECT id, title FROM post');
$posts = [];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$posts[] = $row;
}
// include the HTML presentation code
require 'templates/list.php';
Der HTML-Code wird nun in einer separaten Datei templates/list.php
gespeichert, die in erster Linie eine HTML-Datei ist, die eine template-ähnliche PHP-Syntax verwendet:
<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/show.php?id=<?= htmlspecialchars(urlencode($post['id'])) ?>">
<?= htmlspecialchars($post['title']) ?>
</a>
</li>
<?php endforeach ?>
</ul>
</body>
</html>
Die Datei, die die gesamte Anwendungslogik enthält – index.php
– wird per Konvention als "Controller" bezeichnet. Der Begriff "Controller" ist ein Wort, das Sie oft hören werden, unabhängig von der Programmiersprache oder dem eingesetzten Framework. Er bezieht sich auf den Bereich Ihres Codes, der Benutzereingaben verarbeitet und die Antwort vorbereitet.
In diesem Fall bereitet der Controller Daten aus der Datenbank vor und fügt dann eine Vorlage zur Darstellung dieser Daten hinzu. Wenn der Controller isoliert ist, brauchen Sie nur die Vorlagendatei ändern, wenn Sie die Blogeinträge in einem anderen Format darstellen müssen (z. B. list.json.php
für das JSON-Format).
Isolierung der Anwendungs- (Domänen-) Logik
Bisher enthält die Anwendung nur eine Seite. Was aber, wenn eine zweite Seite die gleiche Datenbankverbindung oder sogar die gleiche Reihe von Blog-Einträgen verwenden müsste? Der Code wird nun so geändert, dass das Kernverhalten und die Datenzugriffsfunktionen der Anwendung in einer neuen Datei namens model.php
isoliert werden:
function open_database_connection()
{
$connection = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
return $connection;
}
function close_database_connection(&$connection)
{
$connection = null;
}
function get_all_posts()
{
$connection = open_database_connection();
$result = $connection->query('SELECT id, title FROM post');
$posts = [];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$posts[] = $row;
}
close_database_connection($connection);
return $posts;
}
Der Dateiname model.php
wird verwendet, weil die Logik und der Datenzugriff einer Anwendung traditionell als "Modell"-Schicht bekannt ist. In einer gut organisierten Anwendung sollte der Großteil des Codes, der Ihre "Geschäftslogik" repräsentiert, im Modell leben (im Gegensatz zum Leben in einem Controller). Und anders als in diesem Beispiel ist nur ein Teil (oder keiner) des Modells tatsächlich mit dem Zugriff auf eine Datenbank befasst.
Der Controller (index.php
) besteht jetzt nur noch aus einigen wenigen Codezeilen:
// index.php
require_once 'model.php';
$posts = get_all_posts();
require 'templates/list.php';
Die einzige Aufgabe des Controllers besteht nun darin, Daten aus der Modellschicht der Anwendung (dem Modell) zu erhalten und eine Vorlage zur Darstellung dieser Daten aufzurufen. Dies ist ein sehr prägnantes Beispiel für das Model-View-Controller-Muster.
Isolierung des Layouts
Bis jetzt wurde die Anwendung in drei verschiedene Teile umgestaltet, die verschiedene Vorteile und die Möglichkeit bieten, fast alles auf verschiedenen Seiten wiederzuverwenden.
Der einzige Teil des Codes, der nicht wiederverwendet werden kann, ist das Seitenlayout. Beheben Sie dies, indem Sie eine neue Datei templates/layout.php
erstellen:
<!-- templates/layout.php -->
<!DOCTYPE html>
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $content ?>
</body>
</html>
Die Vorlage templates/list.php
kann nun vereinfacht werden, um die templates/layout.php
zu "erweitern":
<!-- templates/list.php -->
<?php $title = 'List of Posts' ?>
<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/show.php?id=<?= htmlspecialchars(urlencode($post['id'])) ?>">
<?= htmlspecialchars($post['title']) ?>
</a>
</li>
<?php endforeach ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
Sie haben nun ein Setup, das Ihnen die Wiederverwendung des Layouts ermöglicht. Leider sind Sie dazu gezwungen, einige hässliche PHP-Funktionen (ob_start()
, ob_get_clean())
in der Vorlage zu verwenden. Symfony löst dies mit Hilfe einer Templating-Komponente. Sie werden es in Kürze in Aktion sehen.
Hinzufügen einer Blog-"Show"-Seite
Die Blog-"Liste"-Seite wurde jetzt überarbeitet, sodass der Code besser organisiert und wiederverwendbar ist. Um dies zu beweisen, fügen Sie eine Blog-"Show"-Seite hinzu, die einen individuellen Blog-Beitrag anzeigt, der durch einen id-Abfrageparameter identifiziert wird.
Erstellen Sie zu Beginn eine neue Funktion in der Datei model.php
, die ein individuelles Blog-Ergebnis auf der Grundlage einer gegebenen ID abruft:
// model.php
function get_post_by_id($id)
{
$connection = open_database_connection();
$query = 'SELECT created_at, title, body FROM post WHERE id=:id';
$statement = $connection->prepare($query);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$row = $statement->fetch(PDO::FETCH_ASSOC);
close_database_connection($connection);
return $row;
}
Als nächstes erstellen Sie eine neue Datei namens show.php
– der Controller für diese neue Seite:
// show.php
require_once 'model.php';
$post = get_post_by_id($_GET['id']);
require 'templates/show.php';
Erstellen Sie schließlich die neue Vorlagendatei – templates/show.php
-, um den einzelnen Blog-Post zu rendern:
<!-- templates/show.php -->
<?php $title = $post['title'] ?>
<?php ob_start() ?>
<h1><?= htmlspecialchars($post['title']) ?></h1>
<div class="date"><?= htmlspecialchars($post['created_at']) ?></div>
<div class="body">
<?= htmlspecialchars($post['body']) ?>
</div>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
Die Erstellung der zweiten Seite erfordert nun sehr wenig Arbeit und es wird kein Code dupliziert. Dennoch birgt eine weitere Seite potentielle Probleme, die ein Framework für Sie lösen kann. Zum Beispiel führt ein fehlender oder ungültiger id-Abfrageparameter zum Absturz der Seite. Es wäre besser, wenn dadurch eine 404-Seite gerendert werden würde, aber das ist noch nicht wirklich möglich.
Ein weiteres großes Problem ist, dass jede einzelne Controller-Datei die model.php
-Datei enthalten muss. Was wäre, wenn jede Controller-Datei plötzlich eine zusätzliche Datei aufnehmen oder eine andere globale Aufgabe (z. B. Sicherheit durchsetzen) erfüllen müsste? In der jetzigen Form müsste dieser Code zu jeder Controller-Datei hinzugefügt werden. Wenn Sie vergessen, etwas in eine Datei einzufügen, hat das hoffentlich nichts mit Sicherheit zu tun.
Ein "Front-Controller" als Rettung?
Die Lösung ist die Verwendung eines Front-Controllers: eine einzige PHP-Datei, über die alle Anfragen verarbeitet werden. Mit einem Front-Controller ändern sich die URIs für die Anwendung leicht, werden aber allmählich flexibler:
Without a front controller
/index.php => Blog post list page (index.php executed)
/show.php => Blog post show page (show.php executed)
With index.php as the front controller
/index.php => Blog post list page (index.php executed)
/index.php/show => Blog post show page (index.php executed)
Durch die Verwendung von Rewrite-Regeln in Ihrer Webserver-Konfiguration wird die index.php
nicht benötigt und Sie erhalten schöne, saubere URLs (z. B. /show
).
Wenn Sie einen Front-Controller verwenden, wird jede Anfrage von einer einzigen PHP-Datei (in diesem Fall index.php
) gerendert. Für die Blog-Post-Show-Seite wird /index.php/show
tatsächlich die Datei index.php
ausführen, die nun für die interne Weiterleitung von Anfragen auf der Grundlage der vollständigen URI verantwortlich ist. Wie Sie sehen werden, ist ein Front-Controller ein sehr leistungsfähiges Werkzeug.
Erstellen des Front-Controllers
Sie sind dabei, mit der Anwendung einen großen Schritt zu machen. Mit einer einzigen Datei, die alle Anfragen bearbeitet, können Sie Dinge wie Sicherheitshandling, Laden der Konfiguration und Routing zentralisieren. In dieser Anwendung muss index.php
nun intelligent genug sein, um die Seite der Blog-Post-Liste oder die Seite der Blog-Post-Anzeige auf der Grundlage der angeforderten URI zu rendern:
// index.php
// load and initialize any global libraries
require_once 'model.php';
require_once 'controllers.php';
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ('/index.php' === $uri) {
list_action();
} elseif ('/index.php/show' === $uri && isset($_GET['id'])) {
show_action($_GET['id']);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
Aus organisatorischen Gründen sind beide Controller (früher /index.php und /index.php/show) jetzt PHP-Funktionen, und jeder wurde in eine separate Datei namens controllers.php verschoben:
// controllers.php
function list_action()
{
$posts = get_all_posts();
require 'templates/list.php';
}
function show_action($id)
{
$post = get_post_by_id($id);
require 'templates/show.php';
}
Als Front-Controller hat index.php eine völlig neue Rolle übernommen, eine, die das Laden der Kernbibliotheken und das Routing der Anwendung umfasst, sodass einer der beiden Controller (die Funktionen list_action()
und show_action())
aufgerufen wird. In Wirklichkeit sieht der Front-Controller allmählich so aus und verhält sich ähnlich wie Symfony, wie es Anfragen behandelt und weiterleitet.
Aber achten Sie darauf, die Begriffe Front-Controller und Controller nicht zu verwechseln. Ihre Anwendung wird normalerweise nur einen Front-Controller haben, der Ihren Code startet. Sie werden viele Controller-Funktionen haben: eine für jede Seite.
/show
zu /read
geändert werden kann, indem der Code nur an einer Stelle geändert wird. Vorher musste eine ganze Datei umbenannt werden. In Symfony sind die URLs noch flexibler.Inzwischen hat sich die Anwendung von einer einzigen PHP-Datei zu einer Struktur entwickelt, die organisiert ist und die Wiederverwendung von Code ermöglicht. Sie sollten zufriedener sein, aber bei weitem nicht zufrieden. Zum Beispiel ist das Routing-System wankelmütig und würde nicht erkennen, dass die Listenseite – /index.php
– auch über /
zugänglich sein sollte (wenn Apache-Rewrite-Regeln hinzugefügt wurden). Außerdem wird statt der Entwicklung des Blogs viel Zeit mit der "Architektur" des Codes verbracht (z. B. Routing, Aufruf von Controllern, Vorlagen usw.). Es muss mehr Zeit für die Bearbeitung von Formularen, die Eingabevalidierung, die Protokollierung und die Sicherheit aufgewendet werden. Warum sollten Sie Lösungen für all diese Routineprobleme neu erfinden müssen?
Einen Hauch von Symfony hinzufügen
Symfony zur Rettung. Bevor Sie Symfony tatsächlich benutzen können, müssen Sie es herunterladen. Dies kann mit Hilfe des Composers geschehen, der sich um das Herunterladen der richtigen Version und all ihrer Abhängigkeiten kümmert und einen Autoloader zur Verfügung stellt. Ein Autoloader ist ein Werkzeug, das es ermöglicht, mit der Verwendung von PHP-Klassen zu beginnen, ohne die Datei, die die Klasse enthält, explizit mit einzubeziehen.
Erstellen Sie in Ihrem Stammverzeichnis eine Datei composer.json
mit folgendem Inhalt:
{
"require": {
"symfony/http-foundation": "^4.0"
},
"autoload": {
"files": ["model.php","controllers.php"]
}
}
Laden Sie als nächstes Composer herunter und führen Sie dann den folgenden Befehl aus, der Symfony in ein vendor/Verzeichnis herunterlädt:
composer install
Neben dem Herunterladen Ihrer Abhängigkeiten generiert der Composer eine Datei vendor/autoload.php
, die das automatische Laden aller Dateien im Symfony Framework sowie der im Abschnitt autoload
Ihres composer.json
erwähnten Dateien übernimmt.
Der Kern der Symfony-Philosophie ist die Idee, dass die Hauptaufgabe einer Anwendung darin besteht, jede Anfrage zu interpretieren und eine Antwort zurückzugeben. Zu diesem Zweck stellt Symfony sowohl eine Anforderungs- als auch eine Antwortklasse zur Verfügung. Diese Klassen sind objektorientierte Darstellungen der rohen HTTP-Anfrage, die verarbeitet wird, und der HTTP-Antwort, die zurückgegeben wird. Verwenden Sie sie zur Verbesserung des Blogs:
// index.php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
if ('/' === $uri) {
$response = list_action();
} elseif ('/show' === $uri && $request->query->has('id')) {
$response = show_action($request->query->get('id'));
} else {
$html = '<html><body><h1>Page Not Found</h1></body></html>';
$response = new Response($html, Response::HTTP_NOT_FOUND);
}
// echo the headers and send the response
$response->send();
Die Controller sind nun für die Rückgabe eines Response-Objekts verantwortlich. Um dies zu erleichtern, können Sie eine neue render_template()
-Funktion hinzufügen, die sich übrigens ähnlich wie die Symfony-Templating-Engine verhält:
// controllers.php
use Symfony\Component\HttpFoundation\Response;
function list_action()
{
$posts = get_all_posts();
$html = render_template('templates/list.php', ['posts' => $posts]);
return new Response($html);
}
function show_action($id)
{
$post = get_post_by_id($id);
$html = render_template('templates/show.php', ['post' => $post]);
return new Response($html);
}
// helper function to render templates
function render_template($path, array $args)
{
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}
Durch die Einbringung eines kleinen Teils von Symfony ist die Anwendung flexibler und zuverlässiger. Die Anfrage bietet eine zuverlässige Möglichkeit, auf Informationen über die HTTP-Anfrage zuzugreifen. Insbesondere gibt die Methode getPathInfo()
eine bereinigte URI zurück (immer /show
und nie /index.php/show
). Selbst wenn der Benutzer also nach /index.php/show
geht, ist die Anwendung intelligent genug, um die Anfrage durch show_action()
zu leiten.
Das Response-Objekt bietet Flexibilität bei der Erstellung der HTTP-Antwort und ermöglicht das Hinzufügen von HTTP-Headern und -Inhalten über eine objektorientierte Schnittstelle. Und obwohl die Antworten in dieser Anwendung einfach sind, wird sich diese Flexibilität mit dem Wachstum Ihrer Anwendung auszahlen.
Die Beispielanwendung in Symfony
Das Blog hat einen langen Weg zurückgelegt, aber er enthält immer noch eine Menge Code für eine so einfache Anwendung. Auf dem Weg dorthin haben Sie ein einfaches Routing-System und eine Methode mit ob_start()
und ob_get_clean()
zum Rendern von Vorlagen erstellt. Wenn Sie aus irgendeinem Grund dieses "Framework" von Grund auf neu aufbauen müssten, könnten Sie zumindest die eigenständige Routing-Komponente von Symfony und Twig verwenden, die diese Probleme bereits lösen.
Anstatt häufige Probleme neu zu lösen, können Sie diese von Symfony für Sie erledigen lassen. Hier ist die gleiche Beispielanwendung, die jetzt in Symfony eingebaut ist:
// src/Controller/BlogController.php
namespace App\Controller;
use App\Entity\Post;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class BlogController extends AbstractController
{
public function list()
{
$posts = $this->getDoctrine()
->getRepository(Post::class)
->findAll();
return $this->render('blog/list.html.twig', ['posts' => $posts]);
}
public function show($id)
{
$post = $this->getDoctrine()
->getRepository(Post::class)
->find($id);
if (!$post) {
// cause the 404 page not found to be displayed
throw $this->createNotFoundException();
}
return $this->render('blog/show.html.twig', ['post' => $post]);
}
}
Beachten Sie, dass beide Controller-Funktionen jetzt innerhalb einer "Controller-Klasse" leben. Dies ist eine nette Möglichkeit, verwandte Seiten zu gruppieren. Die Controller-Funktionen werden manchmal auch als Aktionen bezeichnet.
Die beiden Controller (oder Actions) sind immer noch leichtgewichtig. Jeder verwendet die Doctrine ORM-Bibliothek, um Objekte aus der Datenbank abzurufen, und die Templating-Komponente, um ein Template zu rendern und ein Response-Objekt zurückzugeben. Die Vorlage list.html.twig
ist jetzt etwas einfacher und verwendet Twig:
<!-- templates/blog/list.html.twig -->
{% extends 'base.html.twig' %}
{% block title %}List of Posts{% endblock %}
{% block body %}
<h1>List of Posts</h1>
<ul>
{% for post in posts %}
<li>
<a href="{{ path('blog_show', { id: post.id }) }}">
{{ post.title }}
</a>
</li>
{% endfor %}
</ul>
{% endblock %}
Die layout.php-Datei is nahezu identisch:
<!-- templates/base.html.twig -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
show.html.twig
wird als Aufgabe belassen: Die Aktualisierung sollte wirklich ähnlich wie die Aktualisierung der Vorlage list.html.twig
sein.Wenn die Symfony-Engine (genannt der Kernel) hochfährt, benötigt sie eine Map, damit sie weiß, welche Controller sie aufgrund der Anforderungsinformationen ausführen soll. Eine Routing-Konfigurationsmap – config/routes.yaml
– stellt diese Informationen in einem lesbaren Format zur Verfügung:
# config/routes.yaml
blog_list:
path: /blog
controller: App\Controller\BlogController::list
blog_show:
path: /blog/show/{id}
controller: App\Controller\BlogController::show
Jetzt, wo Symfony alle alltäglichen Aufgaben erledigt, wird der Frontcontroller public/index.php
auf das Bootstrapping reduziert. Und da er so wenig tut, müssen Sie ihn nie wieder anfassen:
// public/index.php
require_once __DIR__.'/../app/bootstrap.php';
require_once __DIR__.'/../src/Kernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new Kernel('prod', false);
$kernel->handle(Request::createFromGlobals())->send();
Die einzige Aufgabe des Front-Controllers ist es, die Symfony-Engine (den sogenannten Kernel) zu initialisieren und ihr ein Request-Objekt zu übergeben. Der Symfony-Kern bittet den Router, die Anfrage zu prüfen. Der Router ordnet die eingehende URL einer bestimmten Route zu und gibt Informationen über die Route zurück, einschließlich des Controllers, der ausgeführt werden soll. Der richtige Controller aus der abgeglichenen Route wird ausgeführt, und Ihr Code innerhalb des Controllers erstellt und gibt das entsprechende Response-Objekt zurück. Die HTTP-Header und der Inhalt des Response-Objekts werden an den Client zurückgeschickt.
Das ist eine schöne Sache.
Fazit
In den restlichen Artikeln der Dokumentation erfahren Sie mehr darüber, wie jedes einzelne Stück von Symfony funktioniert und wie Sie Ihr Projekt organisieren können. Feiern Sie vorerst, wie die Migration des Blogs von flachem PHP zu Symfony Ihr Leben verbessert hat:
- Ihre Anwendung hat jetzt einen klaren und konsistent organisierten Code (obwohl Symfony Sie nicht dazu zwingt). Dies fördert die Wiederverwendbarkeit und ermöglicht es neuen Entwicklern, in Ihrem Projekt schneller produktiv zu sein;
- 100% des von Ihnen geschriebenen Codes ist für Ihre Anwendung bestimmt. Sie müssen keine Low-Level-Utilities wie Autoloading, Routing oder Rendering-Controller entwickeln oder pflegen;
- Symfony gibt Ihnen Zugang zu Open-Source-Tools wie Doctrine und den Komponenten Templating, Security, Form, Validator und Translation (um nur einige zu nennen);
- Dank der Routing-Komponente verfügt die Anwendung nun über voll flexible URLs;
- Die HTTP-zentrierte Architektur von Symfony ermöglicht Ihnen den Zugriff auf leistungsstarke Tools wie den HTTP-Cache, der durch den internen HTTP-Cache von Symfony bereitgestellt wird, oder leistungsfähigere Tools wie Varnish. Dies wird in einem anderen Artikel über das Caching behandelt.
Und vielleicht das Beste von allem: Durch die Verwendung von Symfony haben Sie jetzt Zugang zu einer ganzen Reihe von hochwertigen Open-Source-Tools, die von der Symfony-Community entwickelt wurden! Eine gute Auswahl von Symfony-Community-Tools finden Sie auf GitHub.
Quellen
Dieses Werk, einschließlich der Code-Beispiele, ist unter einer Creative-Commons-Lizenz BY-SA 3.0 lizenziert und wurde in der englischen Version unter https://symfony.com/doc/current/introduction/from_flat_php_to_symfony.html veröffentlicht.