تخطَّ إلى المحتوى

محلِّل CSS: التتالي والخصوصية

يطابق CssResolver المحدِّدات مع دفق الرموز، ويرتِّب القواعد المطابَقة حسب طبقة التتالي والخصوصية وترتيب المستند، ثم يطبِّق !important في مرور ثانٍ.

Terminal window
composer require nextpdf/core:^3

CssResolver هو مكوِّن الطبقة 1 (وفقًا لـ ⁨ADR-010⁩). يحتفظ بقواعد صفائح الأنماط المتتالية (⁨CSS⁩) المحلَّلة، ويقرِّر أيُّ التصريحات تنطبق على كل عنصر. استُخرج الصنف من HtmlParser للحفاظ على وضوح البنية، وهو داخلي وليس واجهة برمجة تطبيقات (⁨API⁩) عامة.

لا يحتاج المحلِّل إلى شجرة مستند. تقرأ مطابقة المحدِّدات دفق الرموز المسطَّح، وتستخدم خرائط الفهرسة التي يبنيها HtmlChildScanner في المسار في المرحلة 3: عدد العناصر الأبناء، وعدد العناصر ذات الوسم نفسه، والفراغ. تتولى هذه الخرائط الإجابة عن الأصناف الزائفة البنيوية. يستخدم المحدِّد العلائقي :has() المسح المسبق المحدود الموصوف في قيود البثّ.

يُحلّ التتالي عبر مرورين داخل CssResolver::resolveMatchingProperties(). يطبِّق المرور 1 التصريحات العادية بترتيب التتالي: وزن طبقة التتالي أولًا، ثم الخصوصية، ثم ترتيب المستند. ويطبِّق المرور 2 تصريحات !important بترتيب الخصوصية. يتجاوز تصريح !important أيَّ تصريح عادي، بصرف النظر عن الخصوصية. هذا التقسيم إلى مرورين هو إستراتيجية التنفيذ، وينتج مجموعة الخصائص المحلولة التي تستهلكها طبقة التخطيط.

يتوافق ترتيب التتالي الذي ينفِّذه المحلِّل مع مواصفة ⁨CSS⁩ للتتالي والوراثة الصادرة عن اتحاد الشبكة العالمية (⁨W3C⁩). تُرتَّب التصريحات أولًا حسب المصدر والأهمية، ثم حسب خصوصية المحدِّد. وعند تساوي الخصوصية، يفوز آخر تصريح في ترتيب المستند (⁨CSS Cascade 5⁩ §6.4؛ راجع المطابقة). يستشهد التعليق داخل المصدر في CssResolver بالبند نفسه، وبذلك تتوفر طريقة ثالثة للتحقق من هذا السلوك إلى جانب المواصفة والمسرد.

تُحسَب الخصوصية كثلاثية (⁨A⁩، ⁨B⁩، ⁨C⁩) من أعداد مكوِّنات المعرِّف والصنف والنوع، وتُقارَن الثلاثيات مكوِّنًا بمكوِّن (⁨Selectors Level 4⁩ §16). يحسب ⁨NextPDF⁩ الخصوصية لكل قاعدة مطابَقة قبل ترتيب التتالي.

هناك قيد واحد مهم. تنطبق قاعدة عكس الطبقات §6.4.3 على تصريحات !important عبر طبقات التتالي، ويسجِّلها المصدر بوصفها معلَّقة لمجموعة عمل طبقة التتالي. عند إعلان طبقات التتالي وعبور !important بين الطبقات، قد يختلف الترتيب المحلول عن سلوك المواصفة الكامل. تُعد مصفوفة دعم ⁨CSS⁩ المرجع لحالة الدعم لكل خاصية، ولا تعيد هذه الصفحة سرد الدعم لكل خاصية.

الرمزالموضعالدور
CssResolver::parseStyleBlock(string $css, bool $nestingEnabled = false): voidsrc/Html/CssResolver.phpتحليل كتلة <style> إلى قواعد.
CssResolver::resolveMatchingProperties(...)src/Html/CssResolver.phpمطابقة المحدِّدات وحلّ التتالي ذي المرورين.
CssResolver::resolveHasSelectors(array $tokens): arraysrc/Html/CssResolver.phpمسح مسبق محدود لـ :has() (مقيَّد ببوابة).
CssResolver::resolveFirstLetterProperties(...)src/Html/CssResolver.phpحلّ خصائص ::first-letter.
CssResolver::resolvePseudoElementProperties(...)src/Html/CssResolver.phpحلّ خصائص ::before / ::after.
CssResolver::getLayerRegistry(): LayerRegistrysrc/Html/CssResolver.phpطبقات التتالي المُعلَنة.

مثال على الكود — البدء السريع

قسم بعنوان «مثال على الكود — البدء السريع»

لا تستدعِ المحلِّل مباشرةً. اكتب ⁨CSS⁩، وسيعمل المحلِّل داخل writeHtml(). في التتالي أدناه، يُحَلّ p إلى اللون الأحمر لأن قاعدة الصنف ذات خصوصية أعلى من قاعدة النوع.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->writeHtml(
'<style>p { color: blue; } .lead { color: red; }</style>'
. '<p class="lead">Higher-specificity class wins.</p>'
);
$doc->save(__DIR__ . '/output/cascade.pdf');

يُظهر هذا المثال المرور الثاني لـ !important. يتجاوز تصريح النوع !important تصريح الصنف المكافئ للمضمَّن، مع أن محدِّد الصنف ذو خصوصية أعلى.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->writeHtml(
'<style>p { color: green !important; } .lead { color: red; }</style>'
. '<p class="lead">!important overrides higher specificity.</p>'
);
$doc->save(__DIR__ . '/output/important.pdf');
  • !important يتجاهل الخصوصية. يطبِّق المرور 2 تصريحات !important بترتيب الخصوصية، وتتجاوز هذه التصريحات دائمًا التصريحات العادية.
  • طبقات التتالي + !important عبر الطبقات. يسجِّل المصدر قاعدة عكس الطبقات §6.4.3 للتصريحات المهمة بوصفها معلَّقة. تحقَّق من السلوك مقابل مصفوفة دعم ⁨CSS⁩ قبل الاعتماد عليه.
  • عدم إعلان أي طبقات هو المسار السريع. بدون @layer، يتقلَّص الترتيب إلى سلوك يعتمد على الخصوصية وحدها، ويكون مطابقًا بِتيًّا للسلوك السابق للطبقات.
  • :has() مقيَّد ببوابة. يعمل المسح المسبق العلائقي فقط عند تمكين الميزة التجريبية css.has.
  • مطابقة المحدِّدات قائمة على الدفق. تستخدم المحدِّدات البنيوية خرائط الفهرسة، لا اجتياز الشجرة. أي محدِّد يستلزم اجتيازًا اعتباطيًّا للشجرة يتجاوز خرائط الفهرسة لا يمكن حلّه في هذا النموذج.

في أسوأ الحالات، تكون مطابقة المحدِّدات ⁨O⁩(القواعد × العناصر)، مقيَّدةً بـ حدود البثّ. ترتيبا التتالي هما ⁨O⁩(القواعد المطابَقة · لوغاريتم القواعد المطابَقة) لكل عنصر. يتجاوز المسار غير المستخدم لطبقات التتالي حلّ الطبقات بالكامل. تحدِّد ميزانية الأداء performance_budget لكل صفحة (wall_ms: 1500، peak_mb: 64) السقف التشغيلي. يحرس اختبار قياس أداء مسار عرض ⁨HTML⁩ من حدوث التراجعات (عمل مدمَج، ⁨PR⁩ #564).

لا يرى المحلِّل سوى ⁨CSS⁩ الذي يسمح به DefaultHtmlSecurityPolicy::isCssPropertyAllowed(). تحدِّد قائمة السماح سقف الأمان، ويحدِّد جدول الدعم في وقت التشغيل سقف قدرة منفصلًا. الخاصية التي تحظرها السياسة لا تصل أبدًا إلى التتالي. راجع نموذج أمان وحدة ⁨HTML⁩.

السلوكالمواصفةالبند⁨reference_id⁩
فرز التتالي: ⁨origin/importance⁩ ← الخصوصية ← ترتيب الظهور⁨W3C CSS Cascading and Inheritance Level 5⁩§6.4 (css_cascade_5#x1.x7.x1.p21)
الخصوصية كثلاثية (⁨A⁩،⁨B⁩،⁨C⁩) من أعداد المعرِّف/الصنف/النوع⁨W3C Selectors Level 4⁩§16 (selectors_4#x1.x16.p2)
تحليل حتمي واستعادة من أخطاء التحليل⁨W3C CSS Syntax Level 3⁩§4 (css_syntax_3#x1.x4.p2)

مادة ⁨W3C⁩ مرخَّصة وفق ⁨CC-BY 4.0.⁩ الادعاءات أعلاه مُعاد صياغتها. تُقدَّم معرِّفات البنود والمقاطع لأغراض التحقق. لا يدّعي ⁨NextPDF⁩ المطابقة الكاملة لهذه الوحدات؛ راجع مصفوفة دعم ⁨CSS⁩ للاطلاع على الحالة المتحقَّقة لكل وحدة.

قدرة ⁨Enterprise.⁩ يوسِّع ⁨Premium⁩ مجموعة الخصائص المطابَقة والمطبَّقة. خوارزمية التتالي ونموذج !important ذي المرورين متطابقان عبر الإصدارات. راجع مصفوفة دعم ⁨CSS⁩.