البيانات الوصفية: بناء حزمة XMP وقراءتها تدفقيًا
نظرة سريعة
قسم بعنوان «نظرة سريعة»وحدة البيانات الوصفية هي طبقة منصّة البيانات الوصفية القابلة للتوسعة (XMP) في المحرّك. فهي تبني حزمة XMP التي يحملها ملف بصيغة المستندات المحمولة (PDF) بوصفها دفق بيانات وصفية، وتقرأ حزمة موجودة من دون تحميل المستند كاملًا في الذاكرة، وتُصدِر امتداد XMP لسجلّ تدقيق المحرّك.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/core:^3نظرة عامة مفاهيمية
قسم بعنوان «نظرة عامة مفاهيمية»يخزّن PDF البيانات الوصفية على مستوى المستند بوصفها حزمة XMP داخل دفق بيانات وصفية مرفق بفهرس المستند، كما يوضّح ISO 32000-2 §14.3. تتولّى هذه الوحدة إنتاج تلك الحزمة واستهلاكها. واجهتها صغيرة ومركّزة عن قصد: ثلاثة أصناف ضمن NextPDF\Metadata\Xmp.
ينتج XmpMetadataBuilder الحزمة. فهو يسلسل مجموعة خصائص في مستند XMP صحيح البنية ومغلّف بتعليمات المعالجة القياسية <?xpacket?>. ويستخدم المعرّف الفريد عالميًا (GUID) المعياري للحزمة وعلامة ترتيب البايتات اللذين تحدّدهما مواصفة XMP. الناتج هو سلسلة البايتات التي يضمّنها Writer بوصفها دفق البيانات الوصفية، أي تمثيل XMP داخل PDF الموصوف في §14.3.
يستهلك XmpStreamReader الحزمة. وهو مصمَّم للتعامل مع المدخلات العدائية. يُقرأ المصدر على شكل أجزاء بحجم 64 KB إلى ملف مؤقت محدود قبل التحليل. ويفرض القارئ حدًّا أقصى إجماليًا للبايتات أثناء تلك الكتابة. يُضبَط محمّل الكيانات في libxml على null أثناء التحليل ثم يُستعاد بعده. يؤدّي وجود DOCTYPE إلى رفض صارم. يُعيد iterateProperties() مولّدًا يُنتج صفوف (namespaceUri, localName, textContent) لكل عنصر طرفي من دون بناء الشجرة كاملة في الذاكرة؛ ولا يبقى مقيمًا في المحلّل في أي لحظة إلا العنصر الحالي وعقدة نصّه. تُطلِق الحزمة المفرطة الحجم PacketTooLargeException؛ بينما تُطلِق لغة الترميز القابلة للتوسعة (XML) المشوّهة، أو وجود DOCTYPE، أو المدخلات غير المرمّزة بـ UTF-8 الاستثناء InvalidConfigException.
يمثّل XmpAuditFieldEmitter الامتداد الخاص بالمحرّك. فهو يصيّر AuditReport في حقل XMP مخصّص ضمن مساحة الأسماء nextpdfAudit، بحيث يرافق تدقيق مطابقة المستند الملفَ بوصفه XMP متوافقًا مع المعايير، بدلًا من أن يكون ملفًا جانبيًا. إنّ AuditReport الذي يصيّره ليس من إنتاج المُصدِر. يفعّل المستدعي الإثراء بتشغيل عملية تصيير في وضع CssRenderingMode::Audit مع auditCollector يوفّره المستدعي ويُهيَّأ عبر Config(auditCollector: ...). المجمّع مدفوع من المستدعي: فالمستدعي يغذّيه، والمُصدِر يصيّر كل ما جمعه. وهو أحدث من واجهة XMP الأساسية (@since 5.4.0). أمّا الباني والقارئ فهما @since 2.0.0.
واجهة API
قسم بعنوان «واجهة API»| الصنف | الأعضاء الرئيسيون | الدور |
|---|---|---|
XmpMetadataBuilder | build(): string، XPACKET_GUID، XPACKET_BOM | يسلسل مجموعة خصائص في حزمة XMP (@since 2.0.0) |
XmpStreamReader | iterateProperties(mixed $source, int $byteCap = DEFAULT_BYTE_CAP): \Generator، DEFAULT_BYTE_CAP | قارئ XMP محدود وتدفقي ويرفض DOCTYPE (@since 2.0.0) |
PacketTooLargeException | يمتدّ من NextPdfException | يُطلَق عندما تتجاوز حزمة XMP الحدّ الأقصى للبايتات (@since 2.0.0) |
XmpAuditFieldEmitter | render(?AuditReport $report): string، NAMESPACE_URI | يصيّر سجلّ التدقيق بوصفه حقل XMP مخصّص (@since 5.4.0) |
شغّل composer docs:generate-api-php -- --module=Metadata لإنشاء جدول PHPDoc الكامل.
مثال برمجي — البدء السريع
قسم بعنوان «مثال برمجي — البدء السريع»اقرأ الخصائص تدفقيًا من حزمة XMP موجودة ضمن حدّ أقصى صريح للبايتات.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Metadata\Xmp\XmpStreamReader;
$reader = new XmpStreamReader();
foreach ($reader->iterateProperties(file_get_contents('/srv/in/xmp.xml'), byteCap: 1_048_576) as [$ns, $name, $value]) { printf("%s:%s = %s\n", $ns, $name, $value);}مثال برمجي — الإنتاج
قسم بعنوان «مثال برمجي — الإنتاج»اقرأ الحزمة بأسلوب دفاعي، وحوّل إخفاقات الوحدة المُنمَّطة إلى نتيجة على مستوى التطبيق بدلًا من ترك أعطال المحلّل الخام تنفلت.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Exception\InvalidConfigException;use NextPDF\Metadata\Xmp\PacketTooLargeException;use NextPDF\Metadata\Xmp\XmpStreamReader;use Psr\Log\LoggerInterface;
final readonly class XmpIngestService{ public function __construct( private XmpStreamReader $reader, private LoggerInterface $logger, ) {}
/** * @param resource|string $source A stream resource or XMP byte string. * * @return array<string, string> Flattened "ns:name" => value map. */ public function ingest(mixed $source): array { $properties = [];
try { // Cap untrusted XMP at 4 MB regardless of the 1 GiB default. foreach ($this->reader->iterateProperties($source, byteCap: 4_194_304) as [$ns, $name, $value]) { $properties["{$ns}:{$name}"] = $value; } } catch (PacketTooLargeException $e) { $this->logger->warning('XMP packet exceeded ingest cap; rejected.', ['error' => $e->getMessage()]);
return []; } catch (InvalidConfigException $e) { $this->logger->warning('XMP packet malformed or unsafe; rejected.', ['error' => $e->getMessage()]);
return []; }
return $properties; }}الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- يرفض
XmpStreamReaderأي DOCTYPE رفضًا قاطعًا. هذا دفاع ضدّ هجوم الكيان الخارجي في XML (XXE)، وليس تحسينًا تجميليًا للتحقق؛ فالحزمة التي تحتاج إلى DOCTYPE لا تُقبَل. طهّرها في مرحلة سابقة. - يبلغ الحدّ الأقصى للبايتات افتراضيًا 1 GiB (
DEFAULT_BYTE_CAP). هذا الافتراضي سقف، وليس توصية. مرّرbyteCapأضيق للمدخلات غير الموثوقة. iterateProperties()مولّد. استهلكه مرة واحدة؛ فالمرور عليه مرتين لا يُعيد التشغيل.- يضبط القارئ محمّل الكيانات في libxml على null أثناء التحليل ثم يستعيده. لا تشغّله بالتزامن مع أي تحليل آخر قائم على libxml في الطلب نفسه إذا كان ذلك التحليل يعتمد على محمّل الكيانات.
- إنّ
XmpAuditFieldEmitter::render(null)صالح ويُنتج تصييرًا فارغًا؛ كما أنّAuditReportالذي تكون قيمته null يعني “لا تدقيق”، لا خطأً.
الأداء
قسم بعنوان «الأداء»الباني خطّي بالنسبة إلى عدد الخصائص. يُهيمن أطول نصّ متّصل مفرد على استهلاك القارئ للذاكرة، لا حجم المستند، لأنّ العنصر الحالي وحده يبقى مقيمًا في المحلّل؛ فالحزم الكبيرة تُقرأ تدفقيًا بدلًا من تحميلها في الذاكرة. يقع حمل العمل المرجعي الافتراضي ضمن ميزانية قدرها 1500 ms زمن جداري / 64 MB ذروة. سمة قابلية إعادة الإنتاج هي structural: فحزمة XMP تسجّل طوابع زمنية للتعديل. قد يختلف بناءان للبيانات الوصفية المنطقية نفسها في تلك الحقول، بينما تبقى بنيتهما متطابقة.
ملاحظات أمنية
قسم بعنوان «ملاحظات أمنية»يحلّل XmpStreamReader XML غير موثوق ومحصَّن وفقًا لذلك. يقيّد التقطيع التدفقي مع حدّ أقصى مفروض للبايتات هجوم حجب الخدمة القائم على تضخيم الذاكرة. رفض DOCTYPE يسدّ ثغرة XXE. يحجب LIBXML_NONET استعلام الكيانات عبر الشبكة. تُرفَض المدخلات غير المرمّزة بـ UTF-8. ومع ذلك اضبط byteCap بما يلائم النشر لأي حزمة مصدرها خارجي، بدلًا من الاعتماد على الافتراضي البالغ غيغابايت. عامِل قيم خصائص XMP بوصفها سلاسل غير موثوقة عند عودتها إلى التطبيق. راجع نموذج تهديدات المحرّك في /modules/core/security/.
المطابقة
قسم بعنوان «المطابقة»الحزمة التي ينتجها XmpMetadataBuilder هي تمثيل دفق البيانات الوصفية XMP داخل PDF المعرّف في ISO 32000-2 §14.3 (). أمّا صيغة تسلسل XMP نفسها فتحكمها مواصفة XMP (ISO 16684-1)، وهي ليست ضمن مجموعة الاستشهادات القابلة للتحقق. يُشار إلى ذلك المتطلب بالرقم، لا بتثبيت مقطعي. هذه حقائق تنفيذية ينتجها src/Metadata/Xmp/ وتختبرها tests/Unit/Metadata/Xmp/. يُتحقّق من مطابقة البيانات الوصفية من الطرف إلى الطرف لأي ملف تعريف (PDF/A، PDF/UA) عبر مجموعات الأوراكل والمجموعات الذهبية الموصوفة في /modules/core/conformance/.
انظر أيضًا
قسم بعنوان «انظر أيضًا»- وحدة Document — شجرة DPart المقترنة ببيانات أجزاء المستند الوصفية (DPM).
- وحدة Audit — تنتج
AuditReportالذي يصيّره المُصدِر. - وحدة Writer — تضمّن الحزمة بوصفها دفق بيانات وصفية.
- نظرة عامة على المطابقة
- نموذج أمان المحرّك