الفرق بين المراجعتين لصفحة: «Twig/advanced»
أسامه-دمراني (نقاش | مساهمات) تغيير ترجمة token (من رمز إلى وحدة) لمناسبة السياق. |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(6 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:توسيع Twig}}</noinclude> | <noinclude>{{DISPLAYTITLE:توسيع عمل محرك Twig}}</noinclude> | ||
توسيع Twig يتم بعدة | توسيع Twig يتم بعدة طرائق، إذ تستطيع إضافة وسوم tags إضافية ومرشحات واختبارات وعوامل operators ومتغيرات عامة global variables ودوال أيضًا، بل تستطيع توسيع المحلل parser نفسه بزوار العقد node visitors. لاحظ أن أول جزء من هذه الصفحة يشرح كيفية توسيع عمل محرك Twig، فإذا أردت إعادة استخدام ما تفعله في مشاريع مختلفة أو كنت تريد مشاركتها مع غيرك فيجب أن تنشئ توسيعًا بنفسك كما هو موضح أدناه. | ||
وانتبه إلى أن توسيع Twig من غير إنشاء توسعة جديدة extension تجعل Twig غير قادر على إعادة تصريف recompile قوالبك عند تحديث شيفرة [[PHP]]، ويجب أن تعطل تخزين القوالب المؤقت template caching أو تحزِّم شيفرتك في توسيعة كي ترى تغييراتك في الوقت الحقيقي، كما سيلي بيانه لاحقًا في هذه الصفحة. | |||
__toc__ | |||
انظر كيفية تنفيذ مولد lorem ipsum -الذي يحتاج أن يعرف عدد الكلمات التي يجب توليدها- كي تفهم كيف يكشف Twig كثيرًا من نقاط | يجب كذلك أن تفهم أولًا الاختلافات بين نقاط التوسعة extension points المختلفة ومتى يجب استخدام كل منها، قبل توسيع Twig نفسه، فاعلم أن Twig به بنيتين لغويتين أساسيتين: | ||
* <code><nowiki>{{ }}</nowiki></code>: تُستخدم لطباعة نتيجة تقييم التعبير. | |||
* <code>{% %}</code>: تُستخدم لتنفيذ التعليمات. | |||
انظر كيفية تنفيذ مولد النص التلقائي lorem ipsum -الذي يحتاج أن يعرف عدد الكلمات التي يجب توليدها- كي تفهم كيف يكشف Twig كثيرًا من نقاط التوسعة، استخدم وسم <code>lipsum</code>:<syntaxhighlight lang="twig"> | |||
{% lipsum 40 %} | {% lipsum 40 %} | ||
سطر 18: | سطر 22: | ||
{{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }} | {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }} | ||
</syntaxhighlight>ولا تحتاج إلى إنشاء وسوم إلا نادرًا، وهذا أمر حسن لأنها أعقد نقاط | </syntaxhighlight>ولا تحتاج إلى إنشاء وسوم إلا نادرًا، وهذا أمر حسن لأنها أعقد نقاط التوسعة. لنستخدم الآن مرشح <code>lipsum</code>: <syntaxhighlight lang="twig"> | ||
{{ 40|lipsum }} | {{ 40|lipsum }} | ||
</syntaxhighlight>وهذا يعمل أيضًا لكن يجب أن يحول | </syntaxhighlight>وهذا يعمل أيضًا لكن يجب أن يحول المرشحُ القيمةَ الممرَّرة إلى شيء آخر، وهنا نستخدم القيمة للإشارة إلى عدد الكلمات التي يجب توليدها، لذا فإن <code>40</code> هي وسيط argument للمرشح، وليست القيمة التي نريد تحويلها. لنستخدم الآن دالة <code>lipsum</code>:<syntaxhighlight lang="twig"> | ||
{{ lipsum(40) }} | {{ lipsum(40) }} | ||
</syntaxhighlight>ولهذا المثال خاصة فإن نقطة | </syntaxhighlight>ولهذا المثال خاصة فإن نقطة التوسعة التي ستُستخدم هي إنشاء الدالة، وتستطيع استخدامها في أي مكان يقبل تعبيرًا:<syntaxhighlight lang="twig"> | ||
{{ 'some text' ~ lipsum(40) ~ 'some more text' }} | {{ 'some text' ~ lipsum(40) ~ 'some more text' }} | ||
{% set lipsum = lipsum(40) %} | {% set lipsum = lipsum(40) %} | ||
</syntaxhighlight>وأخيرًا تستطيع استخدام كائنًا عامًا | </syntaxhighlight>وأخيرًا تستطيع استخدام كائنًا عامًا global object مع تابع قادر على إنشاء نص لوريم إبسوم:<syntaxhighlight lang="twig"> | ||
{{ text.lipsum(40) }} | {{ text.lipsum(40) }} | ||
سطر 39: | سطر 43: | ||
!متى | !متى | ||
|- | |- | ||
|شيفرة جامعة | |شيفرة جامعة macro | ||
|بسيط | |بسيط | ||
|متكرر | |متكرر | ||
|إنشاء المحتوى | |إنشاء المحتوى | ||
|- | |- | ||
|عام | |عام global | ||
|بسيط | |بسيط | ||
|متكرر | |متكرر | ||
|كائن مساعد | |كائن مساعد | ||
|- | |- | ||
|دالة | |دالة function | ||
|بسيط | |بسيط | ||
|متكرر | |متكرر | ||
|تَحوُّل القيمة | |تَحوُّل القيمة | ||
|- | |- | ||
|وسم | |وسم tag | ||
|معقد | |معقد | ||
|نادر | |نادر | ||
|بنية خاصة | |بنية خاصة للغة Domain Specific Language | ||
|- | |- | ||
|اختبار | |اختبار test | ||
|بسيط | |بسيط | ||
|نادر | |نادر | ||
|إجراء بولياني | |إجراء بولياني | ||
|- | |- | ||
|عامل | |عامل operator | ||
|بسيط | |بسيط | ||
|نادر | |نادر | ||
سطر 70: | سطر 74: | ||
|} | |} | ||
== المتغيرات العامة | == المتغيرات العامة Global Variables == | ||
المتغير العام يشبه أي متغير قالب آخر إلا أنه متاح في جميع القوالب والشيفرات الجامعة:<syntaxhighlight lang=" | المتغير العام يشبه أي متغير قالب آخر إلا أنه متاح في جميع القوالب والشيفرات الجامعة:<syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$twig->addGlobal('text', new Text()); | $twig->addGlobal('text', new Text()); | ||
سطر 79: | سطر 83: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == المرشحات == | ||
إنشاء | إنشاء المرشح يكون بربط اسم مع نوع البيانات "[[PHP/callable|callable]]" من [[PHP]]:<syntaxhighlight lang="php"> | ||
// دالة مجهولة | // دالة مجهولة | ||
$filter = new \Twig\TwigFilter('rot13', function ($string) { | $filter = new \Twig\TwigFilter('rot13', function ($string) { | ||
سطر 89: | سطر 93: | ||
$filter = new \Twig\TwigFilter('rot13', 'str_rot13'); | $filter = new \Twig\TwigFilter('rot13', 'str_rot13'); | ||
// | // class static method أو تابع صنف ساكن | ||
$filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); | $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); | ||
$filter = new \Twig\TwigFilter('rot13', 'SomeClass::rot13Filter'); | $filter = new \Twig\TwigFilter('rot13', 'SomeClass::rot13Filter'); | ||
سطر 97: | سطر 101: | ||
// هذا الأخير يحتاج إلى تطبيق في وقت التشغيل، انظر أدناه للتوضيح. | // هذا الأخير يحتاج إلى تطبيق في وقت التشغيل، انظر أدناه للتوضيح. | ||
$filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); | $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); | ||
</syntaxhighlight>يكون أول وسيط ممرَّر إلى منشئ <code>\Twig\TwigFilter</code> هو اسم | </syntaxhighlight>يكون أول وسيط ممرَّر إلى منشئ <code>\Twig\TwigFilter</code> هو اسم المرشح الذي ستستخدمه في القوالب والثاني هو نوع البيانات "[[PHP/callable|callable]]" المرتبط به. ثم بعد ذلك، نضيف المرشح إلى بيئة Twig: <syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$twig->addFilter($filter); | $twig->addFilter($filter); | ||
سطر 104: | سطر 108: | ||
{# Gjvt سيُخرج #} | {# Gjvt سيُخرج #} | ||
</syntaxhighlight>يستقبل نوع البيانات "callable" الجانب الأيسر من | </syntaxhighlight>يستقبل نوع البيانات "[[PHP/callable|callable]]" (ردود النداء) الجانب الأيسر من المرشح قبل الأنبوب <code>|</code> كوسيط أول عند استدعاء Twig له، وتمرَّر وسائط إضافية إلى المرشح داخل أقواس <code>()</code>. فمثلًا تُصرَّف الشيفرة التالية:<syntaxhighlight lang="twig"> | ||
{{ 'TWIG'|lower }} | {{ 'TWIG'|lower }} | ||
{{ now|date('d/m/Y') }} | {{ now|date('d/m/Y') }} | ||
</syntaxhighlight>لتكون كما يلي بعد التصريف:<syntaxhighlight lang=" | </syntaxhighlight>لتكون كما يلي بعد التصريف:<syntaxhighlight lang="php"> | ||
<?php echo strtolower('TWIG') ?> | <?php echo strtolower('TWIG') ?> | ||
<?php echo twig_date_format_filter($now, 'd/m/Y') ?> | <?php echo twig_date_format_filter($now, 'd/m/Y') ?> | ||
</syntaxhighlight>يأخذ الصنف <code>\Twig\TwigFilter</code> مصفوفة من الخيارات كآخر وسيط له:<syntaxhighlight lang=" | </syntaxhighlight>يأخذ الصنف <code>\Twig\TwigFilter</code> مصفوفة من الخيارات كآخر وسيط له:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('rot13', 'str_rot13', $options); | $filter = new \Twig\TwigFilter('rot13', 'str_rot13', $options); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === المرشحات الواعية للبيئة === | ||
إذا أردت الوصول إلى نسخة البيئة الحالية في | إذا أردت الوصول إلى نسخة البيئة الحالية في مرشحك فاجعل الخيار <code>needs_environment</code> على <code>true</code>، وسيمرر Twig البيئة الحالية كأول وسيط إلى استدعاء المرشح:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $string) { | $filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $string) { | ||
// اجلب مجموعة المحارف الحالية للنسخة. | // اجلب مجموعة المحارف الحالية للنسخة. | ||
سطر 125: | سطر 129: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === المرشحات الواعية للسياق === | ||
إذا أردت الوصول إلى السياق الحالي في | إذا أردت الوصول إلى السياق الحالي في مرشحك فاجعل الخيار <code>needs_context</code> على <code>true</code>، وسيمرر Twig السياق الحالي كأول وسيط إلى استدعاء المرشح أو الوسيط الثاني إذا كان <code>needs_environment</code> على <code>true</code> أيضًا:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('rot13', function ($context, $string) { | $filter = new \Twig\TwigFilter('rot13', function ($context, $string) { | ||
// ... | // ... | ||
سطر 136: | سطر 140: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== التهريب التلقائي | === التهريب التلقائي Automatic Escaping === | ||
إذا كان التهريب التلقائي مفعلًا فقد يُهرَّب خرج | إذا كان التهريب التلقائي مفعلًا فقد يُهرَّب خرج المرشح قبل الطباعة، وإن كان مرشحك يتصرف كمهرِّب أو يخرج شيفرة HTML أو جافاسكربت فربما تريد طباعة الخرج الخام، فاضبط خيار <code>is_safe</code> في مثل تلك الحالة:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('nl2br', 'nl2br', ['is_safe' => ['html']]); | $filter = new \Twig\TwigFilter('nl2br', 'nl2br', ['is_safe' => ['html']]); | ||
</syntaxhighlight>قد تحتاج بعض | </syntaxhighlight>قد تحتاج بعض المرشحات إلى التعامل مع مدخلات مهرَّبة سلفًا أو آمنة، كما في حالة إضافة وسوم HTML آمنة إلى خرج غير آمن، ففي مثل تلك الحالة، اضبط خيار <code>pre_escape</code> ليهرب بيانات الدخل قبل تشغيلها عبر المرشح الخاص بك:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('somefilter', 'somefilter', ['pre_escape' => 'html', 'is_safe' => ['html']]); | $filter = new \Twig\TwigFilter('somefilter', 'somefilter', ['pre_escape' => 'html', 'is_safe' => ['html']]); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === المرشحات المتغيرة Variadic Filters === | ||
إذا وجب على | إذا وجب على مرشح أن يقبل عددًا عشوائيًا من الوسائط، فاجعل خيار <code>is_variadic</code> على <code>true</code>، كي يمرِّر Twig الوسائط الإضافية كوسيط أخير في هيئة مصفوفة إلى استدعاء المرشح:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('thumbnail', function ($file, array $options = []) { | $filter = new \Twig\TwigFilter('thumbnail', function ($file, array $options = []) { | ||
// ... | // ... | ||
}, ['is_variadic' => true]); | }, ['is_variadic' => true]); | ||
</syntaxhighlight>لكن انتبه إلى أن [[Twig/templates|الوسائط المسمَّاة | </syntaxhighlight>لكن انتبه إلى أن [[Twig/templates|الوسائط المسمَّاة named arguments]] التي تمرَّر إلى مرشح متغير لا يمكن التحقق من صلاحيتها بما أنها ستؤول تلقائيًا إلى مصفوفة الخيارات option array. | ||
=== | === المرشحات الديناميكية === | ||
المرشح الديناميكي هو مرشح يحتوي اسمه على المحرف الخاص <code>*</code>، وهذا المحرف مطابق لأي سلسلة نصية:<syntaxhighlight lang="php"> | |||
$filter = new \Twig\TwigFilter('*_path', function ($name, $arguments) { | $filter = new \Twig\TwigFilter('*_path', function ($name, $arguments) { | ||
// ... | // ... | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight>المرشحات التالية مطابقة للمرشح الديناميكي المعرَّف أعلاه: | ||
* <code>product_path</code> | * <code>product_path</code> | ||
* <code>category_path</code> | * <code>category_path</code> | ||
يستطيع | يستطيع المرشح الديناميكي أن يعرِّف أكثر من جزء ليكون ديناميكيًا أيضًا:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('*_path_*', function ($name, $suffix, $arguments) { // ...}); | $filter = new \Twig\TwigFilter('*_path_*', function ($name, $suffix, $arguments) { // ...}); | ||
</syntaxhighlight>يستقبل | </syntaxhighlight>يستقبل المرشح كل قيم الجزء الديناميكي قبل وسائط المرشح العادي، لكن بعد البيئة والسياق، فمثلًا، إذا استدعينا <code>'foo'|a_path_b()</code> فستُمرَّر الوسائط <code>('a', 'b', 'foo')</code> إلى المرشح. | ||
=== | === المرشحات المهملة Deprecated Filters === | ||
تستطيع تحديد | تستطيع تحديد المرشح ليكون مهملًا من خلال ضبط الخيار <code>deprecated</code> على <code>true</code>، كما تستطيع تحديد مرشح بديل يكون بديلًا للمهمل إذا رأيت ذلك:<syntaxhighlight lang="php"> | ||
$filter = new \Twig\TwigFilter('obsolete', function () { | $filter = new \Twig\TwigFilter('obsolete', function () { | ||
// ... | // ... | ||
}, ['deprecated' => true, 'alternative' => 'new_one']); | }, ['deprecated' => true, 'alternative' => 'new_one']); | ||
</syntaxhighlight>عند إهمال | </syntaxhighlight>عند إهمال المرشح فإن Twig يرسل إشعار إهمال عند تصريف قالب يستخدم ذلك المرشح، انظر صفحة [[Twig/recipes|الوصفات]] للمزيد. | ||
== الدوال == | == الدوال == | ||
تعرَّف الدوال بنفس طريقة تعريف | تعرَّف الدوال بنفس طريقة تعريف المرشحات، لكن تحتاج إلى إنشاء نسخة من <code>\Twig\TwigFunction</code>:<syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$function = new \Twig\TwigFunction('function_name', function () { | $function = new \Twig\TwigFunction('function_name', function () { | ||
سطر 178: | سطر 182: | ||
}); | }); | ||
$twig->addFunction($function); | $twig->addFunction($function); | ||
</syntaxhighlight>تدعم الدوال نفس المزايا التي تدعمها | </syntaxhighlight>تدعم الدوال نفس المزايا التي تدعمها المرشحات ما عدا خياري <code>pre_escape</code> و <code>preserves_safety</code>. | ||
== الاختبارات == | == الاختبارات == | ||
تعَّرف الاختبارات بنفس طريقة تعريف الدوال | تعَّرف الاختبارات بنفس طريقة تعريف الدوال والمرشحات، لكن تحتاج إلى إنشاء نسخة من <code>\Twig\TwigTest</code>:<syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$test = new \Twig\TwigTest('test_name', function () { | $test = new \Twig\TwigTest('test_name', function () { | ||
سطر 187: | سطر 191: | ||
}); | }); | ||
$twig->addTest($test); | $twig->addTest($test); | ||
</syntaxhighlight>تسمح لك الاختبارات بإنشاء منطق يختص بتطبيق ما من أجل تقييم الشرطيات البوليانية | </syntaxhighlight>تسمح لك الاختبارات بإنشاء منطق يختص بتطبيق ما من أجل تقييم الشرطيات البوليانية boolean conditions. مثلًا، لتنشئ اختبار Twig يتحقق إن كانت الكائنات حمراء red:<syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$test = new \Twig\TwigTest('red', function ($value) { | $test = new \Twig\TwigTest('red', function ($value) { | ||
سطر 210: | سطر 203: | ||
}); | }); | ||
$twig->addTest($test); | $twig->addTest($test); | ||
</syntaxhighlight>يجب أن تعيد دوال الاختبار إما <code>true</code> أو <code>false</code>. | </syntaxhighlight>يجب أن تعيد دوال الاختبار إما <code>true</code> أو <code>false</code>. | ||
استخدم الخيار <code>node_class</code> عند إنشاء الاختبارات من أجل توفير تجميعة مخصصة للاختبار، وذلك ينفعك إن كان يمكن تصريف اختبارك داخل أنوع البيانات الأولية لـ PHP، وتستخدمه اختبارات عديدة مضمنة في Twig:<syntaxhighlight lang=" | استخدم الخيار <code>node_class</code> عند إنشاء الاختبارات من أجل توفير تجميعة مخصصة للاختبار، وذلك ينفعك إن كان يمكن تصريف اختبارك داخل أنوع البيانات الأولية لـ PHP، وتستخدمه اختبارات عديدة مضمنة في Twig:<syntaxhighlight lang="php"> | ||
namespace App; | namespace App; | ||
سطر 263: | سطر 231: | ||
} | } | ||
} | } | ||
</syntaxhighlight>يوضح المثال أعلاه كيف تنشئ اختبارات تستخدم صنف العقدة | </syntaxhighlight>يوضح المثال أعلاه كيف تنشئ اختبارات تستخدم صنف العقدة node class الذي يكون له وصول إلى عقدة فرعية اسمها <code>node</code>، تحتوي على القيمة التي تُختبر.<syntaxhighlight lang="twig"> | ||
{% if my_value is odd %} | {% if my_value is odd %} | ||
</syntaxhighlight>إذا استُخدم | </syntaxhighlight>إذا استُخدم مرشح <code>odd</code> في شيفرة مثل التي أعلاه فإن عقدة <code>node</code> ستحتوي على تعبير لـ <code>my_value</code>، ويكون للاختبارات المبنية على عقد وصول أيضًا إلى عقدة <code>arguments</code>، وستحتوي تلك العقدة على وسائط أخرى عديدة تم توفيرها لاختبارك. | ||
وإذا أردت تمرير عددًا متغيرًا من الوسائط الموضعية | وإذا أردت تمرير عددًا متغيرًا من الوسائط الموضعية positional أو المسماة named إلى الاختبار فاضبط خيار <code>is_variadic</code> على <code>true</code>، إذ أن الاختبارات تدعم الأسماء الديناميكية انظر المرشحات الديناميكية للبنية اللغوية Syntax. | ||
== الوسوم == | == الوسوم == | ||
سطر 273: | سطر 241: | ||
* إذا كان وسمك يولد بعض المخرجات فاستخدم دالة. | * إذا كان وسمك يولد بعض المخرجات فاستخدم دالة. | ||
* إذا كان وسمك يعدل بعض المحتوى ويعيده فاستخدم | * إذا كان وسمك يعدل بعض المحتوى ويعيده فاستخدم مرشحًا. | ||
مثلًا، إذا أردت إنشاء وسم يحول نصًا بصيغة مارك داون إلى HTML، فأنشئ | مثلًا، إذا أردت إنشاء وسم يحول نصًا بصيغة مارك داون إلى HTML، فأنشئ مرشح <code>markdown</code> بدلًا من إنشاء وسم:<syntaxhighlight lang="twig"> | ||
{{ '**markdown** text'|markdown }} | {{ '**markdown** text'|markdown }} | ||
</syntaxhighlight>إذا أردت استخدام هذا | </syntaxhighlight>إذا أردت استخدام هذا المرشح على نصوص كثيرة فغلفه بوسم [[Twig/apply|<code>apply</code>]]:<syntaxhighlight lang="twig"> | ||
{% apply markdown %} | {% apply markdown %} | ||
Title | Title | ||
سطر 284: | سطر 252: | ||
Much better than creating a tag as you can **compose** filters. | Much better than creating a tag as you can **compose** filters. | ||
{% endapply %} | {% endapply %} | ||
</syntaxhighlight>إذا لم يكن وسمك يخرج أي شيء لكنه موجود بسبب أثر جانبي فأنشئ دالة لا تعيد شيئًا واستدعها من خلال وسم <code>filter</code>، فمثلًا إذا أردت إنشاء وسم يسجل النصوص فأنشئ دالة <code>log</code> واستدعها من خلال وسم <code>do</code>:<syntaxhighlight lang="twig"> | </syntaxhighlight>إذا لم يكن وسمك يخرج أي شيء لكنه موجود بسبب أثر جانبي فأنشئ دالة لا تعيد شيئًا واستدعها من خلال وسم <code>[[Twig/filter|filter]]</code>، فمثلًا إذا أردت إنشاء وسم يسجل النصوص فأنشئ دالة <code>log</code> واستدعها من خلال وسم <code>[[Twig/do|do]]</code>:<syntaxhighlight lang="twig"> | ||
{% do log('Log some things') %} | {% do log('Log some things') %} | ||
</syntaxhighlight>والآن لننشئ وسمًا لبُنيةٍ لغوية جديدة وليكن <code>set</code> الذي يسمح بتعريف متغيرات بسيطة من داخل القالب، ويمكن استخدام ذلك الوسم كما يلي:<syntaxhighlight lang="twig"> | </syntaxhighlight>والآن لننشئ وسمًا لبُنيةٍ لغوية جديدة وليكن <code>[[Twig/set|set]]</code> الذي يسمح بتعريف متغيرات بسيطة من داخل القالب، ويمكن استخدام ذلك الوسم كما يلي:<syntaxhighlight lang="twig"> | ||
{% set name = "value" %} | {% set name = "value" %} | ||
سطر 292: | سطر 260: | ||
{# should output value #} | {# should output value #} | ||
</syntaxhighlight>لاحظ أن وسم <code>set</code> جزء من توسيع <code>core</code> وعليه سيكون متاحًا دائمًا، والنسخة المضمنة منه أقوى وتدعم إسنادات متعددة افتراضيًا. ستحتاج إلى ثلاثة خطوات من أجل تعريف وسم جديد: | </syntaxhighlight>لاحظ أن وسم [[Twig/set|<code>set</code>]] جزء من توسيع النواة <code>core</code> وعليه سيكون متاحًا دائمًا، والنسخة المضمنة منه أقوى وتدعم إسنادات متعددة افتراضيًا. ستحتاج إلى ثلاثة خطوات من أجل تعريف وسم جديد: | ||
* تعريف صنف محلل للوحدات | * تعريف صنف محلل للوحدات token parser class مسؤول عن تحليل شيفرة القالب. | ||
* تعريف صنف عقدة يكون مسؤولًا عن تحويل الشيفرة المحلَّلة إلى PHP. | * تعريف صنف عقدة يكون مسؤولًا عن تحويل الشيفرة المحلَّلة إلى PHP. | ||
* تسجيل الوسم. | * تسجيل الوسم. | ||
سطر 302: | سطر 270: | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$twig->addTokenParser(new Project_Set_TokenParser()); | $twig->addTokenParser(new Project_Set_TokenParser()); | ||
</syntaxhighlight>لننظر الآن في الشيفرة الحقيقية لهذا الصنف:<syntaxhighlight lang=" | </syntaxhighlight> | ||
=== تعريف محلل مفتاح Token Parser === | |||
لننظر الآن في الشيفرة الحقيقية لهذا الصنف:<syntaxhighlight lang="php"> | |||
class Project_Set_TokenParser extends \Twig\TokenParser\AbstractTokenParser | class Project_Set_TokenParser extends \Twig\TokenParser\AbstractTokenParser | ||
{ | { | ||
سطر 343: | سطر 294: | ||
} | } | ||
} | } | ||
</syntaxhighlight>يجب أن يعيد التابع <code>getTag()</code> الوسم الذي نريد تحليله، وهو <code>set</code> هنا، ويُستدعى التابع <code>parse()</code> كلما وجد وسم <code>set</code>، ويجب أن يعيد نسخة من <code>\Twig\Node\Node</code> تمثل العقدة (ستجد شرح إنشاء استدعاءات <code>Project_Set_Node</code> لاحقًا في الصفحة). | </syntaxhighlight>يجب أن يعيد التابع <code>getTag()</code> الوسم الذي نريد تحليله، وهو [[Twig/set|<code>set</code>]] هنا، ويُستدعى التابع <code>parse()</code> كلما وجد وسم <code>[[Twig/set|set]]</code>، ويجب أن يعيد نسخة من <code>\Twig\Node\Node</code> تمثل العقدة (ستجد شرح إنشاء استدعاءات <code>Project_Set_Node</code> لاحقًا في الصفحة). | ||
وتُبسَّط عملية التحليل بفضل مجموعة من التوابع التي يمكنك استدعاءها من مجرى | وتُبسَّط عملية التحليل بفضل مجموعة من التوابع التي يمكنك استدعاءها من مجرى مفتاح التشفير token stream أي <code>$this->parser->getStream()</code>: | ||
* <code>getCurrent()</code>: تجلب | * <code>getCurrent()</code>: تجلب المفتاح token الحالي في المجرى. | ||
* <code>next()</code>: تتحرك إلى | * <code>next()</code>: تتحرك إلى المفتاح التالي في المجرى لكن تعيد المفتاح القديم. | ||
* <code>test($type)</code> أو <code>test($value)</code> أو <code>test($type, $value)</code>: | * <code>test($type)</code> أو <code>test($value)</code> أو <code>test($type, $value)</code>: تحدد ما إذا كان المفتاح الحالي من نوع أو قيمة بعينها أو كليهما، وقد تكون القيمة مصفوفة من عدة قيم ممكنة. | ||
* <code>expect($type[, $value[, $message]])</code>: إذا لم يكن | * <code>expect($type[, $value[, $message]])</code>: إذا لم يكن المفتاح الحالي من النوع المعطى أو القيمة المعطاة فيُرفع خطأ لغوي syntax error، وإلا إذا كان النوع صحيحًا وكذلك القيمة فيعاد المفتاح وينتقل المجرى إلى الوحدة التالية. | ||
* <code>look()</code>: ينظر إلى الوحدة التالية من غير استهلاكه. | * <code>look()</code>: ينظر إلى الوحدة التالية من غير استهلاكه. | ||
* يتم تحليل التعابير من خلال استدعاء <code>parseExpression()</code> كما فعلنا في وسم <code>set</code>. | * يتم تحليل التعابير من خلال استدعاء <code>parseExpression()</code> كما فعلنا في وسم <code>set</code>. | ||
سطر 357: | سطر 308: | ||
تستطيع تعلم جميع التفاصيل التي في عملية التحليل بقراءة أصناف <code>TokenParser</code> الموجودة فعليًا. | تستطيع تعلم جميع التفاصيل التي في عملية التحليل بقراءة أصناف <code>TokenParser</code> الموجودة فعليًا. | ||
=== تعريف | === تعريف عقدة === | ||
إن الصنف <code>Project_Set_Node</code> نفسه قصير نوعًا ما:<syntaxhighlight lang=" | إن الصنف <code>Project_Set_Node</code> نفسه قصير نوعًا ما:<syntaxhighlight lang="php"> | ||
class Project_Set_Node extends \Twig\Node\Node | class Project_Set_Node extends \Twig\Node\Node | ||
{ | { | ||
سطر 402: | سطر 336: | ||
* <code>addDebugInfo()</code>: يضيف السطر المتعلق بالعقدة الحالية من ملف القالب الأصلي كتعليق. | * <code>addDebugInfo()</code>: يضيف السطر المتعلق بالعقدة الحالية من ملف القالب الأصلي كتعليق. | ||
* <code>indent()</code>: يزيح الشيفرة المولَّدة، انظر <code>\Twig\Node\BlockNode</code> لمثال على الاستخدام. | * <code>indent()</code>: يزيح الشيفرة المولَّدة، انظر <code>\Twig\Node\BlockNode</code> لمثال على الاستخدام. | ||
* <code>outdent()</code>: يزيح الشيفرة المولَّدة خارجيًا | * <code>outdent()</code>: يزيح الشيفرة المولَّدة خارجيًا outdent ، انظر <code>\Twig\Node\BlockNode</code> لمثال على الاستخدام. | ||
== إنشاء | == إنشاء توسعة Extension == | ||
إن ما يجعلك تنشئ | إن ما يجعلك تنشئ توسعة هو نقل الشيفرة المستخدمة بكثرة إلى صنف قابل لإعادة استخدامه مثل إضافة دعم للدُوَلية مثلًا internationalization، بأن يكون التطبيق مستخدمًا في عدة دول بعدة لغات مختلفة. ويستطيع التوسيع أن يعرِّف الوسوم والمرشحات والاختبارات والعوامل والدوال وزوار العقد node visitors . | ||
ومن المفيد غالبًا أن تنشئ | ومن المفيد غالبًا أن تنشئ توسعة واحدة لمشروعك لاستضافة جميع الوسوم المحددة والمرشحات التي تريد إضافتها إلى Twig. كذلك، عند تحزيم شيفرتك في توسيع فإن Twig من الذكاء بحيث يعيد تصريف قوالبك كلما أجريت تغييرًا فيه، إذا كان <code>auto_reload</code> مفعلًا. | ||
التوسعة Extension هي صنف يطبق الواجهة التالية:<syntaxhighlight lang="php"> | |||
interface \Twig\Extension\ExtensionInterface | interface \Twig\Extension\ExtensionInterface | ||
{ | { | ||
/** | /** | ||
* يعيد | * يعيد نسخ محلل الرموز لإضافته إلى القائمة الحالية | ||
* | * | ||
* @return \Twig\TokenParser\TokenParserInterface[] | * @return \Twig\TokenParser\TokenParserInterface[] | ||
سطر 464: | سطر 354: | ||
/** | /** | ||
* يعيد نسخ زائد العقدة لإضافتها إلى القائمة الحالية | * يعيد نسخ زائد العقدة لإضافتها إلى القائمة الحالية | ||
* | * | ||
* @return \Twig\NodeVisitor\NodeVisitorInterface[] | * @return \Twig\NodeVisitor\NodeVisitorInterface[] | ||
سطر 471: | سطر 361: | ||
/** | /** | ||
* يعيد قائمة من الفلاتر لإضافتها إلى القائمة الحالية | * يعيد قائمة من الفلاتر لإضافتها إلى القائمة الحالية | ||
* | * | ||
* @return \Twig\TwigFilter[] | * @return \Twig\TwigFilter[] | ||
سطر 478: | سطر 368: | ||
/** | /** | ||
* يعيد قائمة من الاختبارات لإضافتها إلى القائمة الحالية | * يعيد قائمة من الاختبارات لإضافتها إلى القائمة الحالية | ||
* | * | ||
* @return \Twig\TwigTest[] | * @return \Twig\TwigTest[] | ||
سطر 485: | سطر 375: | ||
/** | /** | ||
* يعيد قائمة من الدوال لإضافتها إلى القائمة الحالية | * يعيد قائمة من الدوال لإضافتها إلى القائمة الحالية | ||
* | * | ||
* @return \Twig\TwigFunction[] | * @return \Twig\TwigFunction[] | ||
سطر 499: | سطر 389: | ||
public function getOperators(); | public function getOperators(); | ||
} | } | ||
</syntaxhighlight>إذا أردت الحفاظ على صنف التوسيع الخاص بك رشيقًا فاجعله يرث من الصنف المضمن <code>\Twig\Extension\AbstractExtension</code> | </syntaxhighlight>إذا أردت الحفاظ على صنف التوسيع الخاص بك رشيقًا فاجعله يرث من الصنف المضمن <code>\Twig\Extension\AbstractExtension</code> بدلًا من تطبيق الواجهة بما أنه يوفر تطبيقات فارغة لجميع التوابع:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
} | } | ||
</syntaxhighlight>لا | </syntaxhighlight>لا تفعل هذه التوسعة أي شيء إلى الآن، وسنخصصها فيما يلي من الشرح، وتستطيع حفظها في أي مكان في نظام الملفات بما أن جميع التوسعات يجب أن تُسجل صراحة لتكون متاحة في قوالبك، كما تستطيع تسجيل توسيع باستخدام التابع <code>addExtension()</code> على الكائن الرئيسي <code>Enivronment</code> الخاص بك: <syntaxhighlight lang="php"> | ||
$twig = new \Twig\Environment($loader); | $twig = new \Twig\Environment($loader); | ||
$twig->addExtension(new Project_Twig_Extension()); | $twig->addExtension(new Project_Twig_Extension()); | ||
</syntaxhighlight>تستطيع النظر في | </syntaxhighlight>تستطيع النظر في توسعات النواة <code>core</code> الخاصة بـ Twig إذ هي أمثلة ممتازة على كيفية عمل التوسعات. | ||
=== المتغيرات العامة === | === المتغيرات العامة === | ||
يمكن تسجيل المتغيرات العامة في | يمكن تسجيل المتغيرات العامة في توسعة من خلال التابع <code>getGlobals()</code>:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface | ||
{ | { | ||
سطر 535: | سطر 414: | ||
=== الدوال === | === الدوال === | ||
يمكن تسجيل الدوال في | يمكن تسجيل الدوال في توسعة من خلال التابع <code>getFunctions()</code> :<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 560: | سطر 428: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === المرشحات === | ||
إذا أردت إضافة | إذا أردت إضافة مرشح إلى توسعة فستحتاج إلى إعادة تعريف التابع <code>getFilters()</code>، فيجب أن يعيد هذا التابع مصفوفة من المرشحات لإضافتها إلى بيئة Twig:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 587: | سطر 444: | ||
=== الوسوم === | === الوسوم === | ||
يمكن إضافة الوسوم إلى | يمكن إضافة الوسوم إلى توسعة بإعادة تعريف override التابع <code>getTokenParsers()</code>، فيجب أن يعيد مصفوفةً من الوسوم لإضافتها إلى بيئة Twig:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 597: | سطر 454: | ||
// ... | // ... | ||
} | } | ||
</syntaxhighlight>أضفنا في الشيفرة السابقة وسمًا واحدًا جديدًا، عرَّفه الصنف <code>Project_Set_TokenParser</code> ، وهذا الصنف مسؤول عن تحليل الوسم وتصريفه إلى PHP. | </syntaxhighlight>أضفنا في الشيفرة السابقة وسمًا واحدًا جديدًا، عرَّفه الصنف <code>Project_Set_TokenParser</code>، وهذا الصنف مسؤول عن تحليل الوسم وتصريفه إلى [[PHP/Array|PHP]]. | ||
=== العوامل | === العوامل Operators === | ||
يسمح لك التابع <code>getOperators()</code> بإضافة عوامل جديدة، انظر الشيفرة التالية كمثال على كيفية إضافة العوامل <code>!</code> و <code>||</code> و <code>&&</code>:<syntaxhighlight lang=" | يسمح لك التابع <code>getOperators()</code> بإضافة عوامل جديدة، انظر الشيفرة التالية كمثال على كيفية إضافة العوامل <code>!</code> و <code>||</code> و <code>&&</code>:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 638: | سطر 478: | ||
=== الاختبارات === | === الاختبارات === | ||
يسمح لك التابع <code>getTests()</code> بإضافة دوال اختبار جديدة:<syntaxhighlight lang=" | يسمح لك التابع <code>getTests()</code> بإضافة دوال اختبار جديدة:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 664: | سطر 493: | ||
=== موازنة بين التعريف ووقت التشغيل === | === موازنة بين التعريف ووقت التشغيل === | ||
يمكن تعريف | يمكن تعريف تنفيذات وقت التشغيل runtime implementations لمرشحات Twig ودواله واختباراته كأي نوع بيانات "[[PHP/callable|callable]]" في PHP: | ||
* '''الدوال/التوابع الساكنة''': بسيطة في تطبيقها، وسريعة -تستخدمها جميع | * '''الدوال/التوابع الساكنة''': بسيطة في تطبيقها، وسريعة -تستخدمها جميع توسعات <code>core</code> في Twig، لكن من الصعب أن يعتمد وقت التشغيل runtime على كائنات خارجية. | ||
* '''الإغلاقات | * '''الإغلاقات closures''' : بسيطة في تنفيذها. | ||
* '''توابع الكائن''': أكثر مرونة وتحتاجها إن كانت شيفرة وقت التشغيل لديك تعتمد على كائنات خارجية. | * '''توابع الكائن''': أكثر مرونة وتحتاجها إن كانت شيفرة وقت التشغيل لديك تعتمد على كائنات خارجية. | ||
إن أبسط طريقة لاستخدام التوابع هي تعريفها على | إن أبسط طريقة لاستخدام التوابع هي تعريفها على توسعة نفسها:<syntaxhighlight lang="php"> | ||
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | class Project_Twig_Extension extends \Twig\Extension\AbstractExtension | ||
{ | { | ||
سطر 715: | سطر 523: | ||
</syntaxhighlight>هذه الطريقة على بساطتها إلا أنه لا يُنصح بها لأنها تجعل تصريف القالب يعتمد على اعتماديات وقت التشغيل حتى لو لم تكن ثمة حاجة إليها. | </syntaxhighlight>هذه الطريقة على بساطتها إلا أنه لا يُنصح بها لأنها تجعل تصريف القالب يعتمد على اعتماديات وقت التشغيل حتى لو لم تكن ثمة حاجة إليها. | ||
تستطيع فصل تعريفات | تستطيع فصل تعريفات توسعة من تنفيذات وقت تشغيلها بتسجيل نسخة من <code>\Twig\RuntimeLoader\RuntimeLoaderInterface</code> على البيئة التي تعرف كيفية بدء أصناف وقت التشغيل تلك، ويجب أن تكون أصناف وقت التشغيل قابلة للتحميل التلقائي:<syntaxhighlight lang="php"> | ||
class RuntimeLoader implements \Twig\RuntimeLoader\RuntimeLoaderInterface | class RuntimeLoader implements \Twig\RuntimeLoader\RuntimeLoaderInterface | ||
{ | { | ||
سطر 749: | سطر 541: | ||
$twig->addRuntimeLoader(new RuntimeLoader()); | $twig->addRuntimeLoader(new RuntimeLoader()); | ||
</syntaxhighlight>لاحظ أن Twig يأتي بمحمِّل وقت تشغيل <code>\Twig\RuntimeLoader\ContainerRuntimeLoader</code> متوافق مع PSR-11، وصار من الممكن أن تنقل منطق وقت التشغيل إلى صنف <code>Project_Twig_RuntimeExtension</code> | </syntaxhighlight>لاحظ أن Twig يأتي بمحمِّل وقت تشغيل <code>\Twig\RuntimeLoader\ContainerRuntimeLoader</code> متوافق مع PSR-11، وصار من الممكن أن تنقل منطق وقت التشغيل إلى صنف <code>Project_Twig_RuntimeExtension</code> جديد وتستخدمه مباشرة في التوسيع:<syntaxhighlight lang="php"> | ||
class Project_Twig_RuntimeExtension | class Project_Twig_RuntimeExtension | ||
{ | { | ||
سطر 804: | سطر 570: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== اختبار | == اختبار توسعة == | ||
=== الاختبارات | === الاختبارات الدالية === | ||
تستطيع إنشاء اختبارات دالّية | تستطيع إنشاء اختبارات دالّية Functional Tests للتوسعات من خلال إنشاء بنية الملف التالية في مجلد اختبارك:<syntaxhighlight lang="php"> | ||
Fixtures/ | Fixtures/ | ||
filters/ | filters/ | ||
سطر 830: | سطر 585: | ||
bar.test | bar.test | ||
IntegrationTest.php | IntegrationTest.php | ||
</syntaxhighlight>ويجب أن يبدو ملف <code>IntegrationTest.php</code> كما يلي:<syntaxhighlight lang=" | </syntaxhighlight>ويجب أن يبدو ملف <code>IntegrationTest.php</code> كما يلي:<syntaxhighlight lang="php"> | ||
use Twig\Test\IntegrationTestCase; | use Twig\Test\IntegrationTestCase; | ||
سطر 865: | سطر 603: | ||
} | } | ||
} | } | ||
</syntaxhighlight>تستطيع العثور على أمثلة لواجهات الاختبار | </syntaxhighlight>تستطيع العثور على أمثلة لواجهات الاختبار Fixtures داخل مستودع Twig، داخل مجلد [https://github.com/twigphp/Twig/tree/3.x/tests/Fixtures tests/Twig/Fixtures]. | ||
=== اختبارات | === اختبارات العقد === | ||
قد يكون اختبار زوار العقد معقدًا، لذلك | قد يكون اختبار زوار العقد معقدًا، لذلك اجلب حالات اختبارك عبر الوراثة من <code>\Twig\Test\NodeTestCase</code>، وستجد أمثلة في مستودع Twig، داخل مجلد [https://github.com/twigphp/Twig/tree/3.x/tests/Node tests/Twig/Node]. | ||
== انظر أيضًا == | == انظر أيضًا == | ||
* [[Twig/templates|Twig لمصممي القوالب]] | * [[Twig/templates|Twig لمصممي القوالب]] | ||
* [[Twig/api|Twig للمطورين]] | * [[Twig/api|Twig للمطورين]] | ||
* [[Twig/internals|المكونات الداخلية لمحرك القوالب Twig]] | * [[Twig/internals|المكونات الداخلية لمحرك القوالب Twig]] | ||
== المصادر == | == المصادر == | ||
* [https://twig.symfony.com/doc/3.x/advanced.html#environment-aware-filters صفحة | * [https://twig.symfony.com/doc/3.x/advanced.html#environment-aware-filters صفحة Extending Twig من توثيق Twig الرسمي] | ||
[[تصنيف:Twig]] | [[تصنيف:Twig]] | ||
[[تصنيف: | [[تصنيف:Twig Intro]] | ||
المراجعة الحالية بتاريخ 10:55، 7 أكتوبر 2022
توسيع Twig يتم بعدة طرائق، إذ تستطيع إضافة وسوم tags إضافية ومرشحات واختبارات وعوامل operators ومتغيرات عامة global variables ودوال أيضًا، بل تستطيع توسيع المحلل parser نفسه بزوار العقد node visitors. لاحظ أن أول جزء من هذه الصفحة يشرح كيفية توسيع عمل محرك Twig، فإذا أردت إعادة استخدام ما تفعله في مشاريع مختلفة أو كنت تريد مشاركتها مع غيرك فيجب أن تنشئ توسيعًا بنفسك كما هو موضح أدناه.
وانتبه إلى أن توسيع Twig من غير إنشاء توسعة جديدة extension تجعل Twig غير قادر على إعادة تصريف recompile قوالبك عند تحديث شيفرة PHP، ويجب أن تعطل تخزين القوالب المؤقت template caching أو تحزِّم شيفرتك في توسيعة كي ترى تغييراتك في الوقت الحقيقي، كما سيلي بيانه لاحقًا في هذه الصفحة.
يجب كذلك أن تفهم أولًا الاختلافات بين نقاط التوسعة extension points المختلفة ومتى يجب استخدام كل منها، قبل توسيع Twig نفسه، فاعلم أن Twig به بنيتين لغويتين أساسيتين:
{{ }}
: تُستخدم لطباعة نتيجة تقييم التعبير.{% %}
: تُستخدم لتنفيذ التعليمات.
انظر كيفية تنفيذ مولد النص التلقائي lorem ipsum -الذي يحتاج أن يعرف عدد الكلمات التي يجب توليدها- كي تفهم كيف يكشف Twig كثيرًا من نقاط التوسعة، استخدم وسم lipsum
:
{% lipsum 40 %}
هذا وإن كان يعمل إلا أن استخدام وسمًا لـ lipsum
ليس الخيار الأمثل لثلاثة أسباب رئيسية:
lipsum
ليس بنية لغوية.- الوسم له مخرجات.
- الوسم ليس مرنًا إذ لا تستطيع استخدامه في تعبير:
{{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
ولا تحتاج إلى إنشاء وسوم إلا نادرًا، وهذا أمر حسن لأنها أعقد نقاط التوسعة. لنستخدم الآن مرشح lipsum
:
{{ 40|lipsum }}
وهذا يعمل أيضًا لكن يجب أن يحول المرشحُ القيمةَ الممرَّرة إلى شيء آخر، وهنا نستخدم القيمة للإشارة إلى عدد الكلمات التي يجب توليدها، لذا فإن 40
هي وسيط argument للمرشح، وليست القيمة التي نريد تحويلها. لنستخدم الآن دالة lipsum
:
{{ lipsum(40) }}
ولهذا المثال خاصة فإن نقطة التوسعة التي ستُستخدم هي إنشاء الدالة، وتستطيع استخدامها في أي مكان يقبل تعبيرًا:
{{ 'some text' ~ lipsum(40) ~ 'some more text' }}
{% set lipsum = lipsum(40) %}
وأخيرًا تستطيع استخدام كائنًا عامًا global object مع تابع قادر على إنشاء نص لوريم إبسوم:
{{ text.lipsum(40) }}
وكقاعدة عامة، استخدم الدوال في حالة المزايا التي يكثر استخدامها، والكائنات العامة لأي شيء آخر، واعتبر بالجدول التالي عند توسيع Twig:
الطريقة | صعوبة التنفيذ | التواتر | متى |
---|---|---|---|
شيفرة جامعة macro | بسيط | متكرر | إنشاء المحتوى |
عام global | بسيط | متكرر | كائن مساعد |
دالة function | بسيط | متكرر | تَحوُّل القيمة |
وسم tag | معقد | نادر | بنية خاصة للغة Domain Specific Language |
اختبار test | بسيط | نادر | إجراء بولياني |
عامل operator | بسيط | نادر | تَحوُّل القيمة |
المتغيرات العامة Global Variables
المتغير العام يشبه أي متغير قالب آخر إلا أنه متاح في جميع القوالب والشيفرات الجامعة:
$twig = new \Twig\Environment($loader);
$twig->addGlobal('text', new Text());
تستطيع حينها استخدام متغير text
في أي مكان داخل القالب:
{{ text.lipsum(40) }}
المرشحات
إنشاء المرشح يكون بربط اسم مع نوع البيانات "callable" من PHP:
// دالة مجهولة
$filter = new \Twig\TwigFilter('rot13', function ($string) {
return str_rot13($string);
});
// بسيطة PHP أو دالة
$filter = new \Twig\TwigFilter('rot13', 'str_rot13');
// class static method أو تابع صنف ساكن
$filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']);
$filter = new \Twig\TwigFilter('rot13', 'SomeClass::rot13Filter');
// أو تابع صنف
$filter = new \Twig\TwigFilter('rot13', [$this, 'rot13Filter']);
// هذا الأخير يحتاج إلى تطبيق في وقت التشغيل، انظر أدناه للتوضيح.
$filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']);
يكون أول وسيط ممرَّر إلى منشئ \Twig\TwigFilter
هو اسم المرشح الذي ستستخدمه في القوالب والثاني هو نوع البيانات "callable" المرتبط به. ثم بعد ذلك، نضيف المرشح إلى بيئة Twig:
$twig = new \Twig\Environment($loader);
$twig->addFilter($filter);
انظر الآن كيف تستخدمه في قالب:
{{ 'Twig'|rot13 }}
{# Gjvt سيُخرج #}
يستقبل نوع البيانات "callable" (ردود النداء) الجانب الأيسر من المرشح قبل الأنبوب |
كوسيط أول عند استدعاء Twig له، وتمرَّر وسائط إضافية إلى المرشح داخل أقواس ()
. فمثلًا تُصرَّف الشيفرة التالية:
{{ 'TWIG'|lower }}
{{ now|date('d/m/Y') }}
لتكون كما يلي بعد التصريف:
<?php echo strtolower('TWIG') ?>
<?php echo twig_date_format_filter($now, 'd/m/Y') ?>
يأخذ الصنف \Twig\TwigFilter
مصفوفة من الخيارات كآخر وسيط له:
$filter = new \Twig\TwigFilter('rot13', 'str_rot13', $options);
المرشحات الواعية للبيئة
إذا أردت الوصول إلى نسخة البيئة الحالية في مرشحك فاجعل الخيار needs_environment
على true
، وسيمرر Twig البيئة الحالية كأول وسيط إلى استدعاء المرشح:
$filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $string) {
// اجلب مجموعة المحارف الحالية للنسخة.
$charset = $env->getCharset();
return str_rot13($string);
}, ['needs_environment' => true]);
المرشحات الواعية للسياق
إذا أردت الوصول إلى السياق الحالي في مرشحك فاجعل الخيار needs_context
على true
، وسيمرر Twig السياق الحالي كأول وسيط إلى استدعاء المرشح أو الوسيط الثاني إذا كان needs_environment
على true
أيضًا:
$filter = new \Twig\TwigFilter('rot13', function ($context, $string) {
// ...
}, ['needs_context' => true]);
$filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $context, $string) {
// ...
}, ['needs_context' => true, 'needs_environment' => true]);
التهريب التلقائي Automatic Escaping
إذا كان التهريب التلقائي مفعلًا فقد يُهرَّب خرج المرشح قبل الطباعة، وإن كان مرشحك يتصرف كمهرِّب أو يخرج شيفرة HTML أو جافاسكربت فربما تريد طباعة الخرج الخام، فاضبط خيار is_safe
في مثل تلك الحالة:
$filter = new \Twig\TwigFilter('nl2br', 'nl2br', ['is_safe' => ['html']]);
قد تحتاج بعض المرشحات إلى التعامل مع مدخلات مهرَّبة سلفًا أو آمنة، كما في حالة إضافة وسوم HTML آمنة إلى خرج غير آمن، ففي مثل تلك الحالة، اضبط خيار pre_escape
ليهرب بيانات الدخل قبل تشغيلها عبر المرشح الخاص بك:
$filter = new \Twig\TwigFilter('somefilter', 'somefilter', ['pre_escape' => 'html', 'is_safe' => ['html']]);
المرشحات المتغيرة Variadic Filters
إذا وجب على مرشح أن يقبل عددًا عشوائيًا من الوسائط، فاجعل خيار is_variadic
على true
، كي يمرِّر Twig الوسائط الإضافية كوسيط أخير في هيئة مصفوفة إلى استدعاء المرشح:
$filter = new \Twig\TwigFilter('thumbnail', function ($file, array $options = []) {
// ...
}, ['is_variadic' => true]);
لكن انتبه إلى أن الوسائط المسمَّاة named arguments التي تمرَّر إلى مرشح متغير لا يمكن التحقق من صلاحيتها بما أنها ستؤول تلقائيًا إلى مصفوفة الخيارات option array.
المرشحات الديناميكية
المرشح الديناميكي هو مرشح يحتوي اسمه على المحرف الخاص *
، وهذا المحرف مطابق لأي سلسلة نصية:
$filter = new \Twig\TwigFilter('*_path', function ($name, $arguments) {
// ...
});
المرشحات التالية مطابقة للمرشح الديناميكي المعرَّف أعلاه:
product_path
category_path
يستطيع المرشح الديناميكي أن يعرِّف أكثر من جزء ليكون ديناميكيًا أيضًا:
$filter = new \Twig\TwigFilter('*_path_*', function ($name, $suffix, $arguments) { // ...});
يستقبل المرشح كل قيم الجزء الديناميكي قبل وسائط المرشح العادي، لكن بعد البيئة والسياق، فمثلًا، إذا استدعينا 'foo'|a_path_b()
فستُمرَّر الوسائط ('a', 'b', 'foo')
إلى المرشح.
المرشحات المهملة Deprecated Filters
تستطيع تحديد المرشح ليكون مهملًا من خلال ضبط الخيار deprecated
على true
، كما تستطيع تحديد مرشح بديل يكون بديلًا للمهمل إذا رأيت ذلك:
$filter = new \Twig\TwigFilter('obsolete', function () {
// ...
}, ['deprecated' => true, 'alternative' => 'new_one']);
عند إهمال المرشح فإن Twig يرسل إشعار إهمال عند تصريف قالب يستخدم ذلك المرشح، انظر صفحة الوصفات للمزيد.
الدوال
تعرَّف الدوال بنفس طريقة تعريف المرشحات، لكن تحتاج إلى إنشاء نسخة من \Twig\TwigFunction
:
$twig = new \Twig\Environment($loader);
$function = new \Twig\TwigFunction('function_name', function () {
// ...
});
$twig->addFunction($function);
تدعم الدوال نفس المزايا التي تدعمها المرشحات ما عدا خياري pre_escape
و preserves_safety
.
الاختبارات
تعَّرف الاختبارات بنفس طريقة تعريف الدوال والمرشحات، لكن تحتاج إلى إنشاء نسخة من \Twig\TwigTest
:
$twig = new \Twig\Environment($loader);
$test = new \Twig\TwigTest('test_name', function () {
// ...
});
$twig->addTest($test);
تسمح لك الاختبارات بإنشاء منطق يختص بتطبيق ما من أجل تقييم الشرطيات البوليانية boolean conditions. مثلًا، لتنشئ اختبار Twig يتحقق إن كانت الكائنات حمراء red:
$twig = new \Twig\Environment($loader);
$test = new \Twig\TwigTest('red', function ($value) {
if (isset($value->color) && $value->color == 'red') {
return true;
}
if (isset($value->paint) && $value->paint == 'red') {
return true;
}
return false;
});
$twig->addTest($test);
يجب أن تعيد دوال الاختبار إما true
أو false
.
استخدم الخيار node_class
عند إنشاء الاختبارات من أجل توفير تجميعة مخصصة للاختبار، وذلك ينفعك إن كان يمكن تصريف اختبارك داخل أنوع البيانات الأولية لـ PHP، وتستخدمه اختبارات عديدة مضمنة في Twig:
namespace App;
use Twig\Environment;
use Twig\Node\Expression\TestExpression;
use Twig\TwigTest;
$twig = new Environment($loader);
$test = new TwigTest(
'odd',
null,
['node_class' => OddTestExpression::class]);
$twig->addTest($test);
class OddTestExpression extends TestExpression
{
public function compile(\Twig\Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' % 2 != 0')
->raw(')')
;
}
}
يوضح المثال أعلاه كيف تنشئ اختبارات تستخدم صنف العقدة node class الذي يكون له وصول إلى عقدة فرعية اسمها node
، تحتوي على القيمة التي تُختبر.
{% if my_value is odd %}
إذا استُخدم مرشح odd
في شيفرة مثل التي أعلاه فإن عقدة node
ستحتوي على تعبير لـ my_value
، ويكون للاختبارات المبنية على عقد وصول أيضًا إلى عقدة arguments
، وستحتوي تلك العقدة على وسائط أخرى عديدة تم توفيرها لاختبارك.
وإذا أردت تمرير عددًا متغيرًا من الوسائط الموضعية positional أو المسماة named إلى الاختبار فاضبط خيار is_variadic
على true
، إذ أن الاختبارات تدعم الأسماء الديناميكية انظر المرشحات الديناميكية للبنية اللغوية Syntax.
الوسوم
إحدى المزايا المثيرة في محرك قوالب مثل Twig هو إمكانية تعريف بنيات لغوية جديدة، وهي إحدى أعقد الإمكانيات التي تتعرض لها إذا أردت فهم كيفية عمل Twig، لكننا لن نحتاج إلى وسم في أغلب الأحيان:
- إذا كان وسمك يولد بعض المخرجات فاستخدم دالة.
- إذا كان وسمك يعدل بعض المحتوى ويعيده فاستخدم مرشحًا.
مثلًا، إذا أردت إنشاء وسم يحول نصًا بصيغة مارك داون إلى HTML، فأنشئ مرشح markdown
بدلًا من إنشاء وسم:
{{ '**markdown** text'|markdown }}
إذا أردت استخدام هذا المرشح على نصوص كثيرة فغلفه بوسم apply
:
{% apply markdown %}
Title
=====
Much better than creating a tag as you can **compose** filters.
{% endapply %}
إذا لم يكن وسمك يخرج أي شيء لكنه موجود بسبب أثر جانبي فأنشئ دالة لا تعيد شيئًا واستدعها من خلال وسم filter
، فمثلًا إذا أردت إنشاء وسم يسجل النصوص فأنشئ دالة log
واستدعها من خلال وسم do
:
{% do log('Log some things') %}
والآن لننشئ وسمًا لبُنيةٍ لغوية جديدة وليكن set
الذي يسمح بتعريف متغيرات بسيطة من داخل القالب، ويمكن استخدام ذلك الوسم كما يلي:
{% set name = "value" %}
{{ name }}
{# should output value #}
لاحظ أن وسم set
جزء من توسيع النواة core
وعليه سيكون متاحًا دائمًا، والنسخة المضمنة منه أقوى وتدعم إسنادات متعددة افتراضيًا. ستحتاج إلى ثلاثة خطوات من أجل تعريف وسم جديد:
- تعريف صنف محلل للوحدات token parser class مسؤول عن تحليل شيفرة القالب.
- تعريف صنف عقدة يكون مسؤولًا عن تحويل الشيفرة المحلَّلة إلى PHP.
- تسجيل الوسم.
تسجيل وسم جديد
أضف وسمًا من خلال استدعاء التابع addTokenParser
على نسخة \Twig\Environment
:
$twig = new \Twig\Environment($loader);
$twig->addTokenParser(new Project_Set_TokenParser());
تعريف محلل مفتاح Token Parser
لننظر الآن في الشيفرة الحقيقية لهذا الصنف:
class Project_Set_TokenParser extends \Twig\TokenParser\AbstractTokenParser
{
public function parse(\Twig\Token $token)
{
$parser = $this->parser;
$stream = $parser->getStream();
$name = $stream->expect(\Twig\Token::NAME_TYPE)->getValue();
$stream->expect(\Twig\Token::OPERATOR_TYPE, '=');
$value = $parser->getExpressionParser()->parseExpression();
$stream->expect(\Twig\Token::BLOCK_END_TYPE);
return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'set';
}
}
يجب أن يعيد التابع getTag()
الوسم الذي نريد تحليله، وهو set
هنا، ويُستدعى التابع parse()
كلما وجد وسم set
، ويجب أن يعيد نسخة من \Twig\Node\Node
تمثل العقدة (ستجد شرح إنشاء استدعاءات Project_Set_Node
لاحقًا في الصفحة).
وتُبسَّط عملية التحليل بفضل مجموعة من التوابع التي يمكنك استدعاءها من مجرى مفتاح التشفير token stream أي $this->parser->getStream()
:
getCurrent()
: تجلب المفتاح token الحالي في المجرى.
next()
: تتحرك إلى المفتاح التالي في المجرى لكن تعيد المفتاح القديم.test($type)
أوtest($value)
أوtest($type, $value)
: تحدد ما إذا كان المفتاح الحالي من نوع أو قيمة بعينها أو كليهما، وقد تكون القيمة مصفوفة من عدة قيم ممكنة.expect($type[, $value[, $message]])
: إذا لم يكن المفتاح الحالي من النوع المعطى أو القيمة المعطاة فيُرفع خطأ لغوي syntax error، وإلا إذا كان النوع صحيحًا وكذلك القيمة فيعاد المفتاح وينتقل المجرى إلى الوحدة التالية.look()
: ينظر إلى الوحدة التالية من غير استهلاكه.- يتم تحليل التعابير من خلال استدعاء
parseExpression()
كما فعلنا في وسمset
.
تستطيع تعلم جميع التفاصيل التي في عملية التحليل بقراءة أصناف TokenParser
الموجودة فعليًا.
تعريف عقدة
إن الصنف Project_Set_Node
نفسه قصير نوعًا ما:
class Project_Set_Node extends \Twig\Node\Node
{
public function __construct($name, \Twig\Node\Expression\AbstractExpression $value, $line, $tag = null)
{
parent::__construct(['value' => $value], ['name' => $name], $line, $tag);
}
public function compile(\Twig\Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('$context[\''.$this->getAttribute('name').'\'] = ')
->subcompile($this->getNode('value'))
->raw(";\n")
;
}
}
ينفذ المصرِّف واجهة سلسلة ويوفر توابع تساعد المطور على توليد شيفرات PHP جميلة وسهلة القراءة:
subcompile()
: يصرِّف العقدة.raw()
: يكتب السلسلة النصية كما هي.write()
: يكتب السلسلة النصية المعطاة بإضافة إزاحة في بداية كل سطر.string()
: يكتب سلسلة نصية بين علامتي اقتباس.repr()
: يكتب تمثيل PHP لقيمة معطاة، انظر \Twig\Node\ForNode
لمثال على استخدامه.addDebugInfo()
: يضيف السطر المتعلق بالعقدة الحالية من ملف القالب الأصلي كتعليق.indent()
: يزيح الشيفرة المولَّدة، انظر \Twig\Node\BlockNode
لمثال على الاستخدام.outdent()
: يزيح الشيفرة المولَّدة خارجيًا outdent ، انظر \Twig\Node\BlockNode
لمثال على الاستخدام.
إنشاء توسعة Extension
إن ما يجعلك تنشئ توسعة هو نقل الشيفرة المستخدمة بكثرة إلى صنف قابل لإعادة استخدامه مثل إضافة دعم للدُوَلية مثلًا internationalization، بأن يكون التطبيق مستخدمًا في عدة دول بعدة لغات مختلفة. ويستطيع التوسيع أن يعرِّف الوسوم والمرشحات والاختبارات والعوامل والدوال وزوار العقد node visitors .
ومن المفيد غالبًا أن تنشئ توسعة واحدة لمشروعك لاستضافة جميع الوسوم المحددة والمرشحات التي تريد إضافتها إلى Twig. كذلك، عند تحزيم شيفرتك في توسيع فإن Twig من الذكاء بحيث يعيد تصريف قوالبك كلما أجريت تغييرًا فيه، إذا كان auto_reload
مفعلًا.
التوسعة Extension هي صنف يطبق الواجهة التالية:
interface \Twig\Extension\ExtensionInterface
{
/**
* يعيد نسخ محلل الرموز لإضافته إلى القائمة الحالية
*
* @return \Twig\TokenParser\TokenParserInterface[]
*/
public function getTokenParsers();
/**
* يعيد نسخ زائد العقدة لإضافتها إلى القائمة الحالية
*
* @return \Twig\NodeVisitor\NodeVisitorInterface[]
*/
public function getNodeVisitors();
/**
* يعيد قائمة من الفلاتر لإضافتها إلى القائمة الحالية
*
* @return \Twig\TwigFilter[]
*/
public function getFilters();
/**
* يعيد قائمة من الاختبارات لإضافتها إلى القائمة الحالية
*
* @return \Twig\TwigTest[]
*/
public function getTests();
/**
* يعيد قائمة من الدوال لإضافتها إلى القائمة الحالية
*
* @return \Twig\TwigFunction[]
*/
public function getFunctions();
/**
* يعيد قائمة من العوامل لإضافتها إلى القائمة الحالية.
* Returns a list of operators to add to the existing list.
*
* @return array<array> First array of unary operators, second array of binary operators
*/
public function getOperators();
}
إذا أردت الحفاظ على صنف التوسيع الخاص بك رشيقًا فاجعله يرث من الصنف المضمن \Twig\Extension\AbstractExtension
بدلًا من تطبيق الواجهة بما أنه يوفر تطبيقات فارغة لجميع التوابع:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
}
لا تفعل هذه التوسعة أي شيء إلى الآن، وسنخصصها فيما يلي من الشرح، وتستطيع حفظها في أي مكان في نظام الملفات بما أن جميع التوسعات يجب أن تُسجل صراحة لتكون متاحة في قوالبك، كما تستطيع تسجيل توسيع باستخدام التابع addExtension()
على الكائن الرئيسي Enivronment
الخاص بك:
$twig = new \Twig\Environment($loader);
$twig->addExtension(new Project_Twig_Extension());
تستطيع النظر في توسعات النواة core
الخاصة بـ Twig إذ هي أمثلة ممتازة على كيفية عمل التوسعات.
المتغيرات العامة
يمكن تسجيل المتغيرات العامة في توسعة من خلال التابع getGlobals()
:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
{
public function getGlobals(): array
{
return [
'text' => new Text(),
];
}
// ...
}
الدوال
يمكن تسجيل الدوال في توسعة من خلال التابع getFunctions()
:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('lipsum', 'generate_lipsum'),
];
}
// ...
}
المرشحات
إذا أردت إضافة مرشح إلى توسعة فستحتاج إلى إعادة تعريف التابع getFilters()
، فيجب أن يعيد هذا التابع مصفوفة من المرشحات لإضافتها إلى بيئة Twig:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getFilters()
{
return [
new \Twig\TwigFilter('rot13', 'str_rot13'),
];
}
// ...
}
الوسوم
يمكن إضافة الوسوم إلى توسعة بإعادة تعريف override التابع getTokenParsers()
، فيجب أن يعيد مصفوفةً من الوسوم لإضافتها إلى بيئة Twig:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getTokenParsers()
{
return [new Project_Set_TokenParser()];
}
// ...
}
أضفنا في الشيفرة السابقة وسمًا واحدًا جديدًا، عرَّفه الصنف Project_Set_TokenParser
، وهذا الصنف مسؤول عن تحليل الوسم وتصريفه إلى PHP.
العوامل Operators
يسمح لك التابع getOperators()
بإضافة عوامل جديدة، انظر الشيفرة التالية كمثال على كيفية إضافة العوامل !
و ||
و &&
:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getOperators()
{
return [
[
'!' => ['precedence' => 50, 'class' => \Twig\Node\Expression\Unary\NotUnary::class],
],
[
'||' => ['precedence' => 10, 'class' => \Twig\Node\Expression\Binary\OrBinary::class, 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT],
'&&' => ['precedence' => 15, 'class' => \Twig\Node\Expression\Binary\AndBinary::class, 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT],
],
];
}
// ...
}
الاختبارات
يسمح لك التابع getTests()
بإضافة دوال اختبار جديدة:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getTests()
{
return [
new \Twig\TwigTest('even', 'twig_test_even'),
];
}
// ...
}
موازنة بين التعريف ووقت التشغيل
يمكن تعريف تنفيذات وقت التشغيل runtime implementations لمرشحات Twig ودواله واختباراته كأي نوع بيانات "callable" في PHP:
- الدوال/التوابع الساكنة: بسيطة في تطبيقها، وسريعة -تستخدمها جميع توسعات
core
في Twig، لكن من الصعب أن يعتمد وقت التشغيل runtime على كائنات خارجية. - الإغلاقات closures : بسيطة في تنفيذها.
- توابع الكائن: أكثر مرونة وتحتاجها إن كانت شيفرة وقت التشغيل لديك تعتمد على كائنات خارجية.
إن أبسط طريقة لاستخدام التوابع هي تعريفها على توسعة نفسها:
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
private $rot13Provider;
public function __construct($rot13Provider)
{
$this->rot13Provider = $rot13Provider;
}
public function getFunctions()
{
return [
new \Twig\TwigFunction('rot13', [$this, 'rot13']),
];
}
public function rot13($value)
{
return $this->rot13Provider->rot13($value);
}
}
هذه الطريقة على بساطتها إلا أنه لا يُنصح بها لأنها تجعل تصريف القالب يعتمد على اعتماديات وقت التشغيل حتى لو لم تكن ثمة حاجة إليها.
تستطيع فصل تعريفات توسعة من تنفيذات وقت تشغيلها بتسجيل نسخة من \Twig\RuntimeLoader\RuntimeLoaderInterface
على البيئة التي تعرف كيفية بدء أصناف وقت التشغيل تلك، ويجب أن تكون أصناف وقت التشغيل قابلة للتحميل التلقائي:
class RuntimeLoader implements \Twig\RuntimeLoader\RuntimeLoaderInterface
{
public function load($class)
{
// $class طبق المنطق لإنشاء نسخة من
// وأدخل اعتمادياتها.
// وسيعني ذلك في الغالب أن تستخدم حاوية حقن الاعتماديات
// dependency injection container أو
if ('Project_Twig_RuntimeExtension' === $class) {
return new $class(new Rot13Provider());
} else {
// ...
}
}
}
$twig->addRuntimeLoader(new RuntimeLoader());
لاحظ أن Twig يأتي بمحمِّل وقت تشغيل \Twig\RuntimeLoader\ContainerRuntimeLoader
متوافق مع PSR-11، وصار من الممكن أن تنقل منطق وقت التشغيل إلى صنف Project_Twig_RuntimeExtension
جديد وتستخدمه مباشرة في التوسيع:
class Project_Twig_RuntimeExtension
{
private $rot13Provider;
public function __construct($rot13Provider)
{
$this->rot13Provider = $rot13Provider;
}
public function rot13($value)
{
return $this->rot13Provider->rot13($value);
}
}
class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('rot13', ['Project_Twig_RuntimeExtension', 'rot13']),
// أو
new \Twig\TwigFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
];
}
}
اختبار توسعة
الاختبارات الدالية
تستطيع إنشاء اختبارات دالّية Functional Tests للتوسعات من خلال إنشاء بنية الملف التالية في مجلد اختبارك:
Fixtures/
filters/
foo.test
bar.test
functions/
foo.test
bar.test
tags/
foo.test
bar.test
IntegrationTest.php
ويجب أن يبدو ملف IntegrationTest.php
كما يلي:
use Twig\Test\IntegrationTestCase;
class Project_Tests_IntegrationTest extends IntegrationTestCase
{
public function getExtensions()
{
return [
new Project_Twig_Extension1(),
new Project_Twig_Extension2(),
];
}
public function getFixturesDir()
{
return __DIR__.'/Fixtures/';
}
}
تستطيع العثور على أمثلة لواجهات الاختبار Fixtures داخل مستودع Twig، داخل مجلد tests/Twig/Fixtures.
اختبارات العقد
قد يكون اختبار زوار العقد معقدًا، لذلك اجلب حالات اختبارك عبر الوراثة من \Twig\Test\NodeTestCase
، وستجد أمثلة في مستودع Twig، داخل مجلد tests/Twig/Node.