الملفات
`files->list` و`listAll` و`iterate` و`get` و`download` و`listLinks` و`listAllLinks` و`iterateLinks` و`createLink` و`revokeLink` و`revokeAllLinks` و`stats` و`upload` و`delete` و`deleteMany`.
كل الدوالّ
use OpenEmail\Constants\FileKinds;use OpenEmail\Constants\FileSorts; $largest = $client->files->list(kind: FileKinds::PDF, sort: FileSorts::LARGEST, limit: 1); foreach ($largest as $file) { foreach ($client->files->listAllLinks($file['id']) as $link) { echo $link['url'], ' ', $link['downloads'], PHP_EOL; } file_put_contents(basename($file['filename']), $client->files->download($file['id']));} $stats = $client->files->stats();echo $stats['totals']['files'], ' ', $stats['uploaded']['bytes'], PHP_EOL;كل ملف يحتفظ به صندوق البريد، من المرفقات المُرسلة والمُستلمة والملفات المرفوعة إليه: أي صفحة الملفات في التطبيق. يعيد download البايتات في صورة سلسلة نصية، يحفظها file_put_contents دون تغيير. والروابط هي روابط التنزيل التي خرج بها الملف، مع عدد مرات جلب كل منها، وstats هو تبويب التحليلات.
يأخذ list وlistAll وiterate الوسائط q: وkind: وsort:، وكذلك direction: (inbound أو outbound أو uploaded)، وaddress: أي ملفات عنوان واحد مع مقارنة لا تراعي حالة الأحرف، وsince: وuntil: بوصفهما DateTimeInterface أو سلسلة نصية بصيغة ISO 8601. ويشمل since: لحظته ويتوقف until: قبل لحظته. ويُرسَل DateTimeInterface بتوقيت UTC، والسلسلة التي تحمل تاريخًا مجردًا مثل 2026-09-01 تُقرأ على أنها منتصف الليل UTC.
يبحث q: في اسم الملف ونوعه. وقيمة kind: هي image أو pdf أو audio أو video أو text، وقيمة sort: هي newest، وهي الافتراضية، أو oldest أو largest أو name. ويسمّي OpenEmail\Constants\FileKinds وFileSorts وFileDirections الخيارات. ويعيد list صفحة OpenEmail\Result\Page واحدة، ويعيد listAll كل الملفات في مصفوفة واحدة. ويعيد iterate كائن Generator يسلّم ملفًا واحدًا في كل مرة ولا يجلب الصفحة التالية إلا حين تحتاجها الحلقة.
تحتاج القراءة إلى files:read، ويحتاج الرفع والحذف ونشر الروابط أو سحبها إلى files:write، الذي يشمل files:read. والمفتاح الذي أُنشئ قبل أن تصير للملفات نطاقات خاصة بها مُنح النطاقات المقابلة. والمفتاح الذي ينقصه النطاق يُرفض بـ 403 insufficient_scope، يُرمى في صورة PermissionException تكون قيمة isScopeMissing() فيه true. والمفتاح المقصور على عناوين أو نطاقات بعينها لا يرى إلا الملفات التي وصلت إليها، فلا يرى الملفات المرفوعة لمساحة العمل كلها.
يحتفظ download بالملف كله في الذاكرة، فاجلب الملفات الكبيرة جدًا واحدًا تلو الآخر. ومرّر الاسم عبر basename() قبل الكتابة به، كما يفعل المثال، لأنه جاء ممن أرسل الملف.
الروابط العامة
use OpenEmail\Constants\FileDirections; $uploads = $client->files->listAll(direction: FileDirections::UPLOADED, since: new \DateTimeImmutable('2026-09-01T00:00:00Z'));$file = $uploads[0] ?? null; if ($file !== null) { $link = $client->files->createLink($file['id'], domain: 'acme.com'); echo $link['url'], PHP_EOL; $client->files->revokeLink($file['id'], $link['id']);}ينشر createLink ملفًا على رابط تنزيل عام يُفتح دون تسجيل دخول، ويعيد الرابط. ويقع الرابط على مضيف الملفات لـ domain: حين يكون لذلك النطاق مضيف، مثل files.acme.com، وإلا فعلى مضيف الملفات للعنوان الذي ينتمي إليه الملف، وإلا فعلى عنوان API. والملف الذي وصل أو خرج مع رسالة يُنسخ أولًا إلى تخزين عام، ويُرفض البرنامج أو السكربت بـ 422 file_unshareable، يُرمى في صورة ValidationException.
كل createLink يصنع رابطًا جديدًا، لذا لا تُعاد محاولته أبدًا بعد ضياع الجواب: ابحث عن الرابط أولًا عبر listLinks. ويوقف revokeLink الرابط نهائيًا، بما في ذلك في البريد الذي خرج من قبل، ويعيده مع ضبط revokedAt. وسحب الرابط مرتين آمن، فيعيد العميل محاولته كما يفعل مع القراءة. والرابط غير المعروف يعطي 404، يُرمى في صورة NotFoundException. ويوقف revokeAllLinks كل روابط ملف واحد دفعة واحدة ويبلّغ عن عددها في revoked.
الرفع والحذف
$report = "month,sent\n2026-09,1200\n";$upload = $client->files->upload($report, filename: 'report.csv', contentType: 'text/csv'); if ($upload['deletable']) { $client->files->delete($upload['id']);} $logo = $client->files->upload(new \SplFileInfo('logo.svg'));echo $logo['filename'], ' ', $logo['mimeType'], PHP_EOL; $result = $client->files->deleteMany(['file_0c4e7a91d2b84f63a5e19b7d', 'file_6bb640f5b99e47deb758f1f5']); foreach ($result['kept'] as $kept) { echo $kept['filename'], ' ', $kept['reason'], PHP_EOL;}يخزّن upload البايتات كما هي، حتى 100 ميغابايت، بالاسم الوارد في filename:، ويعيد الملف. ويرفقه الإرسال بوصفه ['fileId' => $upload['id']] في attachments الخاصة بـ emails->send. مرّر contentType:، أو بيانات تحمل نوعها بنفسها، وكل ما عدا ذلك يُخزَّن بوصفه application/octet-stream. ولا يُستنتج النوع أبدًا من filename:، فسلسلة البايتات تحتاج إلى contentType: كي تُخزَّن بأي نوع آخر. ولا تُعاد محاولته أبدًا، لأن المحاولة الثانية ستخزّن نسخة ثانية.
البيانات سلسلة من البايتات، أو SplFileInfo، أو تدفق من fopen()، أو تدفق PSR-7 أو ملف مرفوع. ولا يحتاج SplFileInfo ولا التدفق المفتوح على ملف إلى أيٍّ من الوسيطين: فالاسم هو اسم الملف نفسه، والنوع يأتي من امتداده حين تعرفه الحزمة، مثل .pdf أو .png. والملف المرفوع في Laravel أو Symfony، وملف PSR-7 المرفوع، يجلبان الاسم والنوع اللذين أرسلهما المتصفح. أما السلسلة النصية فلا تحمل اسمًا، فالسلسلة التي بلا filename: ترمي InvalidArgumentException قبل إرسال أي شيء.
قد يستمر الرفع 600 ثانية، أو مدة timeout الخاصة بالعميل إن كانت أطول، ويضبط timeout: حدًا آخر لاستدعاء واحد. والعميل المبني مع timeout: 0 ينتظر ما يستغرقه الرفع مهما طال.
لا يُحذف إلا ملف مرفوع لا يعتمد عليه شيء، ويذكر deletable وusage في كل ملف ذلك مسبقًا. ويرفض delete أي ملف آخر بـ 409 file_in_use، يُرمى في صورة ConflictException. ويأخذ deleteMany حتى 100 معرّف، ويحذف ما يستطيع، ويبلّغ عن الباقي في kept، كلٌّ مع سببه، وفي missing. ولا تُعاد محاولة أيٍّ منهما. وخطأ 404 في delete ثانٍ بعد ضياع الجواب يعني أن الأول نجح، وdeleteMany الثاني يبلّغ عن تلك الملفات في missing.