PHP SDK
The Unizo PHP SDK provides a convenient and modern way to interact with the Unizo API from PHP applications. It supports PHP 7.4+ and follows PSR standards for better interoperability.
Installation
Composer (Recommended)
composer require unizo/sdk
Manual Installation
Download the latest release and include the autoloader:
require_once 'vendor/autoload.php';
Basic Setup
<?php
require_once 'vendor/autoload.php';
use Unizo\Client;
// Initialize the client
$client = new Client([
'api_key' => 'your-api-key',
'environment' => 'production' // or 'sandbox'
]);
Configuration Options
use Unizo\Client;
use Unizo\Config\RetryConfig;
$client = new Client([
'api_key' => 'your-api-key',
'environment' => 'production',
'timeout' => 30, // Request timeout in seconds
'base_url' => 'https://api.unizo.com',
'user_agent' => 'MyApp/1.0.0',
'retry_config' => new RetryConfig([
'attempts' => 3,
'delay' => 1.0,
'backoff_factor' => 2.0
]),
'debug' => false, // Enable debug logging
'logger' => new \Monolog\Logger('unizo')
]);
Environment Variables
// .env file
UNIZO_API_KEY=your-api-key
UNIZO_ENVIRONMENT=production
// Load configuration from environment
$client = new Client([
'api_key' => $_ENV['UNIZO_API_KEY'] ?? getenv('UNIZO_API_KEY'),
'environment' => $_ENV['UNIZO_ENVIRONMENT'] ?? 'production'
]);
Authentication
API Key Authentication
// Set during initialization
$client = new Client(['api_key' => 'your-api-key']);
// Or set later
$client->setApiKey('your-api-key');
// Using a temporary API key
$client->withApiKey('temp-api-key', function ($tempClient) {
$identity = $tempClient->identity()->get();
return $identity;
});
Bearer Token Authentication
// For user-specific operations
$client = new Client(['bearer_token' => 'user-access-token']);
// Or set dynamically
$client->setBearerToken('user-access-token');
Usage Examples
Identity Management
use Unizo\Exception\UnizoException;
try {
// Get user identity
$identity = $client->identity()->get();
echo "User ID: " . $identity->getId() . "\n";
echo "Email: " . $identity->getEmail() . "\n";
// Update user profile
$updatedProfile = $client->identity()->update([
'name' => 'John Doe',
'email' => 'john@example.com',
'metadata' => [
'department' => 'Engineering',
'role' => 'Senior Developer'
]
]);
echo "Profile updated: " . $updatedProfile->getName() . "\n";
} catch (UnizoException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
Source Code Management
// List repositories
$repositories = $client->sourceCode()->repositories()->list([
'page' => 1,
'limit' => 50,
'provider' => 'github'
]);
foreach ($repositories->getData() as $repo) {
echo "Repository: " . $repo->getName() . " (" . $repo->getUrl() . ")\n";
}
// Get repository details
$repo = $client->sourceCode()->repositories()->get('repo-id');
echo "Repository stats: " . json_encode($repo->getStats()) . "\n";
// Create webhook
$webhook = $client->sourceCode()->webhooks()->create([
'repository_id' => 'repo-id',
'url' => 'https://your-app.com/webhooks/source-code',
'events' => ['push', 'pull_request'],
'secret' => 'webhook-secret'
]);
echo "Webhook created: " . $webhook->getId() . "\n";
Ticketing System
use Unizo\Model\TicketPriority;
use Unizo\Model\TicketStatus;
// Create a ticket
$ticket = $client->ticketing()->tickets()->create([
'title' => 'Bug Report: Login Issues',
'description' => 'Users are experiencing login failures after the latest update',
'priority' => TicketPriority::HIGH,
'assignee' => 'user-id',
'tags' => ['bug', 'login', 'urgent'],
'due_date' => (new DateTime())->add(new DateInterval('P3D')) // 3 days from now
]);
echo "Created ticket: " . $ticket->getId() . "\n";
// List tickets with filtering
$tickets = $client->ticketing()->tickets()->list([
'status' => TicketStatus::OPEN,
'priority' => TicketPriority::HIGH,
'assignee' => 'current-user',
'page' => 1,
'limit' => 25,
'created_after' => (new DateTime())->sub(new DateInterval('P7D')) // 1 week ago
]);
echo "Found " . $tickets->getTotal() . " tickets\n";
// Update ticket
$updatedTicket = $client->ticketing()->tickets()->update($ticket->getId(), [
'status' => TicketStatus::IN_PROGRESS,
'comments' => [[
'text' => 'Started investigating the issue',
'author' => 'developer-id',
'timestamp' => new DateTime()
]]
]);
Communications
// Send email with template
$emailResult = $client->communications()->email()->send([
'to' => ['user@example.com'],
'subject' => 'Welcome to Unizo',
'template' => 'welcome-template',
'variables' => [
'user_name' => 'John Doe',
'activation_link' => 'https://app.unizo.com/activate/token'
],
'attachments' => [[
'filename' => 'welcome_guide.pdf',
'content' => file_get_contents('path/to/welcome_guide.pdf'),
'content_type' => 'application/pdf'
]]
]);
echo "Email sent: " . $emailResult->getMessageId() . "\n";
// Send plain text email
$plainEmail = $client->communications()->email()->send([
'to' => ['user@example.com'],
'subject' => 'System Notification',
'text' => 'Your account has been activated successfully.'
]);
// Send SMS
$smsResult = $client->communications()->sms()->send([
'to' => '+1234567890',
'message' => 'Your verification code is: 123456',
'sender_id' => 'UNIZO'
]);
echo "SMS sent: " . $smsResult->getMessageId() . "\n";
Incident Management
use Unizo\Model\IncidentSeverity;
use Unizo\Model\IncidentStatus;
// Create incident
$incident = $client->incidents()->create([
'title' => 'API Service Degradation',
'description' => 'Response times increased by 200% across all endpoints',
'severity' => IncidentSeverity::MAJOR,
'status' => IncidentStatus::INVESTIGATING,
'services' => ['api-gateway', 'database', 'cache'],
'affected_users' => 1500
]);
echo "Created incident: " . $incident->getId() . "\n";
// List active incidents
$activeIncidents = $client->incidents()->list([
'status' => [IncidentStatus::OPEN, IncidentStatus::INVESTIGATING],
'severity' => [IncidentSeverity::CRITICAL, IncidentSeverity::MAJOR]
]);
// Update incident with timeline
$client->incidents()->update($incident->getId(), [
'status' => IncidentStatus::RESOLVED,
'resolution' => 'Scaled up database instances and optimized queries',
'timeline' => [[
'timestamp' => new DateTime(),
'event' => 'Issue resolved',
'description' => 'Database performance restored to normal levels'
]]
]);
Error Handling
Exception Types
use Unizo\Exception\UnizoException;
use Unizo\Exception\ValidationException;
use Unizo\Exception\AuthenticationException;
use Unizo\Exception\RateLimitException;
use Unizo\Exception\NotFoundException;
use Unizo\Exception\ServerException;
try {
$identity = $client->identity()->get();
} catch (ValidationException $e) {
echo "Validation error: " . $e->getMessage() . "\n";
foreach ($e->getFieldErrors() as $field => $error) {
echo "$field: $error\n";
}
} catch (AuthenticationException $e) {
echo "Authentication failed: " . $e->getMessage() . "\n";
// Handle re-authentication
} catch (RateLimitException $e) {
echo "Rate limit exceeded. Retry after: " . $e->getRetryAfter() . " seconds\n";
// Implement backoff strategy
} catch (NotFoundException $e) {
echo "Resource not found: " . $e->getMessage() . "\n";
} catch (ServerException $e) {
echo "Server error: " . $e->getMessage() . "\n";
// Log for debugging
} catch (UnizoException $e) {
echo "Unizo API error: " . $e->getMessage() . "\n";
echo "Status code: " . $e->getStatusCode() . "\n";
echo "Error code: " . $e->getCode() . "\n";
}
Retry Logic
class RetryHelper
{
public static function withRetry(callable $operation, int $maxAttempts = 3, float $baseDelay = 1.0)
{
$attempt = 1;
while ($attempt <= $maxAttempts) {
try {
return $operation();
} catch (RateLimitException $e) {
if ($attempt === $maxAttempts) {
throw $e;
}
$delay = $baseDelay * pow(2, $attempt - 1);
sleep(max($delay, $e->getRetryAfter()));
$attempt++;
}
}
}
}
// Usage
$identity = RetryHelper::withRetry(function () use ($client) {
return $client->identity()->get();
}, 3, 1.0);
Circuit Breaker Pattern
class CircuitBreaker
{
private int $failureThreshold;
private int $recoveryTimeout;
private int $failureCount = 0;
private ?DateTime $lastFailureTime = null;
private string $state = 'closed';
public function __construct(int $failureThreshold = 5, int $recoveryTimeout = 60)
{
$this->failureThreshold = $failureThreshold;
$this->recoveryTimeout = $recoveryTimeout;
}
public function call(callable $operation)
{
switch ($this->state) {
case 'open':
if ($this->lastFailureTime &&
(time() - $this->lastFailureTime->getTimestamp()) > $this->recoveryTimeout) {
$this->state = 'half-open';
return $this->attemptCall($operation);
} else {
throw new Exception('Circuit breaker is open');
}
case 'half-open':
case 'closed':
return $this->attemptCall($operation);
}
}
private function attemptCall(callable $operation)
{
try {
$result = $operation();
$this->resetFailureCount();
return $result;
} catch (UnizoException $e) {
$this->recordFailure();
throw $e;
}
}
private function recordFailure(): void
{
$this->failureCount++;
$this->lastFailureTime = new DateTime();
if ($this->failureCount >= $this->failureThreshold) {
$this->state = 'open';
}
}
private function resetFailureCount(): void
{
$this->failureCount = 0;
$this->state = 'closed';
}
}
// Usage
$circuitBreaker = new CircuitBreaker(3, 30);
try {
$identity = $circuitBreaker->call(function () use ($client) {
return $client->identity()->get();
});
} catch (Exception $e) {
echo "Service temporarily unavailable: " . $e->getMessage() . "\n";
}
Laravel Integration
Service Provider
<?php
// app/Providers/UnizoServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Unizo\Client;
class UnizoServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Client::class, function ($app) {
return new Client([
'api_key' => config('services.unizo.api_key'),
'environment' => config('services.unizo.environment', 'production'),
'timeout' => 30,
'logger' => $app->make('log')
]);
});
}
public function boot()
{
//
}
}
Configuration
<?php
// config/services.php
return [
// ... other services
'unizo' => [
'api_key' => env('UNIZO_API_KEY'),
'environment' => env('UNIZO_ENVIRONMENT', 'production'),
'webhook_secret' => env('UNIZO_WEBHOOK_SECRET'),
],
];
Service Class
<?php
// app/Services/UnizoService.php
namespace App\Services;
use Unizo\Client;
use App\Models\User;
class UnizoService
{
private Client $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function createSupportTicket(User $user, string $subject, string $message)
{
return $this->client->ticketing()->tickets()->create([
'title' => $subject,
'description' => $message,
'priority' => 'medium',
'metadata' => [
'user_id' => $user->id,
'user_email' => $user->email
]
]);
}
public function getUserTickets(User $user)
{
$result = $this->client->ticketing()->tickets()->list([
'metadata' => ['user_email' => $user->email],
'status' => 'open'
]);
return $result->getData();
}
}
Controller Usage
<?php
// app/Http/Controllers/SupportTicketController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\UnizoService;
use Unizo\Exception\UnizoException;
class SupportTicketController extends Controller
{
private UnizoService $unizoService;
public function __construct(UnizoService $unizoService)
{
$this->unizoService = $unizoService;
}
public function store(Request $request)
{
$request->validate([
'subject' => 'required|string|max:255',
'message' => 'required|string'
]);
try {
$ticket = $this->unizoService->createSupportTicket(
$request->user(),
$request->subject,
$request->message
);
return redirect()->route('support.index')
->with('success', 'Ticket created successfully');
} catch (UnizoException $e) {
\Log::error('Unizo API error: ' . $e->getMessage());
return back()->with('error', 'Failed to create ticket');
}
}
public function index(Request $request)
{
try {
$tickets = $this->unizoService->getUserTickets($request->user());
return view('support.index', compact('tickets'));
} catch (UnizoException $e) {
return view('support.index', ['tickets' => []])
->with('error', 'Unable to load tickets at this time');
}
}
}
Queue Jobs
<?php
// app/Jobs/CreateIncidentJob.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Unizo\Client;
use Unizo\Exception\RateLimitException;
use Unizo\Exception\ServerException;
class CreateIncidentJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $timeout = 60;
private string $title;
private string $description;
private string $severity;
public function __construct(string $title, string $description, string $severity = 'major')
{
$this->title = $title;
$this->description = $description;
$this->severity = $severity;
}
public function handle(Client $client)
{
$incident = $client->incidents()->create([
'title' => $this->title,
'description' => $this->description,
'severity' => $this->severity,
'status' => 'open'
]);
// Notify relevant teams
// NotificationService::notifyIncidentCreated($incident);
}
public function retryUntil()
{
return now()->addMinutes(10);
}
public function failed(\Throwable $exception)
{
\Log::error('Failed to create incident: ' . $exception->getMessage());
}
}
Webhook Handling
Raw PHP Example
<?php
// webhook.php
require_once 'vendor/autoload.php';
use Unizo\Webhook\WebhookValidator;
$signature = $_SERVER['HTTP_X_UNIZO_SIGNATURE'] ?? '';
$payload = file_get_contents('php://input');
$webhookSecret = $_ENV['WEBHOOK_SECRET'];
$validator = new WebhookValidator();
if (!$validator->validateSignature($payload, $signature, $webhookSecret)) {
http_response_code(401);
echo 'Invalid signature';
exit;
}
$event = json_decode($payload, true);
switch ($event['type']) {
case 'ticket.created':
handleTicketCreated($event['data']);
break;
case 'incident.updated':
handleIncidentUpdated($event['data']);
break;
default:
error_log("Unknown event type: " . $event['type']);
}
http_response_code(200);
echo 'OK';
function handleTicketCreated(array $ticketData): void
{
error_log("New ticket created: " . $ticketData['id']);
// Process ticket creation
}
function handleIncidentUpdated(array $incidentData): void
{
error_log("Incident updated: " . $incidentData['id']);
// Process incident update
}
Laravel Webhook Controller
<?php
// app/Http/Controllers/WebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Unizo\Webhook\WebhookValidator;
use App\Jobs\ProcessWebhookJob;
class WebhookController extends Controller
{
public function unizo(Request $request): Response
{
$signature = $request->header('X-Unizo-Signature');
$payload = $request->getContent();
$validator = new WebhookValidator();
if (!$validator->validateSignature($payload, $signature, config('services.unizo.webhook_secret'))) {
return response('Invalid signature', 401);
}
$event = $request->json()->all();
// Process webhook asynchronously
ProcessWebhookJob::dispatch($event);
return response('OK');
}
}
Testing
PHPUnit Examples
<?php
// tests/Unit/UnizoServiceTest.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use Mockery;
use Unizo\Client;
use App\Services\UnizoService;
use App\Models\User;
class UnizoServiceTest extends TestCase
{
private $mockClient;
private $unizoService;
protected function setUp(): void
{
parent::setUp();
$this->mockClient = Mockery::mock(Client::class);
$this->unizoService = new UnizoService($this->mockClient);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function testCreateSupportTicket()
{
$user = new User(['id' => 1, 'email' => 'test@example.com']);
$ticketService = Mockery::mock();
$ticketsService = Mockery::mock();
$this->mockClient->shouldReceive('ticketing')->andReturn($ticketService);
$ticketService->shouldReceive('tickets')->andReturn($ticketsService);
$expectedTicket = (object) ['id' => 'ticket-123', 'title' => 'Help needed'];
$ticketsService->shouldReceive('create')
->with([
'title' => 'Help needed',
'description' => 'I need assistance',
'priority' => 'medium',
'metadata' => [
'user_id' => 1,
'user_email' => 'test@example.com'
]
])
->andReturn($expectedTicket);
$result = $this->unizoService->createSupportTicket($user, 'Help needed', 'I need assistance');
$this->assertEquals('ticket-123', $result->id);
}
}
Integration Testing
<?php
// tests/Integration/UnizoClientTest.php
namespace Tests\Integration;
use PHPUnit\Framework\TestCase;
use Unizo\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
class UnizoClientTest extends TestCase
{
private Client $client;
private MockHandler $mockHandler;
protected function setUp(): void
{
parent::setUp();
$this->mockHandler = new MockHandler();
$handlerStack = HandlerStack::create($this->mockHandler);
$this->client = new Client([
'api_key' => 'test-api-key',
'http_client' => new \GuzzleHttp\Client(['handler' => $handlerStack])
]);
}
public function testGetIdentity()
{
$this->mockHandler->append(
new Response(200, [], json_encode([
'id' => 'user-123',
'email' => 'test@example.com'
]))
);
$identity = $this->client->identity()->get();
$this->assertEquals('user-123', $identity->getId());
$this->assertEquals('test@example.com', $identity->getEmail());
}
public function testCreateTicket()
{
$this->mockHandler->append(
new Response(201, [], json_encode([
'id' => 'ticket-123',
'title' => 'Test Ticket',
'status' => 'open'
]))
);
$ticket = $this->client->ticketing()->tickets()->create([
'title' => 'Test Ticket',
'description' => 'This is a test ticket'
]);
$this->assertEquals('ticket-123', $ticket->getId());
$this->assertEquals('open', $ticket->getStatus());
}
}
Performance Optimization
Connection Pooling
use GuzzleHttp\Client as GuzzleClient;
// Configure HTTP client with connection pooling
$httpClient = new GuzzleClient([
'timeout' => 30,
'connect_timeout' => 10,
'pool_size' => 20,
'keepalive' => true
]);
$client = new Client([
'api_key' => 'your-api-key',
'http_client' => $httpClient
]);
Caching
use Psr\SimpleCache\CacheInterface;
class CachedUnizoClient
{
private Client $client;
private CacheInterface $cache;
private int $ttl;
public function __construct(Client $client, CacheInterface $cache, int $ttl = 300)
{
$this->client = $client;
$this->cache = $cache;
$this->ttl = $ttl;
}
public function getIdentity()
{
$cacheKey = 'unizo_identity';
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$identity = $this->client->identity()->get();
$this->cache->set($cacheKey, $identity, $this->ttl);
return $identity;
}
}
Batch Operations
// Batch ticket creation
$ticketsData = [
['title' => 'Issue 1', 'description' => 'First issue'],
['title' => 'Issue 2', 'description' => 'Second issue'],
['title' => 'Issue 3', 'description' => 'Third issue']
];
// Process in batches
$batchSize = 10;
$chunks = array_chunk($ticketsData, $batchSize);
foreach ($chunks as $batch) {
$results = $client->ticketing()->tickets()->createBatch($batch);
echo "Created " . count($results) . " tickets\n";
}
Logging
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Psr\Log\LoggerInterface;
// Create logger
$logger = new Logger('unizo');
$logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
// Use with client
$client = new Client([
'api_key' => 'your-api-key',
'logger' => $logger,
'debug' => true
]);
// Custom logging middleware
class LoggingMiddleware
{
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function __invoke(callable $handler)
{
return function ($request, array $options) use ($handler) {
$this->logger->info('Request: ' . $request->getMethod() . ' ' . $request->getUri());
$promise = $handler($request, $options);
return $promise->then(function ($response) {
$this->logger->info('Response: ' . $response->getStatusCode());
return $response;
});
};
}
}
// Add middleware to client
$client->addMiddleware(new LoggingMiddleware($logger));
Resources
Support
- Create issues on GitHub
- Email: sdk-support@unizo.com
- Discord: Unizo Developers
Requirements
- PHP 7.4+
- Composer 2.0+
- ext-json
- ext-curl
- guzzlehttp/guzzle >= 7.0
- psr/log >= 1.1 (for logging)
- psr/simple-cache >= 1.0 (for caching, optional)