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

Ast: شجرة المستند الدلالية وتسلسلها

توفّر وحدة ⁨Ast⁩ شجرة بناء الجملة المجردة (⁨AST⁩) الدلالية الخاصة بمستندات المحرّك. وتمثّل المستند كتسلسل هرمي من العُقَد المُصنّفة بحسب النوع: Document، وSection، وHeading، وParagraph، وList، وTable، وFigure، وCode، وFormField. يُسجّل النموذج المربعات المُحيطة ومرتكزات الاستشهاد، ويُسلسلها إلى ⁨JavaScript Object Notation⁩ (⁨JSON⁩) مُصدَّر الإصدار. وتستخدم طبقة وسم إمكانية الوصول هذه الشجرة لإنتاج شجرة بنية.

الاستقرار: تجريبي. هذا سطح نموذج داخلي. فئاته لا توفّر أي ضمانات ثابتة لواجهة برمجة التطبيقات (⁨API⁩) العامة المُجمّدة بحسب الإصدار. قد تتغيّر مجموعة العُقَد وسمات العُقَد. يخضع مخطط التسلسل لإصدارٍ مستقل (AstDocument::CURRENT_SCHEMA_VERSION = '1.0.0'). يكتشف المُسلسِل أي مخطط غير متوافق ويرفضه، بحيث يحتفظ ⁨AST JSON⁩ المُخزَّن بعقد استمرارية مستقر حتى عندما تتغيّر واجهة ⁨API⁩ في الذاكرة.

Terminal window
composer require nextpdf/core:^3

هنا، تُمثّل شجرة ⁨AST⁩ البنية المنطقية للمستند. وهي ليست شجرة بناء جملة لمُحلِّل مرتبط بصيغة إدخال واحدة. AstDocument هو الحاوية، ويحمل AstNode الجذرية (التي يجب أن تكون NodeType::Document)، وإصدار مخطط، وتجزئة لملف ⁨Portable Document Format⁩ (⁨PDF⁩) المصدر، وعدد الصفحات. ويرفض الإنشاء غير الصالح، بما في ذلك إصدار مخطط فارغ، أو عدد صفحات أقل من واحد، أو نوع جذر غير صحيح.

AstNode هي العقدة العَوْدية. ويُعدّد NodeType الأنواع الدلالية. تحمل العقدة عُقَدًا فرعية، وBoundingBox اختيارية، ومحتوى نصيًا اختياريًا، وسمات يتحقّق منها NodeAttributeSchema. تدعم واجهة ⁨API⁩ الخاصة بالعقدة الاشتقاق غير القابل للتغيير. تُعيد withBboxAndText() عقدة جديدة. وتنسخ deepClone() شجرة فرعية. NodeId هي هوية كائن القيمة. ويربط CitationAnchor العقدة بموقع مصدر من أجل إمكانية التتبّع. AstNodeCollection هي مجموعة من نوع Countable/IteratorAggregate تدعم التصفية بواسطة ofType().

AstSerializer هو حدّ الاستمرارية. تكتب serialize() كائن AstDocument إلى ⁨JSON.⁩ وتقرأه deserialize() مجددًا. تتيح لك canDeserialize() وextractSchemaVersion() التحقق من التوافق قبل التحليل، بحيث يصبح عدم تطابق المخطط حالة مُكتشَفة بدلًا من تحميل بيانات تالفة. تساعد AstDocument::estimateTokenCount() في تقدير حجم المحتوى للمعالجة اللاحقة المُقيّدة بعدد الرموز.

الفئةالأعضاء الرئيسيونالدور
AstDocumenttoJson()، nodeCount()، estimateTokenCount()، CURRENT_SCHEMA_VERSIONالحاوية الجذرية؛ تتحقّق من نوع الجذر والمخطط
AstNodeaddChild()، children()، childCount()، totalNodeCount()، withBboxAndText()، deepClone()عقدة دلالية عَوْدية
NodeType (تعداد)Document، Heading، Table، Figure، FormField، …نوع العقدة الدلالي
AstNodeCollectionadd()، count()، isEmpty()، ofType()، toArray()مجموعة عُقَد قابلة للتكرار والتصفية حسب النوع
AstSerializerserialize()، deserialize()، canDeserialize()، extractSchemaVersion()استمرارية ⁨JSON⁩ مُصدَّرة الإصدار
BoundingBoxtoArray()، equals()كائن قيمة هندسي (مقارنة بإبسيلون)
NodeId / CitationAnchortoString()، equals()، toArray()هوية العقدة ومرتكز تتبّع المصدر
NodeAttributeSchemaالتحقق من السماتمخطط سمات العقدة

شغّل composer docs:generate-api-php -- --module=Ast لتوليد جدول ⁨PHPDoc⁩ الكامل.

ابنِ شجرة صغيرة، ثم سلسِلها.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstNode;
use NextPDF\Ast\AstSerializer;
use NextPDF\Ast\NodeType;
$root = new AstNode(NodeType::Document);
$heading = new AstNode(NodeType::Heading);
$root->addChild($heading);
$root->addChild(new AstNode(NodeType::Paragraph));
echo "Nodes: {$root->totalNodeCount()}\n";
$json = (new AstSerializer())->serialize(/* an AstDocument wrapping $root */);

نفّذ دورة ذهاب وإياب لشجرة ⁨AST⁩ المُخزَّنة بأسلوب دفاعي. تحقّق من توافق المخطط قبل فكّ تسلسل ⁨JSON⁩ غير موثوق به.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstDocument;
use NextPDF\Ast\AstSerializer;
use Psr\Log\LoggerInterface;
final readonly class AstStore
{
public function __construct(
private AstSerializer $serializer,
private LoggerInterface $logger,
) {}
public function load(string $json): ?AstDocument
{
if (!$this->serializer->canDeserialize($json)) {
$this->logger->warning('AST JSON schema incompatible; rejected.', [
'found_schema' => $this->serializer->extractSchemaVersion($json),
'expected' => AstDocument::CURRENT_SCHEMA_VERSION,
]);
return null;
}
return $this->serializer->deserialize($json);
}
}
  • يتطلّب AstDocument أن تكون العقدة الجذرية من نوع NodeType::Document. أي شجرة تستخدم جذرًا آخر ستطرح استثناءً عند الإنشاء.
  • تُعيد AstNode::withBboxAndText() وdeepClone() نسخًا جديدة. أمّا مُغيِّرات العقدة المتاحة (addChild()) فتُعدّل العقدة، بينما لا تفعل مساعِدات الاشتقاق ذلك. انتبه إلى الأسلوب الذي تستدعيه.
  • قيّد دائمًا deserialize() بـcanDeserialize() عند التعامل مع ⁨JSON⁩ وارد من مصدر خارجي. عدم تطابق إصدار المخطط حالة متوقّعة وقابلة للكشف.
  • estimateTokenCount() تقدير لتحديد حجم المعالجة اللاحقة، وليست عدًّا دقيقًا للرموز من مُرمِّز. لا تعامِلها على أنها مرجع موثوق.
  • BoundingBox::equals() مقارنة بإبسيلون (الافتراضي 0.001). المساواة الدقيقة بين أعداد الفاصلة العائمة ليست هي المعيار.

يبلغ تعقيد إنشاء الشجرة واجتيازها ⁨O⁩(⁨n⁩) بدلالة عدد العُقَد. ويكون التسلسل خطيًا بدلالة حجم الشجرة. ملف تعريف قابلية إعادة الإنتاج هو bitwise. تتسلسل الشجرة نفسها إلى البايتات نفسها من ⁨JSON⁩، مما يحافظ على استقرار المخطط بوصفه عقد استمرارية. يبقى عبء العمل المرجعي الافتراضي ضمن ميزانية 1500 ⁨ms⁩ للزمن الجداري / 64 ⁨MB⁩ للذروة بفارق مريح.

تحلّل AstSerializer::deserialize() ⁨JSON⁩ قد يكون مُخزَّنًا أو مُرسَلًا. تحقّق من التوافق بواسطة canDeserialize() أولًا. عامِل المحتوى النصي للشجرة بعد فكّ تسلسلها وسماتها على أنها سلاسل غير موثوقة عند إعادتها إلى التطبيق أو عرضها. لا تُجري الوحدة نفسها أي ⁨input/output⁩ (⁨I/O⁩) ولا تُضمِّن أي بيانات خارجية. راجع نموذج تهديد المحرّك في /modules/core/security/.

لا تؤكّد هذه الوحدة أي ادّعاء معياري يخص مواصفة ⁨PDF.⁩ شجرة ⁨AST⁩ الدلالية تجريد داخلي خاص بالمحرّك، ولا تُنفّذ نموذج مستند مُوحّدًا تجب الإشارة إلى بنوده. حيثما تُغذّي شجرة ⁨AST⁩ وسم إمكانية الوصول، يُوثَّق توافق المُخرَج مع ⁨PDF/UA⁩ و⁨tagged-PDF⁩ ويُتحقَّق منه في /modules/core/accessibility/ و/modules/core/conformance/، وليس هنا.