Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Epp2 Laravel Package

africc/php-epp2

A PHP client library for the Extensible Provisioning Protocol (EPP), the standard used by domain registrars. It builds and parses EPP XML requests over TLS, with support for domain, contact, and host commands plus common registry extensions.

Deep Wiki
Context7

Getting Started

Install with Composer and pin the version, because the package is small and not frequently released:

composer require africc/php-epp2:^2.0

Look first at:

  1. README.md for the basic client example.
  2. src/Client.php for connection, login, request, logout, and timeout behavior.
  3. src/Frame/Command/ for the command frames you will use daily: Check, Create, Info, Update, Delete, Transfer, Poll, and Ack.
  4. tests/ for real XML examples. With a package this size, tests are often better documentation than the README.

Minimal first use case: check domain availability over an EPP session.

use AfriCC\EPP\Client;
use AfriCC\EPP\Frame\Command\Check\Domain as DomainCheck;

$client = new Client(
    'epp.registry.example',   // host
    700,                      // port, often 700 or 7000
    getenv('EPP_USER'),
    getenv('EPP_PASS'),
    true,                     // TLS
    [
        'ssl' => [
            'local_cert' => '/path/to/client.pem',
            'passphrase' => getenv('EPP_CERT_PASS'),
            'verify_peer' => true,
            'verify_peer_name' => true,
        ],
    ]
);

$client->connect();
$client->login();

try {
    $check = new DomainCheck();
    $check->addDomain('example.com');
    $check->addDomain('example.net');

    $response = $client->request($check);

    // Inspect once during development:
    // var_dump($response->getCode(), $response->getData());

    if ($response->getCode() === 1000) {
        $data = $response->getData();
        // Parse domain:chkData / domain:cd entries.
        // Namespace-prefixed keys and @attributes are common.
    }
} finally {
    $client->logout();
    $client->close();
}

After availability checks, the next practical use cases are Info\Domain, Create\Domain, Update\Domain, and queue polling with Poll/Ack.

Implementation Patterns

Wrap the client in a service

EPP is a stateful TCP session, so do not scatter new Client() through controllers. Create a small service that owns connect, login, request, logout, and close, and inject it where needed.

final class EppGateway
{
    private Client $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function connect(): void
    {
        $this->client->connect();
        $this->client->login();
    }

    public function request(FrameInterface $frame): Response
    {
        return $this->client->request($frame);
    }

    public function close(): void
    {
        try {
            $this->client->logout();
        } finally {
            $this->client->close();
        }
    }
}

Use try/finally around command execution so a failed command still attempts logout and socket close. In long-running workers, catch transport failures and reconnect explicitly.

Build frames, not XML

Prefer the command frame classes over hand-written XML. They handle EPP namespaces and command structure.

use AfriCC\EPP\Frame\Command\Create\Domain as CreateDomain;

$create = new CreateDomain();
$create->setDomain('example.com');
$create->setPeriod(1);
$create->setRegistrant('contact-id');
$create->setAuthInfo('random-auth-info');
$create->addNs('ns1.example.com');
$create->addNs('ns2.example.com');

$response = $gateway->request($create);

The same pattern applies to contacts and hosts: build a Create\Contact or Create\Host, set required fields, then send it. Keep registry-required data in your application model so frame building stays thin.

For updates, use add/remove semantics rather than replacing the whole object blindly:

use AfriCC\EPP\Frame\Command\Update\Domain as UpdateDomain;

$update = new UpdateDomain();
$update->setDomain('example.com');
$update->addStatus('clientHold');
$update->removeNs('ns1.example.com');
$update->addNs('ns3.example.com');

Treat EPP result codes as application state

Client::request() usually returns a response frame even for protocol-level errors. A successful TCP round trip is not the same as a successful EPP command. Check the result code.

$response = $gateway->request($frame);
$code = $response->getCode();

match (true) {
    $code === 1000 => $this->handleSuccess($response),
    $code === 1001 => $this->handlePending($response),
    $code === 2302 => $this->handleAlreadyExists($response),
    $code === 2303 => $this->handleNotFound($response),
    $code >= 2000  => $this->handleEppError($code, $response),
    default        => $this->handleUnexpected($code, $response),
};

Log the EPP code and message, not just “request failed.” Registry error messages often contain the exact missing field or policy violation.

Batch checks, but do not batch everything

Domain checks support multiple names in one EPP command, which is one of the biggest performance wins. Use it for availability pages and bulk import validation.

$check = new DomainCheck();
foreach ($domains as $domain) {
    $check->addDomain($domain);
}
$response = $gateway->request($check);

For creates, updates, and transfers, send one command per object. Do not try to combine unrelated operations into a single frame unless the registry explicitly supports it.

Poll the registry queue on a schedule

Registry messages, transfer notifications, and contact/host events arrive through the EPP poll queue. A common integration is a cron job or queue worker that logs in, polls repeatedly, acknowledges each message, and dispatches application events.

use AfriCC\EPP\Frame\Command\Poll;
use AfriCC\EPP\Frame\Command\Ack;

$poll = new Poll();
$response = $gateway->request($poll);

while ($response->getCode() === 1000 && $response->getData()) {
    $messageId = /* extract msgID from response */;
    $this->dispatchRegistryMessage($response->getData());

    $ack = new Ack();
    $ack->setMessageId($messageId);
    $response = $gateway->request($ack);
}

Always acknowledge messages. An unacknowledged queue can block later messages and cause registry-side cleanup.

Isolate registry extensions

The base client handles standard EPP. Registries such as Nominet have separate extension packages or require additional XML namespaces. Keep extension-specific frame building behind an adapter or registry-specific service rather than branching inside your domain checkout code.

For unsupported extensions, subclass an existing frame or compose the extension XML carefully and verify the generated frame with (string) $frame before sending it.

Test frames without the network

Frame classes are useful outside the client. Unit-test generated XML by casting frames to strings and comparing against fixtures.

$frame = new CreateDomain();
$frame->setDomain('example.com');
$frame->setRegistrant('contact-id');

$this->assertStringContainsString('<domain:name>example.com</domain:name>', (string) $frame);

For client-level tests, wrap the client in an interface or fake transport and assert that your service maps EPP result codes correctly. This is more valuable than mocking raw sockets.

Gotchas and Tips

  • License: GPL-3.0 is a real constraint. If you distribute a proprietary application that links this package, review the copyleft obligations before adoption.
  • Maintenance risk: 54 stars and a last release in 2022 means a small ecosystem. Pin the version, run your own integration tests against the registry test environment, and be prepared to patch.
  • EPP errors are not transport exceptions: A response with code 2306 or 2304 may still be returned normally. Check getCode() before assuming success.
  • Stateful sessions: EPP login is not cheap and registries limit concurrent sessions. Reuse a connection within a worker, but reconnect after idle timeouts or transport failures.
  • No automatic keepalive: Implement your own reconnect/retry policy. Do not retry non-idempotent creates blindly; a timed-out create may have succeeded.
  • Namespace-prefixed response arrays: Response data is nested and registry-specific. Dump $response->getData() once per command type and build small parsers rather than guessing key names.
  • TLS configuration matters: Ports vary (700 vs 7000), and registries often require client certificates or IP allowlisting. Test certificate verification in staging, not production.
  • Timeouts: Set a transport timeout and wrap commands in an application-level timeout. A default socket timeout can hang a request worker.
  • setDomain vs addDomain: Check the frame class and tests. Check commands typically add multiple names; create/info/delete commands normally target one name.
  • Punycode: Convert internationalized domain names before sending them. Registries expect A-label form, not raw Unicode.
  • Idempotency: Handle “object exists” and “object not found” codes explicitly. Pre-checking with Info adds a round trip; decide whether the race is acceptable.
  • Poll acknowledgement: Forgetting to Ack a poll message is a common source of “missing” registry notifications.
  • Credentials and PII: Keep EPP credentials and certificate passphrases in environment variables or a secret manager. Redact auth info and contact data from logs.
  • Raw XML logging: Log full request/response XML only at debug level and with sensitive fields filtered. It is invaluable when a registry rejects a frame.
  • UTC everywhere: EPP timestamps are UTC. Store them as UTC and convert at presentation time.
  • Extension points: If a method is missing, inspect AbstractFrame and existing command frames before hand-building XML. Extending a frame is usually safer than string concatenation.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings