문서로 건너뛰기
PHP

구성

클라이언트를 만드는 방법, 모든 옵션, 그리고 요청을 보내기 전에 거부하는 것들.

옵션

clients.php
use OpenEmail\OpenEmail; $client = new OpenEmail(); OpenEmail::init(timeout: 10);OpenEmail::getClient()->me->ping(); $billing = new OpenEmail(apiKey: (string) getenv('OPENEMAIL_BILLING_API_KEY')); echo $client->mode, ' ', $billing->mode, PHP_EOL;
진입점제공하는 것
new OpenEmail(...)전달한 명명된 인자로 만든 클라이언트입니다. 생략한 값은 환경 변수에서 읽습니다. 자격 증명을 전달하지 않으면 OPENEMAIL_API_KEY의 키나 OPENEMAIL_ACCESS_TOKEN의 토큰을, 기본 URL을 전달하지 않으면 OPENEMAIL_BASE_URL을 읽습니다.
OpenEmail::createClient(...)new OpenEmail(...)와 같은 클라이언트로, 팩토리를 호출하는 편을 선호하는 코드를 위한 것입니다.
OpenEmail::init(...)클라이언트를 만들어 공유 클라이언트로 보관하고 반환합니다. 같은 명명된 인자를 받습니다.
OpenEmail::getClient()프로세스 어디에서나 쓸 수 있는 공유 클라이언트입니다. init 전에 호출하면 첫 호출 시 환경 변수로 클라이언트를 만듭니다.
OpenEmail::resetClient()공유 클라이언트를 버리므로 다음 getClient()가 새 클라이언트를 만듭니다. 테스트에서 케이스 사이에 필요한 동작입니다.

getenv()를 문자열로 캐스팅하는 것은 의도된 것입니다. 설정되지 않은 변수는 빈 키가 되고, 클라이언트는 필요한 변수의 이름을 밝히는 메시지와 함께 이를 거부합니다. 반면 null이었다면 조용히 OPENEMAIL_API_KEY로 대체되었을 것입니다.

options.php
use OpenEmail\Http\CurlHttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(    apiKey: (string) getenv('OPENEMAIL_API_KEY'),    baseUrl: 'https://api.openemail.uk',    httpClient: new CurlHttpClient(),    maxRetries: 2,    timeout: 30,    userAgent: 'billing-service/1.4',    headers: ['X-Team' => 'billing'],    disableUpdateNotice: true,);
옵션기본값설명
apiKey:OPENEMAIL_API_KEYoe_live_ 또는 oe_test_로 시작해야 합니다. apiKey:와 accessToken: 중 어느 것도 전달하지 않았을 때만 환경 변수에서 읽습니다.
accessToken:OPENEMAIL_ACCESS_TOKENOAuth 액세스 토큰, 또는 토큰을 반환하는 호출 가능 객체입니다. 아래의 OAuth 액세스 토큰을 참고하세요. 키나 토큰 중 하나만 전달하고, 둘 다 전달하지 마세요.
baseUrl:https://api.openemail.uk또는 OPENEMAIL_BASE_URL. 끝의 슬래시는 제거되며, 호스트만 적힌 값 앞에는 https://가, 이 컴퓨터의 호스트(localhost, 127.x.x.x 주소, ::1) 앞에는 http://가 붙습니다. 자격 증명은 그 밖의 호스트로 암호화되지 않은 http를 통해 보내지지 않으며, 0.0.0.0이나 [::]는 클라이언트를 만들 때 거부됩니다. 이 주소들은 서버가 수신 대기하는 주소이지, 요청을 보낼 주소가 아니기 때문입니다.
timeout:30호출당이 아니라 시도당 초 단위이며, 연결과 응답 전체를 읽는 시간까지 포함합니다. 0이면 꺼집니다. files->upload는 그 호출에 timeout:을 전달하지 않는 한 최소 600초를 기다립니다.
maxRetries:2반복해도 안전한 호출에 한해, 첫 시도 이후의 추가 시도 횟수입니다. 호출별이 아니라 클라이언트에 설정합니다. 0이면 재시도가 꺼집니다.
httpClient:CurlHttpClientHTTP 계층입니다. OpenEmail\Http\HttpClient를 구현하는 것이면 무엇이든 되며, Guzzle이나 Symfony HttpClient를 감싼 Psr18HttpClient, 또는 테스트용 가짜 구현이 그 예입니다. 각각은 HTTP 클라이언트 페이지에서 다룹니다.
headers:[]모든 요청에 전송됩니다.
userAgent:openemail-php/<version>모든 요청에 전송됩니다.
disableUpdateNotice:falsePackagist에 새 버전이 있는지 프로세스당 한 번 확인하는 절차를 건너뜁니다. 이 확인은 명령줄에서 표준 출력이 터미널일 때만 실행되며, OPENEMAIL_DISABLE_UPDATE_NOTICE로도 끌 수 있습니다.

환경 변수

변수하는 일
OPENEMAIL_API_KEYapiKey:와 accessToken: 중 어느 것도 전달하지 않았을 때 클라이언트가 사용하는 키입니다.
OPENEMAIL_ACCESS_TOKENOAuth 액세스 토큰으로, 두 자격 증명 중 어느 것도 전달하지 않고 OPENEMAIL_API_KEY도 설정되지 않았을 때만 읽습니다. 즉 환경 변수의 키가 우선합니다.
OPENEMAIL_BASE_URL아무것도 전달하지 않았을 때의 기본 URL입니다. localhost:2222 같은 호스트만 적힌 값에는 스킴이 붙습니다.
OPENEMAIL_DISABLE_UPDATE_NOTICE비어 있지 않은 값이면 무엇이든 프로세스 안의 모든 클라이언트에 대해 업데이트 알림을 끕니다.
HTTPS_PROXY와 NO_PROXY, 또는 https_proxy와 no_proxycURL이 경유해 연결하는 프록시와, 직접 연결하는 호스트입니다. 아래의 프록시와 TLS를 참고하세요.

각 변수는 먼저 getenv()로 읽고, 그다음 $_SERVER와 $_ENV에서 읽으므로, 프레임워크가 .env 파일에서 불러온 값도 인정됩니다. 설정되었지만 비어 있는 변수는 설정되지 않은 것으로 간주합니다.

보내기 전에 거부하는 것

이들은 첫 발송에서 알기 어려운 실패로 나타나는 대신, 잘못된 값이 들어간 바로 그 줄에서 OpenEmail\Exception\InvalidArgumentException을 던집니다. 메시지는 무엇이 잘못되었고 대신 무엇을 전달해야 하는지 알려 주며, 자격 증명을 되풀이해 보여 주지 않습니다.

거부되는 것이유
자격 증명이 전혀 없음apiKey:도 accessToken:도 전달되지 않았고 두 환경 변수도 설정되지 않아 인증할 수단이 없습니다. 클라이언트를 만들 때 던져집니다.
키와 토큰을 함께 전달함모든 요청은 자격 증명 하나만 담으므로, 클라이언트는 어느 것을 의도했는지 알 수 없습니다.
세션 쿠키, 세션 토큰, 또는 다른 서비스의 키여기서 인증되는 것은 oe_live_와 oe_test_뿐이며, API도 같은 말을 합니다. 검사는 접두사 확인 그 이상이 아니므로, 폐기된 키는 여전히 통신 단계에서 AuthenticationException로 실패합니다.
http나 https URL이 아니거나, 사용자 이름이나 비밀번호가 들어 있는 baseUrl:그 밖의 것으로는 연결할 수 없으며, 자격 증명은 URL이 아니라 apiKey:나 accessToken:에 넣어야 합니다. 클라이언트를 만들 때 던져집니다.
이 컴퓨터에 있지 않은 호스트로 암호화되지 않은 http를 통해 보내는 자격 증명무엇이든 보내기 전에 호출에서 던져집니다. https 기본 URL을 사용하세요.
음수인 timeout:초를 전달하거나, 타임아웃을 없애려면 0을 전달하세요. 클라이언트를 만들 때 던져지며, 한 호출에 전달한 타임아웃이라면 그 호출에서 던져집니다.
토큰이 아닌 헤더 이름, 또는 헤더 값 안의 줄바꿈이나 그 밖의 제어 문자headers:, userAgent:, idempotencyKey:에서 검사합니다. 줄바꿈이 있으면 두 번째 헤더가 시작되기 때문입니다. 값 앞뒤의 공백, 탭, 줄바꿈은 fetch처럼 먼저 제거되므로, 줄바꿈으로 끝나는 파일에서 읽은 키도 그대로 작동합니다.
메서드에 빈 id나 점으로만 이루어진 id를 전달메서드를 호출할 때 던져집니다. 점으로 이루어진 경로 세그먼트는 모든 URL 파서가 제거하므로, 요청이 다른 엔드포인트에 도달하게 됩니다. 올바른 UTF-8이 아닌 id도 거부됩니다.
base64가 아닌 첨부 파일 내용문자열은 항상 base64로 읽히므로, 원시 바이트가 담긴 문자열은 깨진 데이터로 전송됩니다. OpenEmail::toBase64()로 인코딩하거나, SplFileInfo, 스트림 또는 PSR-7 스트림을 전달하면 클라이언트가 인코딩합니다.

이 클래스는 PHP 자체의 InvalidArgumentException을 상속하므로 이미 그것을 잡는 코드는 계속 동작하며, 패키지가 던지는 다른 모든 예외처럼 OpenEmail\Exception\OpenEmailException을 구현합니다. 문자열 id가 들어갈 자리에 숫자를 넣는 것처럼 타입이 잘못된 값은 PHP 자체의 TypeError가 됩니다. 모든 메서드가 타입을 선언하기 때문입니다.

testMode: 옵션은 없고 앞으로도 없을 것입니다. 키 체계는 힌트가 아니라 자격 증명의 일부이므로, 모드는 키의 속성입니다. $client->mode는 접두사를 읽어 live 또는 test를 알려 줄 뿐 아무것도 결정하지 않습니다.

하나의 클라이언트, 여러 개의 키

클라이언트는 한 번 만들어 공유하세요. 요청마다 새 클라이언트를 만들면 열린 연결을 헛되이 버리는 셈이고, 클라이언트에 담긴 상태 중 호출자별로 달라지는 것은 없습니다.

여러 워크스페이스를 대신해 발송하는 작업처럼 키마다 클라이언트를 만들어야 할 상황에서는 호출에 apiKey:를 전달하세요. 그 요청에 한해 Authorization 헤더를 대체하며 클라이언트에는 아무것도 남기지 않습니다.

per_call_key.php
$message = [    'from' => '[email protected]',    'to' => '[email protected]',    'subject' => 'Your invoice',    'text' => 'Attached.',];$workspaceKey = (string) getenv('OPENEMAIL_API_KEY'); $client->emails->send($message); $client->emails->send($message, apiKey: $workspaceKey); $client->threads->list(folder: 'inbox', apiKey: $workspaceKey);$client->webhooks->list(apiKey: $workspaceKey);

tempMail 밖의 모든 메서드가 이 값을 마지막 명명된 인자로 받으며(목록에서는 필터 뒤), tempMail의 메서드는 대신 inboxToken:을 받습니다. 이 값은 클라이언트와 같은 규칙으로 요청 전에 검사되므로, 오타가 나면 나중에 찾아 헤매야 할 자격 증명에 대한 401이 아니라 이 호출에 전달한 apiKey에 관한 InvalidArgumentException을 던집니다. 재시도된 호출은 받은 키를 그대로 유지합니다.

$client->mode는 클라이언트를 만들 때 사용한 키를 설명하며, 호출별 재정의를 따라가지 않습니다. 하나의 클라이언트가 여러 키를 쓰는 순간 보고할 단일 모드가 없어지므로, 전달한 키에서 직접 읽으세요. var_dump($client)는 모드와 기본 URL을 보여 줄 뿐 키는 절대 보여 주지 않으며, 자격 증명을 받는 모든 매개변수에는 #[\SensitiveParameter]가 표시되어 있어 스택 트레이스에는 그 자리에 자리 표시자가 출력됩니다.

메서드가 감싸지 않는 엔드포인트

$client->raw는 모든 메서드가 거치는 전송 계층입니다. $client->raw->request()는 아직 어떤 메서드도 감싸지 않는 경로를, 클라이언트의 자격 증명, 기본 URL, 타임아웃, 재시도 정책을 적용해 호출하고, 메서드와 마찬가지로 디코딩된 본문을 반환합니다.

raw_request.php
$ping = $client->raw->request('/ping'); $label = $client->raw->request('/labels', method: 'POST', body: ['name' => 'Invoices']); var_dump($ping, $label);
명명된 인자하는 일
method:따로 지정하지 않으면 GET이며, 그 밖에 POST, PUT, PATCH, DELETE를 쓸 수 있습니다.
query:쿼리 매개변수의 배열입니다. null과 빈 값은 제외되고, 리스트는 쉼표로 이어지며, DateTimeInterface는 UTC 기준 ISO 8601 시각으로 전송됩니다.
body:JSON으로 전송되는 배열입니다.
raw:와 contentType:그대로 보낼 바이트로, 문자열, 스트림 리소스, SplFileInfo, PSR-7 스트림 중 하나입니다. 유형을 지정하지 않으면 application/octet-stream으로 전송됩니다.
accept:와 binary:JSON이 아닌 accept:를 지정하면 본문을 텍스트로 반환하고, binary: true는 바이트 문자열로 반환합니다.
idempotent:와 idempotencyKey:idempotent: true는 Idempotency-Key를 붙이며, 직접 전달하지 않으면 생성됩니다.
repeatable:실패를 재시도할지 여부입니다. repeatable: true를 전달하지 않는 한 GET만 재시도됩니다.
anonymous:true이면 자격 증명을 전혀 보내지 않습니다.
apiKey:, inboxToken:, timeout:같은 호출별 자격 증명과, 이 호출에만 적용되는 초 단위 타임아웃입니다.

경로는 / 하나로 시작해야 하며, 완성된 URL이 기본 URL의 오리진을 벗어나는 경로는 무엇이든 보내기 전에 InvalidArgumentException을 던지므로, 자격 증명이 다른 호스트에 닿는 일은 없습니다.

일회용 받은편지함

OpenEmail::createTempMail()은 API 키를 담지 않고 환경 변수에서도 읽지 않는 일회용 받은편지함용 클라이언트를 만듭니다. 익명으로 받은편지함을 만들고, 읽을 때마다 create가 반환한 받은편지함 토큰이나 extend가 반환한 더 새로운 토큰을 호출별 inboxToken:으로 보내거나 OpenEmail::createTempMail(inboxToken: ...)으로 한 번에 지정합니다.

temp_mail.php
use OpenEmail\OpenEmail; $tempMail = OpenEmail::createTempMail(); $inbox = $tempMail->create();$page = $tempMail->listMessages($inbox['id'], inboxToken: $inbox['token']); echo count($page->items), ' ', $page->expiresAt, PHP_EOL;

OpenEmail::createTempMail()은 다른 클라이언트처럼 baseUrl:, httpClient:, maxRetries:, timeout:, userAgent:, headers:, disableUpdateNotice:를 받으며, 기본 URL을 전달하지 않으면 OPENEMAIL_BASE_URL을 읽습니다.

OAuth 액세스 토큰

명령줄 도구나 에이전트처럼 사용자가 OAuth로 연결한 앱은 API 키 대신 액세스 토큰을 가집니다. 이를 accessToken:으로 전달하세요. 토큰 자체이거나, 클로저나 first-class callable처럼 토큰을 반환하는 호출 가능 객체입니다. 이 객체는 호출마다 한 번 실행되고, 그 호출의 재시도는 반환된 값을 다시 씁니다. 그러니 만료가 가까워지면 그 안에서 토큰을 갱신하세요. 클라이언트를 다시 만들 필요가 없습니다.

access_token.php
use OpenEmail\OpenEmail; $tokens = ['current' => 'token-from-your-oauth-flow']; $oauthClient = new OpenEmail(accessToken: fn(): string => $tokens['current']); $me = $oauthClient->me->get(); if ($me['object'] === 'oauth_token') {    echo $me['clientId'], ' ', $me['expiresAt'], PHP_EOL;}
경우일어나는 일
apiKey:와 accessToken:을 함께 전달하거나, 둘 다 전달하지 않음클라이언트를 만들 때 InvalidArgumentException을 던집니다. 둘 다 없으면 메시지가 OPENEMAIL_API_KEY와 OPENEMAIL_ACCESS_TOKEN을 알려 줍니다.
토큰이 아닌 값토큰은 1~512자이고 oe_로 시작하지 않습니다. OpenEmail::isAccessToken()이 하는 검사입니다. 이 검사를 통과하지 못한 문자열은 클라이언트를 만들 때 거부되고, 그런 값을 반환하는 호출 가능 객체는 아무것도 보내기 전에 호출이 InvalidArgumentException을 던지게 합니다.
OPENEMAIL_ACCESS_TOKEN두 자격 증명 중 어느 것도 전달하지 않고 OPENEMAIL_API_KEY도 설정되지 않았을 때 읽습니다. 즉 환경 변수의 키가 우선합니다.
예외를 던지는 호출 가능 객체호출은 그 예외를 바꾸지 않고 그대로 던지며, 아무것도 보내지 않습니다.
호출별 apiKey:그 요청 하나에 한해 토큰을 대체하고, 호출 가능 객체는 호출되지 않습니다.
$client->mode토큰에서는 항상 live입니다.
OpenEmail::createTempMail()환경에 무엇이 있든 자격 증명을 보내지 않습니다.
me->get()과 me->ping()토큰일 때 get은 object가 oauth_token, id와 roleId가 null, 연결된 앱의 clientId, 그리고 사용자가 앱에 준 승인이 끝나는 시점인 expiresAt으로 응답합니다. ping은 kind가 oauth, keyId가 null, 그리고 clientId로 응답합니다. id나 keyId를 읽기 전에 object나 kind를 확인하세요.

토큰은 사용자를 대신해 움직이며 사용자처럼 메일을 읽을 수 있으므로, 키처럼 서버에 두세요.

인증 코드

도메인 삭제나 웹훅 변경 같은 민감한 변경 전에 API는 액세스 토큰에게, 웹 앱이 사용자에게 요구할 인증 코드를 요구합니다. 호출은 isStepUpRequired()가 true인 403, 즉 PermissionException을 던지며, 아무것도 바뀌지 않았습니다. 코드를 요청하고 사용자가 준 코드를 인증한 뒤 다시 호출하세요. API 키는 요구받지 않습니다.

step_up.php
use OpenEmail\Exception\ApiException; $domainId = 'b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f'; try {    $client->domains->delete($domainId);} catch (ApiException $error) {    if (!$error->isStepUpRequired()) {        throw $error;    }     $challenge = $client->security->beginStepUp();     if ($challenge['method'] === 'email') {        echo 'Enter the code we emailed to ', $challenge['sentTo'], PHP_EOL;    } else {        echo 'Enter the code from your authenticator app, or a backup code', PHP_EOL;    }     $client->security->verifyStepUp(['code' => trim((string) fgets(STDIN))]);    $client->domains->delete($domainId);}
메서드하는 일
security->stepUpStatus()앱이 지금 인증된 상태인지(elevated, elevatedUntil), 다음 코드를 어떻게 확인하는지(method, email 또는 totp), 그리고 유효 시간의 길이인 minutes. 아무것도 보내지 않으며 일시 중지를 알려 주지 않습니다.
security->beginStepUp()인증 요청을 엽니다. email이면 사용자가 로그인에 쓰는 주소로 여섯 자리 코드가 가고, sentTo에 가려진 주소가 표시됩니다. totp이면 사용자가 인증 앱의 코드를 읽거나 복구 코드를 씁니다. 아직 열려 있고 시도 횟수가 남은 요청은 ['resend' => true]를 전달하지 않는 한 다시 쓰이며, 잠겼거나 만료된 요청은 단순한 호출로 교체됩니다. 각 앱은 사람마다 1시간에 5개, 24시간에 20개까지 열 수 있고, 그다음은 429 step_up_throttled를 던집니다.
security->verifyStepUp(['code' => ...])코드를 확인하고 이 앱의 민감한 변경을 60분 동안, elevatedUntil까지, REST와 같은 변경을 하는 MCP 도구 모두에서 풀어 줍니다. 이 앱에서 24시간 동안 틀린 코드가 10개, 또는 사용자의 모든 앱에서 합쳐서 20개가 되면 이 호출과 beginStepUp은 인증이 언제 다시 가능한지 알려 주는 메시지와 함께 429 step_up_locked를 던집니다.

클라이언트는 스스로 코드를 요구하거나 호출을 반복하지 않으며, 세 메서드 중 어느 것도 자동으로 재시도되지 않습니다. 응답을 잃은 뒤 재시도하면 두 번째 이메일이 가거나 시도 횟수를 한 번 더 쓸 수 있기 때문입니다. 스코프는 필요 없고, API 키로 그중 하나를 호출하면 400 step_up_not_applicable이 돌아옵니다. OpenEmail\Constants\StepUpErrorCodes는 인증이 실패하는 모든 경우를 담고 있으며, 각각의 대처법은 API 오류 페이지에 있습니다.

업데이트 알림

Packagist에 패키지의 새 버전이 있으면 클라이언트는 프로세스당 한 번, 표준 오류에 ℹ openemail/sdk 0.0.2 is available, you are on 0.0.1. 같은 줄과 패키지의 페이지를 출력해 알려 줍니다. 이 확인은 명령줄에서 표준 출력이 터미널일 때만 실행되며, 웹 서버에서는 절대 실행되지 않습니다. 첫 클라이언트를 만들 때 시작되어 요청과 나란히 실행되고, 스크립트가 끝날 때 2초 예산 중 남은 시간만큼 기다립니다. Packagist에 연결하지 못해도 무시됩니다.

이 확인은 클라이언트의 httpClient: 바깥에서 cURL로 직접 요청을 보내므로, 테스트의 가짜 HTTP 클라이언트는 이 요청을 보지 못합니다. 끄려면 disableUpdateNotice: true를 전달하거나 OPENEMAIL_DISABLE_UPDATE_NOTICE를 설정하세요.

프록시와 TLS

기본 CurlHttpClient는 프록시를 cURL에 맡기며, cURL은 프록시로 https_proxy 또는 HTTPS_PROXY를, 직접 연결할 호스트로 no_proxy 또는 NO_PROXY를 읽습니다. 대신 코드에서 프록시를 지정하려면 proxy:를 전달하세요. 프록시 URL에 들어 있는 사용자 이름과 비밀번호는 프록시로 전송됩니다.

curl_options.php
use OpenEmail\Http\CurlHttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(httpClient: new CurlHttpClient(    caBundle: '/etc/ssl/certs/corporate-ca.pem',    proxy: 'http://proxy.internal:3128',    curlOptions: [CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4],));

연결은 TLS 1.2 이상을 사용하고 서버의 인증서와 호스트 이름을 확인하며, 리디렉션은 절대 따라가지 않습니다. caBundle:은 TLS를 검사하는 프록시를 위해 신뢰할 인증 기관을 지정합니다. curlOptions:는 그 밖의 cURL 옵션을 설정하지만, URL과 포트, 메서드, 헤더, 본문, 꺼진 리디렉션처럼 요청에 필요한 설정이 항상 우선합니다. CURLOPT_REQUEST_TARGET은 거부되며, cURL이 받아들이지 않는 옵션은 그 이름을 밝힌 InvalidArgumentException을 던집니다.