In diesem Artikel werden wir versuchen, eine Komponente namens Config zu behandeln, mit deren Hilfe verschiedene Daten unabhängig von der Quelle geladen und verarbeitet werden können.
Das Folgende ist eine Übersetzung des Artikels Übersicht über die Symfony2-Komponenten: Config . Das Original wurde 2014 veröffentlicht und zeigt die zweite Version von Symfony an. Die Informationen sind jedoch für die aktuelle, derzeit vierte Version relevant.
Stellen wir uns vor, wir möchten einen Blog-Generator erstellen, der verschiedene Parameter wie Titel ( Titel ), Beschreibung ( Beschreibung ), Anzahl der Beiträge auf der Hauptseite ( posts_main_page ), Symbole für soziale Medien ( sozial ) und das Vorhandensein oder Fehlen von RSS
Feeds ( rss ) verwendet. . Zu diesem Zweck beschreiben wir die Konfigurationsdatei im YAML
Format:
blog: title: description: ... rss: true posts_main_page: 2 social: twitter: url: http://twitter.com/raulfraile icon: twitter.png sensiolabs_connect: url: https://connect.sensiolabs.com/profile/raulfraile icon: sensiolabs_connect.png
Jetzt werden wir versuchen, unsere Datei zu analysieren, die Verfügbarkeit der erforderlichen Felder zu überprüfen und gegebenenfalls Standardwerte festzulegen. Wir überprüfen alle erhaltenen Daten auf Übereinstimmung mit den festgelegten Regeln. Beispielsweise kann rss nur einen Booleschen Wert enthalten, und posts_main_page sollte einen ganzzahligen Wert im Bereich von 1 bis 10 enthalten. Diese Prozeduren müssen bei jedem Zugriff auf die Datei wiederholt werden, es sei denn, das Caching-System wird verwendet . Darüber hinaus erschwert ein solcher Mechanismus die Verwendung von Dateien anderer Formate wie INI
, XML
oder JSON
.
Um die obigen Aktionen zu vereinfachen, verwenden wir die Config-Komponente . Die Komponente ist einfach, gut getestet und flexibel genug für den Einsatz in verschiedenen Projekten.
Architektur
Wir werden das Projekt in zwei Hauptteile aufteilen:
- Bestimmen der hierarchischen Struktur von Parametern.
Mit der Komponente können Sie das Format der Konfigurationsquelle bestimmen, das von einer einfachen INI
Datei bis zu etwas Exotischerem reichen kann. Die TreeBuilder- Klasse hilft dabei, die Parametertypen zu bestimmen, sie obligatorisch / optional zu machen und den Standardwert festzulegen. - Erkennung, Verladung und Verarbeitung.
Nachdem das Quellformat angegeben wurde, muss es gefunden, geladen und verarbeitet werden. Schließlich gibt die Komponente ein einfaches Array mit aktivierten Werten zurück oder löst bei einem Fehler eine Ausnahme aus.
Beispiel
Kehren wir zu unserem Beispiel zurück. Deshalb möchten wir ein flexibles Blog-Generierungssystem schaffen. Zunächst definieren wir eine hierarchische Struktur (Baum), und eine Instanz der TreeBuilder- Klasse hilft uns dabei, die eine DSL
- syntaxähnliche Schnittstelle bereitstellt.
<?php namespace RaulFraile\Config; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('blog'); $rootNode ->children() ->scalarNode('title') ->isRequired() ->end() ->scalarNode('description') ->defaultValue('') ->end() ->booleanNode('rss') ->defaultValue(false) ->end() ->integerNode('posts_main_page') ->min(1) ->max(10) ->defaultValue(5) ->end() ->arrayNode('social') ->arrayPrototype() ->children() ->scalarNode('url')->end() ->scalarNode('icon')->end() ->end() ->end() ->end() ->end() ; return $treeBuilder; } }
Machen Sie sich keine Sorgen, wenn Sie zum ersten Mal eine ähnliche Struktur von PHP-Code sehen. Die darin enthaltene DSL
Syntax sieht immer etwas seltsam aus. Im obigen Beispiel haben wir den Stammknoten des Blogs bestimmt und daraus die Struktur des Konfigurationsbaums erstellt, dessen Zweige die von uns benötigten Parameter und die Regeln für ihre Werte sind. Beispielsweise wird title als obligatorischer Parameter eines Skalartyps angegeben , description als optionaler Parameter, der standardmäßig leer ist. In rss erwarten wir einen booleschen Wert, der standardmäßig false
ist, und posts_main_page sollte einen ganzzahligen Wert im Bereich von 1 bis 10 enthalten, wobei 5 der Standardwert ist .
Nun, wir haben die Struktur definiert. Kommen wir nun zum Laden und Verarbeiten. Je nach Bedingung kann die Quelle eine beliebige sein. Zunächst müssen wir sie in ein reguläres Array konvertieren, um alle Werte mithilfe unserer Konfigurationsstruktur weiter zu überprüfen und zu verarbeiten. Für jedes Quellformat benötigen wir eine separate Klasse. Wenn wir also die Dateiformate YAML
und XML
verwenden möchten, müssen wir zwei Klassen erstellen. Im folgenden Beispiel wird der Einfachheit halber nur die YAML
Formatklasse dargestellt:
<?php namespace RaulFraile\Config; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Yaml\Yaml; class YamlConfigLoader extends FileLoader { public function load($resource, $type = null) { $configValues = Yaml::parse(file_get_contents($resource)); return $configValues; } public function supports($resource, $type = null) { return is_string($resource) && 'yml' === pathinfo( $resource, PATHINFO_EXTENSION ); } }
Wie Sie sehen können, ist alles sehr einfach. Die YamlConfigLoader::supports
Methode wird von der LoaderResolver
Klasse verwendet, um die Konfigurationsquelle zu überprüfen. Die YamlConfigLoader::load
Methode konvertiert eine YAML
Datei mithilfe einer anderen Symfony YAML-Komponente in ein YamlConfigLoader::load
.
Abschließend kombinieren wir die Konfigurationsstruktur und den Loader bei der Verarbeitung der Quelle, um die erforderlichen Werte zu erhalten:
<?php use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Definition\Processor; use RaulFraile\Config\YamlConfigLoader; use RaulFraile\Config\Configuration; include_once __DIR__. '/vendor/autoload.php';
Lassen Sie uns diesen Code analysieren. Zunächst definieren wir ein Array von Verzeichnissen, in denen sich Konfigurationsdateien befinden, und FileLocator
es als Parameter in das FileLocator
Objekt ein, das im angegebenen Verzeichnis nach der Datei FileLocator
sucht. Erstellen Sie dann ein YamlConfigLoader
Objekt, das ein Array mit Werten zurückgibt und das bereits von unserer Konfigurationsstruktur verarbeitet wird.
Als Ergebnis erhalten wir das folgende Array:
array(5) { 'title' => string(7) "My blog" 'description' => string(24) "This is just a test blog" 'rss' => bool(true) 'posts_main_page' => int(2) 'social' => array(2) { 'twitter' => array(2) { 'url' => string(29) "http://twitter.com/raulfraile" 'icon' => string(11) "twitter.png" } 'sensiolabs_connect' => array(2) { 'url' => string(49) "https://connect.sensiolabs.com/profile/raulfraile" 'icon' => string(22) "sensiolabs_connect.png" } } }
Wenn wir versuchen, config.yml
Löschen der Felder rss und post_main_page zu ändern, erhalten wir die Standardwerte:
array(5) { ... 'rss' => bool(false) 'posts_main_page' => int(5)
Caching
Das Verarbeiten großer Konfigurationsdateien kann eine zeitaufwändige Aufgabe sein. Die beschriebene Komponente verfügt über einen einfachen Caching-Mechanismus, der auf der Überprüfung des Änderungsdatums der Konfigurationsdatei basiert.
Um den Cache zu aktivieren, reichen einige Codezeilen aus:
<?php use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Definition\Processor; use RaulFraile\Config\YamlConfigLoader; use RaulFraile\Config\Configuration; include_once __DIR__. '/vendor/autoload.php'; $cachePath = __DIR__.'/cache/config.php'; $configFile = 'config.yml';
Eine Instanz der ConfigCache
Klasse überprüft das Vorhandensein des ConfigCache
und vergleicht, falls verfügbar, das Datum, an dem die Konfigurationsdatei geändert wurde. Wenn wir einen Dateicache erstellen, speichern wir auch eine Liste der verwendeten Objekte zum weiteren Vergleich.
Mehrfaches Laden
Um ein weiteres Konfigurationsformat hinzuzufügen, reicht es aus, eine Klasse zu definieren, die für ein bestimmtes Format verantwortlich ist. Im folgenden Beispiel haben wir die Unterstützung für die XML
Konfiguration und einen entsprechenden Handler hinzugefügt. Die LoaderResolver
Klasse hilft uns, verschiedene Formate in einem gemeinsamen Pool zu kombinieren, und die DelegatingLoader
Klasse lädt die erforderliche Datei auf Anfrage.
<?php namespace RaulFraile\Config; use Symfony\Component\Config\Loader\FileLoader; class XmlConfigLoader extends FileLoader { public function load($resource, $type = null) {
$loaderResolver = new LoaderResolver(array( new YamlConfigLoader($locator), new XmlConfigLoader($locator) )); $delegatingLoader = new DelegatingLoader($loaderResolver); $configValues = $delegatingLoader->load($locator->locate('config.xml'));
Die Komponente verfügt unter anderem über die Funktionalität, Referenzinformationen für Ihre Dokumentation zu generieren.
<?php ... use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; $dumper = new YamlReferenceDumper(); echo $dumper->dump($configuration);
Es wird ausgegeben:
blog: title: ~ # Required description: '' rss: false posts_main_page: 5 social: url: ~ icon: ~
Zusammenfassung
Vielleicht werden Sie feststellen, dass dies alles zu kompliziert und verwirrend ist und Sie mit ein paar Funktionen arbeiten können. Vielleicht, aber das ist der „Preis“ für eine gute OOP-Struktur. Andererseits bietet diese Komponente mehrere Vorteile:
- ~ 80% Abdeckung der Komponente mit Tests und aktiver Unterstützung.
- Das Hinzufügen neuer Konfigurationsformate ist sehr einfach. Es reicht aus, einen Handler zu definieren, der die Quelldaten in ein reguläres Array konvertiert. Eine ähnliche Struktur wird für andere Formate verwendet. Die Erweiterung eines Teils der Komponente wird durch Hinzufügen der erforderlichen Schnittstelle implementiert.
- Das Caching funktioniert sofort mit flexiblen Einstellungen für
dev
und prod
. - Eingebaute Überprüfung der Parameter und ihrer Werte.
- Generierung von Hintergrundinformationen.