پرش به مستندات
PHP

صفحه‌بندی

یک صفحه، همهٔ صفحه‌ها، یا هر بار یک مورد، روی هر فهرستی که صفحه‌بندی دارد.

list، listAll و iterate

هر فهرستی که صفحه‌بندی دارد سه متد دارد. list یک صفحه را می‌گیرد و یک OpenEmail\Result\Page برمی‌گرداند. listAll cursor را در همهٔ صفحه‌ها دنبال می‌کند و یک آرایه برمی‌گرداند. iterate همان صفحه‌ها را هر بار یک مورد می‌پیماید و یک Generator برمی‌گرداند، که صفحهٔ بعد را فقط وقتی به آن برسید می‌گیرد. هر سه، فیلترهای فهرست، limit:، cursor: و apiKey: را می‌گیرند.

three_ways.php
$page = $client->emails->list(status: 'failed', limit: 50); foreach ($page as $email) {    echo $email['id'], ' ', $email['lastError'] ?? '', PHP_EOL;} $failures = $client->emails->listAll(status: 'failed'); foreach ($client->emails->iterate(status: 'failed') as $email) {    echo $email['id'], PHP_EOL;} echo count($failures), ' ', $page->hasMore ? 'more to come' : 'that is all', PHP_EOL;

همین سه نام هر جا که یک فضای نام بیش از یک فهرست دارد تکرار می‌شوند، و به نام فهرستی که می‌پیمایند نام‌گذاری شده‌اند: listEvents، listAllEvents و iterateEvents روی emails، listDeliveries، listAllDeliveries و iterateDeliveries روی webhooks، و مانند آن.

OpenEmail\Result\Page

itemsarray
ردیف‌های این صفحه، بیرون‌کشیده از پاکت `data` در API، هرکدام یک آرایهٔ انجمنی. وقتی صفحه چیزی ندارد خالی است.
hasMorebool
اینکه آیا صفحهٔ دیگری در پی می‌آید. وقتی API هیچ `hasMore` نفرستد، دقیقاً وقتی true است که یک `nextCursor` وجود داشته باشد.
nextCursorstring or null
آنچه برای صفحهٔ بعد باید به‌عنوان `cursor:` پس بدهید، و روی آخرین صفحه null است.

صفحه تغییرناپذیر است: ویژگی‌هایش readonly هستند. همچنین IteratorAggregate و Countable است، پس foreach ($page as $item) ردیف‌هایش را می‌پیماید و count($page) آن‌ها را می‌شمارد.

یک Generator

iterate تا وقتی پیمایشش را شروع نکنید چیزی نمی‌خواهد، و صفحهٔ بعد را فقط وقتی می‌خواهد که همهٔ موردهای صفحهٔ کنونی yield شده باشند، پس هر چیزی که زودتر بایستد درخواست‌ها را هم متوقف می‌کند: break پیمایش را پایان می‌دهد، و بازگشتن از تابعی که حلقه را در بر دارد هم همین‌طور.

generator.php
$latest = []; foreach ($client->emails->iterate(status: 'failed', limit: 100) as $email) {    $latest[] = $email;     if (count($latest) === 10) {        break;    }} $invoice = null; foreach ($client->emails->iterate(status: 'failed') as $email) {    if (($email['tags']['invoice'] ?? null) === 'inv_2026_09_4192') {        $invoice = $email;         break;    }} echo count($latest), ' ', $invoice['id'] ?? 'not found', PHP_EOL;

هر چیزی که به همهٔ موردها نیاز دارد، مانند iterator_to_array() که روی Generator فراخوانی شود، پیش از بازگشت همهٔ صفحه‌ها را می‌خواند، همان‌طور که listAll می‌خواند.

یک Generator را فقط یک بار می‌توان پیمود. پیمودن دوباره‌اش استثنا پرتاب می‌کند، پس وقتی دوباره به موردها نیاز دارید iterate را دوباره فراخوانی کنید، یا به‌جای Generator خود موردها را نگه دارید.

ادامه دادن از یک cursor

cursor مبهم (opaque) است. nextCursor آخرین صفحه‌ای را که خوانده‌اید نگه دارید و آن را به‌عنوان cursor: پس بدهید تا از همان‌جا ادامه دهید، در درخواستی بعدی یا در فرایندی دیگر. listAll و iterate هم cursor: را می‌گیرند و پیمایششان را پس از آن آغاز می‌کنند.

resume.php
$firstPage = $client->emails->list(status: 'failed', limit: 25);$saved = $firstPage->nextCursor; if ($saved !== null) {    $rest = $client->emails->listAll(status: 'failed', cursor: $saved);    echo count($rest), PHP_EOL;}

هر cursor به فهرست و فیلترهایی تعلق دارد که از آن‌ها آمده است، پس همان فیلترها را همراهش بفرستید. cursorی که فهرست نتواند جایش را پیدا کند با invalid_cursor رد می‌شود، و راه‌حل در آن صورت این است که بدون cursor از نو آغاز کنید.

limit:

limit: اندازهٔ هر صفحه است، نه یک مجموع. روی list تعداد ردیف‌هایی است که برمی‌گردند. روی listAll و iterate تعدادی است که هر درخواست می‌خواهد، پس مقدار بزرگ‌تر یعنی رفت‌وبرگشت‌های کمتر برای همان ردیف‌ها. هر فهرست بازه و پیش‌فرض خودش را دارد، بیشتر وقت‌ها 1 تا 100 با 25 وقتی چیزی نفرستید، و مقدار بیرون از بازه رد می‌شود، نه اینکه به مرز بازه بریده شود. مرجع هر فهرست بازه‌اش را می‌گوید.

پیمایش کی می‌ایستد

  • وقتی صفحه‌ای بگوید hasMore برابر false است.
  • وقتی صفحه‌ای هیچ nextCursor نداشته باشد، چون صفحه‌ای که ادعای ادامه کند اما هیچ cursorی را نام نبرد تا ابد حلقه می‌زد.
  • وقتی API یک cursor را پس بدهد که پیمایش پیش‌تر دنبالش کرده است، به همان دلیل.

هر صفحه یک GET است، پس مانند هر خواندنی پیش از آنکه چیزی پرتاب شود جداگانه دوباره تلاش می‌شود. شکستی که پس از تلاش‌های دوباره باقی بماند از listAll بیرون پرتاب می‌شود، و موردهایی که تا آن زمان گرفته شده‌اند دور ریخته می‌شوند. در iterate، موردهای صفحه‌های پیشین تا آن زمان yield شده‌اند، پس کاری را که حلقه انجام می‌دهد طوری بنویسید که دو بار اجرا شدنش بی‌خطر باشد، یا با list صفحه‌بندی کنید و هر nextCursor را نگه دارید تا تلاش دوم بتواند از همان جایی که تلاش نخست ایستاد آغاز شود.

رشته‌ها و پیش‌نویس‌ها

threads->list و drafts->list، همراه با listAll و iterate آن‌ها، به‌جای cursor با pageToken و nextPageToken در API صفحه‌بندی می‌کنند. کلاینت این تفاوت را پنهان می‌کند: توکن را به‌عنوان cursor: بدهید و آن را از nextCursor بخوانید.

page_token.php
$page = $client->threads->list(folder: 'inbox', limit: 50);$later = $page->hasMore ? $client->threads->list(folder: 'inbox', limit: 50, cursor: $page->nextCursor) : null; echo count($page), ' ', $later === null ? 0 : count($later), PHP_EOL;

سرور هر بار که صفحه‌ای پر برگردد یک توکن پیشنهاد می‌دهد، پس hasMore ممکن است روی صفحه‌ای که در عمل آخرین صفحه است true باشد، و فراخوانی بعدی آنگاه هیچ موردی برنمی‌گرداند.

صفحه‌هایی که چیزهای بیشتری دارند

چند فهرست با چیزی بیش از ردیف‌ها پاسخ می‌دهند، و به‌جای Page یک شیء مخصوص خود از OpenEmail\Result برمی‌گردانند. هرکدام تغییرناپذیر است، روی ردیف‌هایش IteratorAggregate است و Countable هم هست.

متدچه برمی‌گرداندآنچه می‌افزاید
addresses->listAddressBookPageaddresses به‌جای items، به‌علاوهٔ unrestricted و domains، همراه با hasMore و nextCursor.
addresses->listAllAddressBookهمهٔ آدرس‌ها در addresses، با unrestricted و domains همان‌طور که آخرین صفحه گزارششان کرده است. این تنها listAll است که به‌جای یک آرایه کل دفترچهٔ آدرس را برمی‌گرداند. addresses->iterate فقط خود آدرس‌ها را yield می‌کند.
contacts->listPeoplePeoplePageseen، که وقتی کلید نمی‌تواند آدرس‌های دیده‌شده در ایمیل‌ها را بخواند false است. listAllPeople و iteratePeople فقط خود افراد را برمی‌گردانند.
tempMail->listMessagesTempMessagesPageexpiresAt، زمانی که صندوق منقضی می‌شود. listAllMessages و iterateMessages فقط خود پیام‌ها را برمی‌گردانند.
templates->listSendsTemplateSendsبه‌جای cursor با شمارهٔ صفحه صفحه‌بندی می‌شود: items، total، page و pageSize. صفحهٔ بعد را با page: بخواهید.
emails->sendBatchBatchResultصفحه نیست: items، یکی برای هر پیامی که فرستادید، همراه با شمارهای sent و failed.

فهرست‌هایی که آرایهٔ خودِ API را برمی‌گردانند

برخی فهرست‌ها با offset، با شمارهٔ صفحه یا با cursor عددیِ مخصوص خودشان صفحه‌بندی می‌کنند، و به‌جای یک Page، بدنهٔ تجزیه‌شده را همان‌طور که آمده برمی‌گردانند: یک آرایه با data. listAll یا iterate ندارند، پس صفحه‌بندی‌شان را خودتان انجام می‌دهید.

متدصفحه‌بندی باآنچه برمی‌گردد
exports->listlimit: و offset:data، total و hasMore.
imports->listFailuresafter: و limit:data، و nextCursor، یک عدد صحیح که باید به‌عنوان after: پس بدهید و روی آخرین صفحه null است.
subscriptions->list و subscriptions->listDomainslimit: و offset:data، total، counts و hasMore.
billing->listInvoicespage: و limit:data، total، page، limit، hasMore و metered.
offset_paging.php
$offset = 0; do {    $batch = $client->subscriptions->list(status: 'active', limit: 50, offset: $offset);     foreach ($batch['data'] as $row) {        echo $row['senderEmail'], ' ', $row['total'], PHP_EOL;    }     $offset += count($batch['data']);} while ($batch['hasMore'] && $batch['data'] !== []);

فهرستی که اصلاً صفحه‌بندی ندارد، مانند languages->list، labels->listColors یا roles->listPermissions، ردیف‌هایش را مستقیماً به‌صورت یک فهرست ساده برمی‌گرداند.