في هذه المقالة ، سنحاول التعامل مع مكون يسمى التكوين ، والذي يساعد على تحميل ومعالجة البيانات المختلفة بغض النظر عن المصدر.
فيما يلي ترجمة للمقالة نظرة عامة على مكونات Symfony2: التكوين . تم نشر الأصل في عام 2014 ويشير إلى الإصدار الثاني من Symfony ، ولكن المعلومات ذات صلة بالإصدار الرابع الأحدث ، في الوقت الحالي.
دعنا نتخيل أننا نريد إنشاء منشئ مدونة يأخذ العديد من المعلمات مثل العنوان ( العنوان ) والوصف ( الوصف ) وعدد المنشورات على الصفحة الرئيسية ( posts_main_page ) ورموز الوسائط الاجتماعية ( الاجتماعية ) ووجود أو عدم وجود موجزات RSS
( rss ) . لهذه الأغراض ، YAML
ملف التكوين بتنسيق 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
الآن سنحاول تحليل ملفنا ، والتحقق من توافر الحقول المطلوبة ، وتعيين القيم الافتراضية إذا لزم الأمر. سوف نتحقق من جميع البيانات التي تم الحصول عليها للتوافق مع القواعد المعمول بها ، على سبيل المثال ، يمكن أن تحتوي rss على قيمة منطقية فقط ، ويجب أن تحتوي posts_main_page على قيمة عددية في النطاق من 1 إلى 10. سيتعين علينا تكرار هذه الإجراءات في كل مرة يتم فيها الوصول إلى الملف ، ما لم يتم بالطبع استخدام نظام التخزين المؤقت . بالإضافة إلى ذلك ، فإن هذه الآلية تعقد استخدام ملفات تنسيقات أخرى مثل INI
أو XML
أو JSON
.
لتبسيط الإجراءات المذكورة أعلاه ، سوف نستخدم مكون التكوين . المكون بسيط ومُختبر جيدًا ومرن بما يكفي للاستخدام في مشاريع مختلفة.
العمارة
سنقسم المشروع إلى قسمين رئيسيين:
- تحديد الهيكل الهرمي للمعلمات.
يسمح لك المكون بتحديد تنسيق مصدر التكوين ، والذي يمكن أن يكون أي شيء من ملف INI
بسيط إلى شيء أكثر غرابة. ستساعد فئة TreeBuilder في تحديد أنواع المعلمات ، وجعلها إلزامية / اختيارية وتعيين القيمة الافتراضية. - الكشف والتحميل والمعالجة.
بعد تحديد تنسيق المصدر ، يجب العثور عليه وتحميله ومعالجته. أخيرًا ، سيُرجع المكون صفيفًا بسيطًا بقيم محددة أو يرمي استثناءً على الخطأ.
مثال
دعنا نعود إلى مثالنا. وهكذا ، نريد إنشاء نظام مرن لإنشاء المدونات. أولاً ، نحدد هيكلًا هرميًا (شجرة) ، وسيساعدنا مثيل لفئة TreeBuilder في ذلك ، مما يوفر واجهة تشبه بنية 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; } }
لا تقلق إذا رأيت بنية مشابهة لرمز PHP للمرة الأولى ، فإن بنية DSL
تبدو دائمًا غريبة بعض الشيء. في المثال أعلاه ، حددنا العقدة الجذرية للمدونة وقمنا ببناء هيكل شجرة التكوين منها ، وفروعها هي المعلمات التي نحتاجها وقواعد قيمها. على سبيل المثال ، يشار إلى العنوان كمعلمة إلزامية من النوع القياسي ، ووصف كمعلمة اختيارية فارغة افتراضيًا ، في rss نتوقع قيمة منطقية false
افتراضيًا ، وينبغي أن تحتوي posts_main_page على قيمة عددية في النطاق من 1 إلى 10 ، مع 5 هي القيمة الافتراضية .
حسنًا ، لقد حددنا البنية ، فلننتقل الآن إلى التحميل والمعالجة. حسب الشرط ، يمكن أن يكون المصدر أيًا ، وبدايةً نحتاج إلى تحويله إلى صفيف عادي من أجل التحقق من جميع القيم ومعالجتها باستخدام هيكل التكوين الخاص بنا. لكل تنسيق مصدر ، نحتاج إلى فصل منفصل ، لذلك إذا كنا سنستخدم تنسيقات ملفات YAML
و XML
، فنحن بحاجة إلى إنشاء فئتين. يظهر المثال أدناه للبساطة فقط فئة تنسيق YAML
:
<?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 ); } }
كما ترى ، كل شيء بسيط للغاية. يتم استخدام الأسلوب YamlConfigLoader::supports
بواسطة فئة LoaderResolver
للتحقق من مصدر التكوين. تحول طريقة YamlConfigLoader::load
ملف YAML
إلى صفيف بيانات باستخدام مكون Symfony YAML آخر.
في الختام ، نقوم بدمج هيكل التكوين والمحمل عند معالجة المصدر للحصول على القيم اللازمة:
<?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';
دعونا نحلل هذا الرمز. أولاً ، نحدد مجموعة من الأدلة حيث يمكن تحديد موقع ملفات التكوين ، ونضعها كمعلمة في كائن FileLocator
الذي يبحث عن ملف config.yml
في الدليل المحدد. بعد ذلك ، قم بإنشاء كائن YamlConfigLoader
بإرجاع صفيف بقيم ، وتتم معالجته بالفعل من خلال بنية التكوين الخاصة بنا.
ونتيجة لذلك ، نحصل على الصفيف التالي:
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" } } }
إذا حاولنا تغيير config.yml
حذف config.yml
rss و post_main_page ، فسوف نحصل على القيم الافتراضية:
array(5) { ... 'rss' => bool(false) 'posts_main_page' => int(5)
التخزين المؤقت
يمكن أن تكون معالجة ملفات التكوين الكبيرة مهمة تستغرق وقتًا طويلاً. يحتوي المكون الموصوف على آلية تخزين مؤقت بسيطة تعتمد على التحقق من تاريخ تغيير ملف التكوين.
لتمكين ذاكرة التخزين المؤقت ، يكفي بضعة أسطر من التعليمات البرمجية:
<?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';
يتحقق مثيل لفئة ConfigCache
من وجود ذاكرة التخزين المؤقت للملف ، ويقارن تاريخ تعديل ملف التكوين ، إذا كان متاحًا. عندما نقوم بإنشاء ذاكرة التخزين المؤقت للملف فإننا نحفظ أيضًا قائمة بالكائنات المستخدمة لمزيد من المقارنة.
تحميل متعددة
لإضافة تنسيق تكوين آخر ، يكفي تحديد فئة ستكون مسؤولة عن تنسيق معين. في المثال أدناه ، أضفنا دعمًا لتهيئة XML
ومعالج مناظر. سوف LoaderResolver
فئة LoaderResolver
على دمج تنسيقات مختلفة في مجموعة مشتركة ، LoaderResolver
فئة LoaderResolver
بتحميل الملف المطلوب عند الطلب.
<?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'));
من بين أمور أخرى ، يحتوي المكون على وظيفة لإنشاء معلومات مرجعية لوثائقك.
<?php ... use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; $dumper = new YamlReferenceDumper(); echo $dumper->dump($configuration);
سيتم إخراج:
blog: title: ~ # Required description: '' rss: false posts_main_page: 5 social: url: ~ icon: ~
الملخص
ربما ستجد أن كل هذا معقد ومربك للغاية ، ويمكنك القيام به مع وظيفتين. ربما ، ولكن هذا هو "السعر" لهيكل OOP جيد. من ناحية أخرى ، يقدم هذا المكون العديد من المزايا:
- تغطية 80٪ للمكون باختبارات ودعم نشط.
- من السهل حقًا إضافة تنسيقات تكوين جديدة. يكفي تعريف معالج يحول البيانات المصدر إلى صفيف عادي. سيتم استخدام بنية مماثلة لصيغ أخرى. يتم تنفيذ تمديد أي جزء من المكون عن طريق إضافة الواجهة اللازمة.
- يعمل التخزين المؤقت خارج الصندوق مع إعدادات مرنة لكل من بيئات
prod
. - التحقق المدمج من المعلمات وقيمها.
- توليد المعلومات الخلفية.