Skip to content

Latest commit

 

History

History
141 lines (103 loc) · 4.13 KB

php.mdx

File metadata and controls

141 lines (103 loc) · 4.13 KB
title PHP SDK
description Test email flows in PHPUnit, Pest and Laravel Dusk — OTPs and magic links auto-extracted.

Install

composer require --dev zerodrop/zerodrop

PHP 8.1+ · ext-curl · ext-json — no other dependencies.

Quick start

use ZeroDrop\Client;

$mail = new Client();
$inbox = $mail->generateInbox();
// => "swift-x7k29ab@zerodrop-sandbox.online"

// Trigger your app's email flow with the inbox address...

$email = $mail->waitForLatest($inbox, timeout: 15);

$email->otp;       // "123456" — auto-extracted, no regex
$email->magicLink; // "https://..." — auto-extracted

Laravel Dusk

use Laravel\Dusk\Browser;
use ZeroDrop\Client;

public function testSignupEmailVerification(): void
{
    $mail = new Client();
    $inbox = $mail->generateInbox();

    $this->browse(function (Browser $browser) use ($mail, $inbox) {
        $browser->visit('/signup')
                ->type('email', $inbox)
                ->type('password', 'TestPassword123!')
                ->press('Create account');

        $email = $mail->waitForLatest($inbox, timeout: 15);

        $browser->type('otp', $email->otp)
                ->press('Verify')
                ->assertPathIs('/dashboard');
    });
}

Pest

use ZeroDrop\Client;

it('verifies the account via emailed OTP', function () {
    $mail = new Client();
    $inbox = $mail->generateInbox();

    // Trigger signup with $inbox...

    $email = $mail->waitForLatest($inbox, timeout: 15);

    expect($email->otp)->not->toBeNull();
});

Filtering

use ZeroDrop\Filter;

$email = $mail->waitForLatest(
    $inbox,
    timeout: 15,
    filter: new Filter(
        from: 'noreply@yourapp.com',
        hasOtp: true,
    ),
);

All string filters are case-insensitive partial matches.

Parallel tests

generateInbox() runs locally — safe with Paratest and parallel Dusk runs. Every call returns a unique isolated inbox.

Free API key (private inboxes)

Without a key, anyone who knows an inbox's name can read it, and one sender can reach only 5 different inboxes a day: past that, the inbox gets a notice from notice@zerodrop.dev instead of the email. Sign in with GitHub at zerodrop.dev/keys for a free key and set it as ZERODROP_API_KEY. With zerodrop/zerodrop 0.2.0 or later, the client reads it, generateInbox() starts names with your key's prefix, only your key can read those inboxes, and mail to them isn't limited:

$mail = new Client(); // reads ZERODROP_API_KEY
$mail->generateInbox();
// "k7x2-swift-x7k29ab@zerodrop-sandbox.online", where k7x2 is your prefix
$mail->prefix(); // "k7x2"

Error handling

use ZeroDrop\Exception\TimeoutException;
use ZeroDrop\Exception\AuthException;
use ZeroDrop\Exception\HeldException;
use ZeroDrop\Exception\NetworkException;

try {
    $email = $mail->waitForLatest($inbox, timeout: 15);
} catch (TimeoutException $e) {
    // No email arrived
} catch (AuthException $e) {
    // The inbox belongs to another key's prefix, or your key is wrong; $e->getMessage() says which
} catch (HeldException $e) {
    // No API key, and ZeroDrop held the email (daily limit); $e->getMessage() says why
} catch (NetworkException $e) {
    // Transport failure
}

HeldException is new in 0.3.0. Without an API key, one sender can reach only 5 different inboxes a day; past that, the email isn't stored and the inbox gets a notice instead. waitForLatest() then throws HeldException at once rather than waiting for the timeout, and fetchLatest() throws it when nothing matches. Older versions treat the notice as an email: without a filter they return it, and a filter such as hasOtp: true skips it, so the wait times out. A free key from zerodrop.dev/keys lifts the limit for inboxes under your key's prefix.

Workspaces & self-hosting

// The key comes from ZERODROP_API_KEY (0.2.0 or later), or pass apiKey: yourself
$mail = new Client(
    baseUrl: 'https://your-instance.yourdomain.com',
);

Links