A PHP SDK for the Emarsys API v3 with OAuth 2.0 authentication and Contact Lists management.
- Contact Lists management (Create, List, Get, Delete)
- OAuth 2.0 Client Credentials authentication
- Type-safe DTOs with full PHPDoc coverage
- Comprehensive error handling with custom exceptions
- PSR-12 compliant code style
- Framework-agnostic with optional Laravel support
- Automatic token refresh and management
- PHPStan level 8 static analysis
- 100% unit test coverage
- PHP 8.3 or higher
- GuzzleHttp 7.0 or higher
You can install the package via composer:
composer require hobbii/emarsys-sdk-phpInitialize the client with your Emarsys OAuth 2.0 credentials:
use Hobbii\Emarsys\Client;
$client = new Client(
clientId: 'your-client-id',
clientSecret: 'your-client-secret'
);The SDK uses OAuth 2.0 Client Credentials flow for authentication. The access token is automatically obtained and refreshed as needed. You don't need to manage tokens manually.
- On first API call, the SDK requests an access token using your
client_idandclient_secret - The token is cached and automatically included in subsequent requests
- When the token expires, it's automatically refreshed
- All authentication is handled transparently
use Hobbii\Emarsys\Domain\ContactLists\DTOs\CreateContactList;
$contactList = new CreateContactList(
name: 'Newsletter Subscribers',
description: 'List of users subscribed to our newsletter',
);
try {
$contactListId = $client->contactLists()->create($contactList);
echo "Created contact list ID: {$contactListId})";
} catch (\Hobbii\Emarsys\Domain\Exceptions\AuthenticationException $e) {
echo "OAuth authentication failed: {$e->getMessage()}";
} catch (\Hobbii\Emarsys\Domain\Exceptions\ApiException $e) {
echo "Error: {$e->getMessage()}";
}try {
$contactLists = $client->contactLists()->list();
foreach ($contactLists as $contactList) {
echo "ID: {$contactList->id}, Name: {$contactList->name}\n";
}
echo "Total contact lists: {$contactLists->count()}";
} catch (\Hobbii\Emarsys\Domain\Exceptions\ApiException $e) {
echo "Error: {$e->getMessage()}";
}try {
$success = $client->contactLists()->delete(123);
if ($success) {
echo "Contact list deleted successfully";
}
} catch (\Hobbii\Emarsys\Domain\Exceptions\ApiException $e) {
echo "Error: {$e->getMessage()}";
}The SDK provides comprehensive error handling with custom exception classes:
EmarsysException- Base exception for all SDK errorsApiException- General API errors with HTTP status codes and response detailsAuthenticationException- Authentication failuresRateLimitException- Rate limit exceeded (429 responses)
use Hobbii\Emarsys\Domain\Exceptions\ApiException;
use Hobbii\Emarsys\Domain\Exceptions\AuthenticationException;
use Hobbii\Emarsys\Domain\Exceptions\EmarsysException;
try {
$contactLists = $client->contactLists()->list();
} catch (AuthenticationException $e) {
echo "OAuth authentication failed: {$e->getMessage()}";
echo "Please check your client_id and client_secret.";
} catch (ApiException $e) {
echo "API error: {$e->getMessage()}";
} catch (EmarsysException $e) {
echo "General Emarsys error: {$e->getMessage()}";
}The Emarsys API enforces rate limits to ensure fair usage and maintain system stability. When the rate limit is exceeded, the API returns a 429 (Too Many Requests) response, and the SDK throws a RateLimitException.
Emarsys Rate Limit Headers:
X-RateLimit-Limit: Request limit per minuteX-Ratelimit-Remaining: The number of requests left for the time windowX-RateLimit-Reset: The time when the rate limit window resets (Unix timestamp)
The exception includes all rate limit information:
use Hobbii\Emarsys\Domain\Exceptions\RateLimitException;
try {
$contactLists = $client->contactLists()->list();
} catch (RateLimitException $e) {
// Get information about the rate limit
echo "Rate limit exceeded!\n";
echo "Retry after: {$e->retryAfterSeconds} seconds\n";
if ($e->resetTimestamp !== null) {
$resetTime = date('Y-m-d H:i:s', $e->resetTimestamp);
echo "Rate limit resets at: {$resetTime}\n";
}
if ($e->limitRemaining !== null) {
echo "Requests remaining: {$e->limitRemaining}\n";
}
if ($e->limitTotal !== null) {
echo "Total limit: {$e->limitTotal} per minute\n";
}
// Wait and retry
sleep($e->retryAfterSeconds);
$contactLists = $client->contactLists()->list();
}Note: In the current version (v1.0.0-RC1), rate limit handling is manual - you need to catch the exception and implement retry logic yourself. Automatic retry with exponential backoff is planned for a future release. See RATE_LIMITING_SPEC.md for details.
The SDK uses type-safe DTOs for all data exchange:
A Docker setup is provided for consistent development and testing across different environments. This is especially useful if you have PHP version or extension conflicts on your local machine.
- Docker Desktop or Docker Engine
- Docker Compose (included with Docker Desktop)
# Build and start the container
make up
# Install dependencies
make install
# Run tests
make test
# Run all checks (format, analyse, test)
make check
# Open an interactive shell
make shellmake help # Display all available commands
make build # Build the Docker image
make up # Start the container
make down # Stop the container
make shell # Open interactive shell
make install # Install composer dependencies
make test # Run PHPUnit tests
make format # Format code with Pint
make analyse # Run PHPStan analysis
make check # Run all checks
make integration-test # Run integration tests
make clean # Remove containers and volumes
make rebuild # Clean and rebuild everythingIf you prefer to use Docker Compose directly:
# Start container
docker-compose up -d
# Run tests
docker-compose exec php composer test
# Run PHPStan
docker-compose exec php composer analyse
# Format code
docker-compose exec php composer format
# Open shell
docker-compose exec php sh
# Stop container
docker-compose downTo run integration tests with Docker, pass your credentials as environment variables:
# Set environment variables in your shell
export EMARSYS_CLIENT_ID='your-client-id'
export EMARSYS_CLIENT_SECRET='your-client-secret'
# Or create a .env file (recommended)
cp .env.example .env
# Edit .env with your credentials
# Run integration tests
make integration-testThe Docker setup automatically passes through EMARSYS_* environment variables to the container.
# Run all tests
composer test
# OR
make test
# Run code formatting
composer format
# OR
make format
# Run static analysis with PHPStan
composer analyse
# OR
make analyse
# Run everything (format, analyse, test)
composer check
# OR
make checkTo test the SDK with your actual Emarsys API credentials:
Option 1: Environment Variables
export EMARSYS_CLIENT_ID='your-client-id'
export EMARSYS_CLIENT_SECRET='your-client-secret'Option 2: .env File (Recommended)
# Copy the template and edit with your credentials
cp .env.example .env
# Edit .env file with your actual credentials# Run quick connection test
make integration-test test=quick
# OR
composer test-integration quick
# OR
php run-integration-tests.php quick# Run comprehensive test (creates and deletes a test contact list)
make integration-test test=tests/Integration/ContactListsIntegrationTest.php
# OR
composer test-integration tests/Integration/ContactListsIntegrationTest.php
# OR
php run-integration-tests.php tests/Integration/ContactListsIntegrationTest.phpThe integration test performs these operations:
- ✅ Authentication - Tests OAuth 2.0 login
- ✅ List Contact Lists - Retrieves existing lists (read-only)
- ✅ Create Contact List - Creates a test list
- ✅ Get Contact List - Retrieves the created list by ID
- ✅ Delete Contact List - Cleans up the test list
Note: The integration test creates and deletes a test contact list but doesn't affect your existing data.
See tests/Integration/README.md for more details.
The project uses several tools to ensure code quality:
- PHPUnit: Unit testing with high coverage
- PHPStan: Static analysis at level 8 (strictest)
- Laravel Pint: PSR-12 code formatting
- Type Safety: Full PHP 8.3 type hints with strict typing
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.