خطاها
هر شکستی یک استثنا پرتاب میکند. یک کلاس برای رد شدن، یکی برای نرسیدن پاسخ، و یک شناسهٔ درخواست روی هر خطای API.
گرفتن یک خطا
use OpenEmail\Exception\ApiException;use OpenEmail\Exception\NetworkException; $message = [ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your September invoice', 'text' => 'Invoice attached.',]; try { $client->emails->send($message);} catch (ApiException $error) { if ($error->isValidation()) { error_log($error->errorCode . ' ' . $error->param . ' ' . $error->getMessage()); } if ($error->isPermission()) { $book = $client->addresses->listAll(); error_log('this key may send as ' . implode(', ', array_column($book->addresses, 'address'))); } if ($error->isRateLimited()) { error_log('try again in ' . $error->retryAfterSeconds . ' seconds'); } error_log($error->status . ' ' . $error->requestId); throw $error;} catch (NetworkException $error) { if ($error->isTimeout()) { error_log('no answer in time'); } throw $error;}یک permission_error روی ارسال معمولاً به اسکوپ ارسالِ خودِ کلید برمیگردد، به دامنه یا آدرسی که به آن داده نشده، نه به فضای کاری، و به همین دلیل نمونه آنچه را addresses->listAll() دربارهٔ آدرسهایی که این کلید میتواند با آنها بفرستد میگوید در لاگ ثبت میکند.
هر نوع رد شدن زیرکلاس خودش را دارد، پس یک catch میتواند آنهایی را که رسیدگی میکند بر اساس کلاس انتخاب کند و بگذارد بقیه به بالا بروند.
use OpenEmail\Exception\AuthenticationException;use OpenEmail\Exception\OpenEmailException;use OpenEmail\Exception\PermissionException;use OpenEmail\Exception\ValidationException; $message = [ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your September invoice', 'text' => 'Invoice attached.',]; try { $client->emails->send($message);} catch (ValidationException $error) { error_log($error->param . ': ' . $error->getMessage());} catch (AuthenticationException|PermissionException $error) { error_log('the key cannot do this: ' . $error->errorCode); throw $error;} catch (OpenEmailException $error) { error_log($error::class . ': ' . $error->getMessage()); throw $error;}کلاسها
همهٔ کلاسها در OpenEmail\Exception قرار دارند.
| کلاس | چه زمانی |
|---|---|
| OpenEmailException | رابطی که هر استثنایی که بسته پرتاب میکند آن را پیادهسازی میکند، پس catch (OpenEmailException $error) همهٔ آنها را میگیرد، از جمله InvalidArgumentException. |
| ApiException | API پاسخ داده است، اما نه با موفقیت. حامل status، type، errorCode، param، docUrl، requestId، retryAfterSeconds، fields و body است. وقتی type برابر api_error باشد، مانند خطای سرور، با همین کلاس پرتاب میشود، و در غیر این صورت با زیرکلاسِ متناظر با type آن. RuntimeException را گسترش میدهد. |
| InvalidRequestException، AuthenticationException، PermissionException، NotFoundException، ConflictException، ValidationException و RateLimitException | زیرکلاسهای ApiException، یکی برای هر type: invalid_request_error، authentication_error، permission_error، not_found_error، conflict_error، validation_error و rate_limit_error. |
| NetworkException | هیچ پاسخی نرسیده است: DNS، TLS، اتصالی که رد یا قطع شده، یا پایان مهلت. getPrevious() استثنای زیرین را نگه میدارد، و وقتی دلیل، پایان مهلت بوده باشد isTimeout() برابر true است. RuntimeException را گسترش میدهد. |
| WebhookSignatureException | OpenEmail::verifyWebhookSignature() یک تحویل را رد کرده است. UnexpectedValueException را گسترش میدهد. |
| InvalidArgumentException | پیش از آنکه چیزی فرستاده شود پرتاب میشود: کلیدِ نبوده یا بدشکل، یک baseUrl: غیرقابلاستفاده، یک شناسهٔ خالی. InvalidArgumentException خودِ PHP را گسترش میدهد، چون یعنی خودِ فراخوانی نادرست است. |
آنچه یک ApiException با خود دارد
getMessage()string- جملهٔ خودِ API، نوشتهشده برای آدم، که مقدار خطاساز را در جایی که وجود دارد نام میبرد. شناسهای پایدار نیست، پس روی `errorCode` شاخه بزنید.
statusint or null- وضعیت HTTP پاسخ، که `getCode()` هم آن را برمیگرداند. فقط وقتی null است که یک پاسخ موفق در قالبی برگشته باشد که کلاینت نتوانسته بخواند.
typestring- یکی از هشت مقدار در `OpenEmail\Constants\ErrorTypes`، مجموعهای که ثابت است و بزرگتر نخواهد شد. وقتی بدنه هیچکدام را نام نبرد، از وضعیت استنتاج میشود.
errorCodestring- شکست مشخص، مانند `from_address_forbidden` یا `invalid_email_address`. نامش `errorCode` است، چون PHP نام `code` را برای عددی که `getCode()` برمیگرداند نگه میدارد. باز و افزودنی است، پس با کدی که نمیشناسید مثل `type` خودش رفتار کنید. وقتی بدنه پاکت خطای API نبوده باشد، برابر `unrecognised_response` است.
paramstring or null- فیلدی که رد شده، بهصورت مسیری نقطهدار مانند `to.0`، وقتی شکست فیلدی را نام ببرد.
docUrlstring or null- صفحهای دربارهٔ این شکست، وقتی API صفحهای را نام ببرد.
requestIdstring or null- شناسهای که سرور درخواست را با آن در لاگ ثبت کرده، از بدنه یا از سرآیند `x-request-id`.
retryAfterSecondsint, float or null- انتظاری که سرور در `Retry-After` خواسته، برحسب ثانیه، چه عدد فرستاده باشد چه تاریخ. وقتی چیزی نفرستاده باشد null است.
fieldsarray or null- یک آرایه برای هر مشکل، هرکدام با `key` و `error`، مانند `['key' => 'email', 'error' => 'email']` وقتی `forms->subscribe` پاسخها را با یک 422 `invalid_form_submission` رد کرده باشد. وقتی خطا هیچ موردی را فهرست نکند null است.
bodymixed- کل پاسخ خطا، تجزیهشده. وقتی خالی بوده یا JSON نبوده null است.
| متد | چه زمانی true است |
|---|---|
| isAuth() | type برابر authentication_error است، یک 401 Unauthorized: نبودن کلید، نوع نادرست اعتبارنامه، یا کلیدی که ما صادرش نکردهایم. |
| isPermission() | permission_error، یک 403 Forbidden: کلیدی واقعی بدون اسکوپ یا بدون آدرس From مورد نیازش. |
| isScopeMissing() | errorCode برابر insufficient_scope است، همان 403 که اسکوپ غایب را نام میبرد. |
| isInvalidRequest() | invalid_request_error، یک 400: درخواستی که قابل فهم نبوده است. پیامی فراتر از سقف اندازه بهصورت یک 422 message_too_large برمیگردد، پس isValidation() متدی است که آن را میگیرد. |
| isValidation() | validation_error، یک 422: اسکیما آن را نپذیرفته است و param نام فیلد را میگوید. |
| isNotFound() | not_found_error، یک 404 Not Found: چنین منبعی وجود ندارد. |
| isConflict() | conflict_error، یک 409 Conflict: منبع از نقطهای گذشته است که بتوان این کار را روی آن انجام داد. |
| isRateLimited() | rate_limit_error، یک 429 Too Many Requests. وقتی سرور مدتی را نام برده باشد، retryAfterSeconds آن انتظار را نگه میدارد. |
| isServerError() | status برابر 500 یا بالاتر است. اگر با پشتیبانی تماس گرفتید، requestId را ذکر کنید. |
| isRetryable() | status یکی از 408، 429، 500، 502، 503 یا 504 است. |
| isStepUpRequired() | errorCode برابر step_up_required است، همان 403 که توکن دسترسی OAuth پیش از یک تغییر حساس میگیرد تا وقتی شخص کدی را تأیید کند. |
بیشتر اینها type را میخوانند، یعنی نیمهٔ ثابتِ پاکت، و هر زیرکلاس نمایندهٔ یک type است. errorCode یک رشته باقی میماند، چون API تضمین میکند که باز و افزودنی است، پس با کدی که نمیشناسید مطابق type آن رفتار کنید. یک فهرست بسته، بهای خواندن یک حالت شکست تازه را به ارتقای بسته تبدیل میکرد.
بدنهای که پاکت خطای API نباشد باز هم یک ApiException پرتاب میکند، با type استنتاجشده از وضعیت و errorCode برابر unrecognised_response. پاسخ موفقی که بدنهاش JSON نباشد نیز همین استثنا را پرتاب میکند.
isRetryable() وضعیت را توصیف میکند، نه فراخوانی شما را. فراخوانیای که تکرارش بیخطر است، تا زمانی که استثنا پرتاب کند دوباره تلاش شده است، و یک 429 مانند send_quota_exceeded یا ai_quota_exceeded تا وقتی سهمیهاش بازنشانی نشود به همان شکل شکست میخورد، پس آن را به یک شخص نشان دهید، نه اینکه روی آن حلقه بزنید.
استثنایی که کلاینت HTTP شما پرتاب کند، پس از مصرف شدن تلاشهای دوبارهای که فراخوانی اجازه میدهد، به یک NetworkException تبدیل میشود که استثنای اصلی بهصورت getPrevious() در آن است. یک LogicException و هر Error، مانند TypeError، یعنی باگی در خود کلاینت، پس بیتغییر پرتاب میشوند و هرگز دوباره تلاش نمیشوند. Psr18HttpClient از استثنای کلاینتی که در بر میگیرد فقط پیامش را نگه میدارد، چون آن استثنا درخواست و سرآیند Authorization آن را در خود دارد.
requestId
هر ApiException شناسهٔ درخواستی را که سرور فرستاده است با خود دارد، از بدنهٔ خطا یا از سرآیند x-request-id، و تنها چیزی است که شکست شما را به یک سطر در لاگ سرور گره میزند. یک پاسخ موفق فقط بدنهٔ تجزیهشده را برمیگرداند، پس روی آن شناسهٔ درخواستی برای خواندن وجود ندارد.