Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## What does this PR do?

<!-- Brief description of the change and the problem it solves -->

## Type of change

- [ ] Feature (`feature/*` → `develop`)
- [ ] Bug fix (`fix/*` → `develop`)
- [ ] Hotfix (`hotfix/*` → `main` + `develop`)
- [ ] Release (`release/*` → `main` + `develop`)

## Test plan

- [ ] `./vendor/bin/pest` — all tests pass
- [ ] `./vendor/bin/pint --test` — no style violations
- [ ] Manually tested the affected flows

## Notes

<!-- Anything reviewers should pay special attention to -->
34 changes: 34 additions & 0 deletions .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Auto Label

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- name: Apply label based on branch name
uses: actions/github-script@v7
with:
script: |
const branch = context.payload.pull_request.head.ref;
const labels = [];

if (branch.startsWith('feature/')) labels.push('feature');
else if (branch.startsWith('fix/')) labels.push('bug');
else if (branch.startsWith('hotfix/')) labels.push('hotfix');
else if (branch.startsWith('release/')) labels.push('release');

if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels,
});
}
28 changes: 28 additions & 0 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Static Analysis

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
phpstan:
runs-on: ubuntu-latest
name: PHPStan

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: none

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Run PHPStan
run: ./vendor/bin/phpstan analyse --no-progress
28 changes: 28 additions & 0 deletions .github/workflows/pint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Code Style

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
pint:
runs-on: ubuntu-latest
name: Laravel Pint

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: none

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Check code style
run: ./vendor/bin/pint --test
35 changes: 35 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Tests

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3']

name: PHP ${{ matrix.php }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, json, libxml, simplexml
coverage: none

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Run tests
run: ./vendor/bin/pest
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.php_cs.cache
.idea/
/composer.lock
CLAUDE.md
OVERVIEW.md
71 changes: 71 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Contributing

This project follows the **Gitflow** branching model.

## Branch structure

| Branch | Purpose |
|--------|---------|
| `main` | Production-ready code. Only receives merges from `release/*` and `hotfix/*`. |
| `develop` | Integration branch. All features land here first. |
| `feature/*` | New features, branched from and merged back into `develop`. |
| `release/*` | Release preparation, branched from `develop`, merged into both `main` and `develop`. |
| `hotfix/*` | Urgent production fixes, branched from `main`, merged into both `main` and `develop`. |

## Day-to-day workflow

### Starting a feature

```bash
git checkout develop
git pull origin develop
git checkout -b feature/my-feature
```

Work, commit, push, then open a PR targeting `develop`.

### Starting a release

```bash
git checkout develop
git pull origin develop
git checkout -b release/1.2.0
# bump version, update changelog, final fixes
git push origin release/1.2.0
```

Open a PR targeting `main`. After merge, also merge into `develop` and tag the release:

```bash
git checkout main && git pull origin main
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0
```

### Fixing a production bug (hotfix)

```bash
git checkout main
git pull origin main
git checkout -b hotfix/fix-critical-bug
# fix, commit
git push origin hotfix/fix-critical-bug
```

Open two PRs: one targeting `main`, one targeting `develop`.

## Branch naming

| Type | Pattern | Example |
|------|---------|---------|
| Feature | `feature/<short-description>` | `feature/domain-transfer` |
| Bug fix | `fix/<short-description>` | `fix/response-code-cast` |
| Release | `release/<semver>` | `release/1.2.0` |
| Hotfix | `hotfix/<short-description>` | `hotfix/sidn-namespace-url` |

## Rules

- `main` and `develop` are protected — direct pushes are blocked.
- Every change requires a PR with at least **1 approval**.
- Stale approvals are dismissed when new commits are pushed.
- Run `./vendor/bin/pest` and `./vendor/bin/pint` before opening a PR.
123 changes: 122 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,125 @@ SIDN_PASSWORD=superpass123!
SIDN_HOSTNAME=drs.domain-registry.nl
```

Start using Laravel EPP !
Start using Laravel EPP !

## Requirements

- PHP 8.1+
- Laravel 10 or 11

## Usage

### Starting a session

```php
use YWatchman\LaravelEPP\Epp;

$epp = new Epp('sidn');
$epp->start();
$epp->login();

// ... perform commands ...

$epp->logout();
```

### Domain commands

**Check availability**
```php
use YWatchman\LaravelEPP\Models\Domain;
use YWatchman\LaravelEPP\Support\Xml\Commands\Domain\CheckCommand;
use YWatchman\LaravelEPP\Responses\Domain\CheckResponse;

$domains = [new Domain(['name' => 'example.nl'])];
$command = new CheckCommand($domains);
$response = new CheckResponse($epp->sendRequest((string) $command));

if ($response->isSucceeded()) {
// $response->domainExists('example.nl')
}
```

**Create a domain**
```php
use YWatchman\LaravelEPP\Support\Xml\Commands\Domain\CreateCommand;
use YWatchman\LaravelEPP\Responses\Domain\CreateResponse;

$command = new CreateCommand($domain, $contacts, $nameservers);
$response = new CreateResponse($epp->sendRequest((string) $command));
```

**Transfer a domain**
```php
use YWatchman\LaravelEPP\Support\Xml\Commands\Domain\TransferCommand;
use YWatchman\LaravelEPP\Responses\Domain\TransferResponse;

$command = new TransferCommand($domain, $authToken);
$response = new TransferResponse($epp->sendRequest((string) $command));
```

### Contact commands

**Create a contact**
```php
use YWatchman\LaravelEPP\Models\Contact;
use YWatchman\LaravelEPP\Support\Xml\Commands\Contact\CreateCommand;
use YWatchman\LaravelEPP\Responses\Contact\CreateResponse;

$contact = new Contact([
'handle' => 'MYHANDLE001',
'name' => 'Jane Doe',
'street' => 'Main Street',
'number' => '1',
'city' => 'Amsterdam',
'postal' => '1000AA',
'country' => 'NL',
'phone' => '+31.201234567',
'email' => 'jane@example.nl',
'legalForm' => 'PERSON', // PERSON, OTHER, or SIDN Dutch codes directly
]);

$command = new CreateCommand($contact);
$response = new CreateResponse($epp->sendRequest((string) $command));
```

### Host commands

**Create a nameserver**
```php
use YWatchman\LaravelEPP\Models\Nameserver;
use YWatchman\LaravelEPP\Support\Xml\Commands\Host\CreateCommand;
use YWatchman\LaravelEPP\Responses\Host\CreateResponse;

$nameserver = new Nameserver(['name' => 'ns1.example.nl', 'address' => '1.2.3.4']);
$command = new CreateCommand($nameserver);
$response = new CreateResponse($epp->sendRequest((string) $command));
```

### Checking a response

All response classes extend `Response` and share a common interface:

```php
$response->isSucceeded(); // bool
$response->getCode(); // int (e.g. 1000, 2303)
$response->getMessage(); // ?string
$response->getServerTransaction(); // string
$response->getClientTransaction(); // ?string
```

## SIDN-specific notes

- Legal form values `PERSON` and `OTHER` are automatically mapped to the Dutch SIDN codes `PERSOON` and `ANDERS`. All other values are passed through as-is.
- The SIDN EPP extension namespace is `https://rxsd.domain-registry.nl/sidn-ext-epp-1.0`.

## Debugging

Set `EPP_DEBUG=true` in your `.env` to dump raw XML frames to stdout during socket I/O.

## Running tests

```bash
./vendor/bin/pest
```
20 changes: 12 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,25 @@
"ext-simplexml": "*",
"php": "^8.2",
"illuminate/support": "^11.40",
"illuminate/database": "^11.40",
"guzzlehttp/guzzle": "^7.9",
"jeremykendall/php-domain-parser": "^6.3",
"symfony/dom-crawler": "^7.2",
"symfony/css-selector": "^7.2"
"jeremykendall/php-domain-parser": "^6.4",
"symfony/dom-crawler": "^7.4.8",
"symfony/css-selector": "^7.4.9"
},
"type": "library",
"require-dev": {
"laravel/pint": "^1.20",
"pestphp/pest": "^3.7",
"orchestra/testbench": "^9.9"
"laravel/pint": "^1.29.1",
"pestphp/pest": "^3.8.6",
"orchestra/testbench": "^9.17",
"phpstan/phpstan": "^2.1"
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"scripts": {
"post-install-cmd": "@fix:pdo-constant",
"post-update-cmd": "@fix:pdo-constant",
"fix:pdo-constant": "@php scripts/fix-pdo-constant.php"
}
}
Loading