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 Epp Client Laravel Package

metaregistrar/php-epp-client

A PHP client for EPP (Extensible Provisioning Protocol) that lets you connect to domain registries to check, register, renew, transfer, and manage domains, contacts, and DNSSEC data through a simple object-oriented API.

Deep Wiki
Context7

Getting Started

Install with Composer:

composer require metaregistrar/php-epp-client

Look here first, in this order:

  1. config/*.ini — the package ships ready-made connection profiles for a number of registries (SIDN, Nominet, RGP/IIS, Key-Systems, etc.). Copy one and edit credentials rather than hand-building a connection. This is the fastest way to understand which knobs exist (hostname, port, username, password, certificate, privatekey, capath, timeout, ssl/tls, logfile, debuglog).
  2. src/Metaregistrar/EPP/ — flat class set: eppConnection, eppRequest/eppResponse and their per-command subclasses, plus the value objects eppDomain, eppContact, eppHost.
  3. tests/ — the most reliable documentation of real request/response flows for your installed version.

A minimal first use case — check availability, then register:

use Metaregistrar\EPP\eppConnection;
use Metaregistrar\EPP\eppDomain;
use Metaregistrar\EPP\eppCheckRequest;
use Metaregistrar\EPP\eppCreateRequest;
use Metaregistrar\EPP\eppContactHandle;

$conn = new eppConnection(__DIR__ . '/config/mijn-registry.ini');
$conn->setLogFile(__DIR__ . '/storage/logs/epp.log');

try {
    if (!$conn->login()) {
        throw new RuntimeException('EPP login failed');
    }

    $check = new eppCheckRequest(new eppDomain('example.nl'));
    $conn->request($check);
    $checked = $conn->getResponse()->getCheckedDomains(); // [['domain'=>..., 'available'=>bool, 'reason'=>...]]

    if ($checked[0]['available']) {
        $domain = new eppDomain('example.nl');
        $domain->setRegistrant(new eppContactHandle('REG-0001'));
        $domain->addContact(new eppContactHandle('TECH-0001'), 'tech');
        $domain->addPeriod(1, 'y');
        $domain->addNs('ns1.example.nl');
        $domain->addNs('ns2.example.nl');
        $domain->setAuthorisationInfo(bin2hex(random_bytes(8)));

        $conn->request(new eppCreateRequest($domain));
        $response = $conn->getResponse();
        if (!$response->success()) {
            throw new RuntimeException($response->getResultCode() . ': ' . $response->getResultMessage());
        }
    }
} finally {
    $conn->logout();
}

That loop — login → one or more request() calls → inspect getResponse() → logout — is the whole API. Everything else is choosing the right request object.

Implementation Patterns

Treat the connection as a long-lived, serialized resource. An eppConnection is one TLS socket with one authenticated session. Don't open one per HTTP request in a busy application; either reuse it across a queue worker's lifetime, or keep a small pool keyed by registry. Wrap it so callers never touch login()/logout() directly:

final class RegistrySession
{
    public function __construct(private eppConnection $conn) {}

    public function __call(string $method, array $args) { /* not needed */ }

    public function execute(eppRequest $request): eppResponse
    {
        if (!$this->conn->request($request)) {
            throw new EppTransportException('request failed');
        }
        return $this->conn->getResponse();
    }

    public function assertSuccess(eppResponse $r): eppResponse
    {
        if (!$r->success()) {
            throw new EppCommandException($r->getResultMessage(), $r->getResultCode());
        }
        return $r;
    }
}

Keep value objects and request objects separate. eppDomain, eppContact, eppHost are plain data holders; epp*Request classes translate them into XML. Build the domain once, reuse it for create/update/renew:

$patch = new eppDomain('example.nl');
$patch->addStatus('clientHold');
$conn->request(new eppUpdateDomainRequest($domain, null, null, $patch));

For NS/contact changes on update, build three eppDomain objects ($add, $remove, $update) and pass them positionally — only the non-null ones are serialized.

Lifecycle commands all follow the same shape:

new eppInfoDomainRequest($domain);                 // + setHosts(true) for NS list
new eppRenewRequest($domain, $currentExpiry, 1);   // currentExpiry is a DateTime/UTC string
new eppDeleteRequest($domain);
new eppTransferRequest(eppTransferRequest::OPERATION_REQUEST, $domain, $authCode);
// OPERATION_QUERY / OPERATION_APPROVE / OPERATION_CANCEL for the rest

Contacts and hosts are first-class too — eppCreateRequest(new eppContact(...)) with postal info, voice, email, and setAuthInfo(); hosts via eppHost with addAddress(), including glue records.

Poll the message queue. Registries push transfer/expiry notifications; you must <poll> and then <poll op="ack"> with the returned msgID, otherwise the queue wedges. Run it on a cron/worker loop:

$conn->request(new eppPollRequest(eppPollRequest::POLL_REQ));
$response = $conn->getResponse();
while ($response->getResultCode() === 1300) {
    $msgId = $response->getPollResId();
    // handle the message, then ack it
    $conn->request(new eppPollRequest(eppPollRequest::POLL_ACK, $msgId));
    $conn->request(new eppPollRequest(eppPollRequest::POLL_REQ));
    $response = $conn->getResponse();
}

Turn on XML logging in every environment except the noisiest production path. The raw <epp>/</epp> payload in the log file is what registry support will ask for, and it is the only practical way to debug a rejected create.

Gotchas and Tips

  • request() semantics vary by version. Older releases return bool and you must call getResponse(); some paths return the response object. Don't assume — check the return of the version you pinned, and never call getResponse() without first checking the boolean, or you'll silently reuse the previous response.
  • Result code 1001 is not a failure. "Command completed successfully; action pending" means the transfer/create is queued. Treat success() as "accepted", then reconcile asynchronously via info or poll messages.
  • Sessions are scarce. Registries cap concurrent sessions per registrar account and often whitelist source IPs. A leaked connection (exception before logout()) burns a slot until timeout. Always logout() in a finally, and set a sane timeout in the ini.
  • Client certificates bite. Wrong capath, a missing intermediate, or a PEM containing both key and cert in the wrong order produces opaque TLS failures that look like login failures. Test with openssl s_client against the registry endpoint first.
  • Date formats are UTC and strict. EPP wants Y-m-d\TH:i:s.v\Z style timestamps; passing a local-time DateTime gives you an off-by-hours expiry or an outright 2005 parameter error. Convert with ->setTimezone(new DateTimeZone('UTC')) before handing it to the request object.
  • INI parsing quirks. Values are read literally — trailing spaces, true vs 1, and relative certificate paths resolved against the working directory all cause confusing failures. Prefer absolute paths, and prefer constructing the connection programmatically for anything non-trivial.
  • XML escaping is handled — but only if you go through the builders. Concatenating your own <extension> XML or injecting into getExtension() output re-introduces escaping bugs. Use the value objects and let them escape.
  • Registries diverge. Contact handle formats, required roles, which TLDs need auth-info, whether DNSSEC is supported, and extension availability all differ. The package models this with registry-specific subclasses (connection and request classes) — check the registry folders/addons directory of your installed version before writing a generic call that one registry will reject.
  • Extensions are the extension point. For RGP, DNSSEC, or a registry's proprietary block, subclass the extension base class and attach it to the request; don't patch the package in vendor/. If you must customize, prefer subclassing epp*Request and overriding the XML builder, then keep your subclasses in app/Epp/.
  • Test against the registry's OTE/test environment, and pin the Composer version. The project ships frequently (recent release dates), so an unpinned ^5 can change response accessors under you without a major-version signal.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle