Skip to main content

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 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

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)