Pada artikel ini, kami akan mencoba menangani komponen yang disebut Config , yang membantu memuat dan memproses berbagai data terlepas dari sumbernya.
Berikut ini adalah terjemahan dari artikel Gambaran umum komponen Symfony2: Konfigurasi . Dokumen asli diterbitkan pada 2014 dan menunjukkan versi kedua Symfony, tetapi informasinya relevan untuk yang terbaru, saat ini, versi keempat.
Mari kita bayangkan bahwa kita ingin membuat generator blog yang akan mengambil beberapa parameter seperti judul ( judul ), deskripsi ( deskripsi ), jumlah posting di halaman utama ( posts_main_page ), ikon media sosial ( sosial ) dan ada atau tidaknya RSS
feed ( rss ) . Untuk tujuan ini, kami akan menjelaskan file konfigurasi dalam format YAML
:
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
Sekarang kita akan mencoba mem-parsing file kita, memeriksa ketersediaan bidang yang diperlukan, dan mengatur nilai default jika perlu. Kami akan memeriksa semua data yang diperoleh untuk kepatuhan dengan aturan yang ditetapkan, misalnya, rss hanya dapat berisi nilai Boolean, dan posts_main_page harus berisi nilai integer dalam rentang 1 hingga 10. Kami harus mengulangi prosedur ini setiap kali file diakses, kecuali tentu saja sistem caching digunakan . Selain itu, mekanisme seperti itu mempersulit penggunaan file format lain seperti INI
, XML
atau JSON
.
Untuk menyederhanakan tindakan di atas, kami akan menggunakan komponen Config . Komponen ini sederhana, teruji dengan baik dan cukup fleksibel untuk digunakan dalam berbagai proyek.
Arsitektur
Kami akan membagi proyek menjadi dua bagian utama:
- Definisi struktur hirarki parameter.
Komponen ini memungkinkan Anda untuk menentukan format sumber konfigurasi, yang bisa berupa apa saja dari file INI
sederhana hingga sesuatu yang lebih eksotis. Kelas TreeBuilder akan membantu menentukan jenis parameter, menjadikannya wajib / opsional dan menetapkan nilai default. - Deteksi, pemuatan, dan pemrosesan.
Setelah format sumber ditentukan, itu harus ditemukan, dimuat dan diproses. Akhirnya, komponen akan mengembalikan array sederhana dengan nilai yang dicentang atau melemparkan pengecualian kesalahan.
Contoh
Mari kita kembali ke contoh kita. Jadi, kami ingin membuat sistem pembuatan blog yang fleksibel. Pertama, kita mendefinisikan struktur hierarki (pohon), dan sebuah instance dari kelas TreeBuilder akan membantu kita dengan ini, yang menyediakan antarmuka seperti sintaksis DSL
.
<?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; } }
Jangan khawatir jika Anda melihat struktur kode PHP yang serupa untuk pertama kalinya, sintaks DSL
di dalamnya selalu terlihat sedikit aneh. Pada contoh di atas, kami menentukan simpul akar blog dan membangun struktur pohon konfigurasi darinya, cabang-cabangnya adalah parameter yang kami butuhkan dan aturan untuk nilainya. Sebagai contoh, judul ditunjukkan sebagai parameter wajib dari jenis skalar, deskripsi sebagai parameter opsional, yang kosong secara default, di rss kami mengharapkan nilai boolean yang false
secara default, dan posts_main_page harus berisi nilai integer dalam rentang dari 1 hingga 10, dengan 5 sebagai default. .
Kami mendefinisikan struktur, sekarang mari kita mulai memuat dan memproses. Dengan syarat, sumbernya bisa berupa apa saja, untuk permulaan kita perlu mengubahnya menjadi array biasa untuk memeriksa lebih lanjut dan memproses semua nilai menggunakan struktur konfigurasi kita. Untuk setiap format sumber, kita memerlukan kelas yang terpisah, jadi jika kita akan menggunakan format file YAML
dan XML
, kita perlu membuat dua kelas. Dalam contoh di bawah ini, untuk kesederhanaan, hanya kelas format YAML
yang disajikan:
<?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 ); } }
Seperti yang Anda lihat, semuanya sangat sederhana. Metode YamlConfigLoader::supports
digunakan oleh kelas LoaderResolver
untuk memverifikasi sumber konfigurasi. Metode YamlConfigLoader::load
mengubah file YAML
ke array data menggunakan komponen Symfony YAML lainnya .
Sebagai kesimpulan, kami menggabungkan struktur konfigurasi dan loader saat memproses sumber untuk mendapatkan nilai yang diperlukan:
<?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';
Mari kita menganalisis kode ini. Pertama, kita mendefinisikan sebuah array direktori di mana file konfigurasi dapat ditemukan, dan meletakkannya sebagai parameter dalam objek FileLocator
yang mencari file config.yml
di direktori yang ditentukan. Lalu, buat objek YamlConfigLoader
yang mengembalikan array dengan nilai, dan itu sudah diproses oleh struktur konfigurasi kami.
Hasilnya, kami mendapatkan array berikut:
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" } } }
Jika kita mencoba mengubah config.yml
menghapus bidang rss dan post_main_page , kita akan mendapatkan nilai default:
array(5) { ... 'rss' => bool(false) 'posts_main_page' => int(5)
Caching
Memproses file konfigurasi besar dapat menjadi tugas yang menghabiskan waktu. Komponen yang dijelaskan memiliki mekanisme caching sederhana berdasarkan memeriksa tanggal perubahan file konfigurasi.
Untuk mengaktifkan cache, beberapa baris kode sudah cukup:
<?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';
Sebuah instance dari kelas ConfigCache
memeriksa keberadaan cache file dan, jika tersedia, membandingkan tanggal file konfigurasi diubah. Ketika kami membuat cache file, kami juga menyimpan daftar objek yang digunakan untuk perbandingan lebih lanjut.
Pemuatan berganda
Untuk menambahkan format konfigurasi lain, cukup menentukan kelas yang akan bertanggung jawab untuk format tertentu. Pada contoh di bawah ini, kami menambahkan dukungan untuk konfigurasi XML
dan penangan yang sesuai. Kelas LoaderResolver
akan membantu kami menggabungkan berbagai format ke dalam kumpulan umum, dan kelas DelegatingLoader
akan memuat file yang diperlukan berdasarkan permintaan.
<?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'));
Antara lain, komponen memiliki fungsi untuk menghasilkan informasi referensi untuk dokumentasi Anda.
<?php ... use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; $dumper = new YamlReferenceDumper(); echo $dumper->dump($configuration);
Ini akan menampilkan:
blog: title: ~ # Required description: '' rss: false posts_main_page: 5 social: url: ~ icon: ~
Ringkasan
Mungkin Anda akan menemukan bahwa semua ini terlalu rumit dan membingungkan, dan Anda dapat melakukannya dengan beberapa fungsi. Mungkin, tetapi itu adalah "harga" untuk struktur OOP yang baik. Di sisi lain, komponen ini menawarkan beberapa keunggulan:
- ~ Cakupan 80% komponen dengan tes dan dukungan aktif.
- Menambahkan format konfigurasi baru sangat mudah. Cukup mendefinisikan handler yang mengubah data sumber menjadi array biasa. Struktur serupa akan digunakan untuk format lain. Perpanjangan bagian mana pun dari komponen diimplementasikan dengan menambahkan antarmuka yang diperlukan.
- Caching bekerja di luar kotak dengan pengaturan fleksibel untuk lingkungan
dev
dan prod
. - Verifikasi built-in parameter dan nilainya.
- Latar belakang pembuatan informasi.