jefyokta / oktaax
Oktaax a openswoole http server library
Requires
- php: >=8.0
- firebase/php-jwt: ^6.10
Requires (Dev)
- illuminate/http: ^12.9
- mockery/mockery: ^1.6
- pestphp/pest: ^3.8
This package is auto-updated.
Last update: 2026-04-26 10:52:32 UTC
README
Oktaax is a lightweight, high-performance PHP HTTP and WebSocket server framework built on Swoole. It is designed for real-time applications and API-first workflows with minimal boilerplate, featuring built-in asynchronous programming support with Promises and coroutines.
Table of Contents
- π Requirements
- π¦ Installation
- π§ Quick Start
- π HTTP Routing
- π§© Request API
- π§© Response API
- β‘ Asynchronous Programming
- π Authentication & Security
- π οΈ Server Configuration
- πΈοΈ Middleware
- π¬ WebSocket Support
- π Error Handling
- π File Uploads
- π Benchmarks
- π Examples
- π€ Contributing
- π License
π Requirements
- PHP 8.1+
- Swoole extension installed (
pecl install swooleorpecl install openswoole)
π¦ Installation
composer require jefyokta/oktaax
π§ Quick Start
Basic HTTP Server
<?php require 'vendor/autoload.php'; use Oktaax\Oktaax; use Oktaax\Http\Request; use Oktaax\Http\Response; $app = new Oktaax(); $app->get('/', function (Request $request, Response $response) { $response->end('Hello World!'); }); $app->listen(3000);
Start the server and visit http://localhost:3000.
HTTP + WebSocket Server
<?php require 'vendor/autoload.php'; use Oktaax\Oktaax; use Oktaax\Trait\HasWebsocket; $app = new class extends Oktaax { use HasWebsocket; }; $app->get('/', fn($req, $res) => $res->end('HTTP + WS Server')); $app->ws('hello', function ($server, $client) { $server->reply($client, ['message' => 'Hello from WebSocket!']); }); $app->listen(3000);
π HTTP Routing
Oktaax supports all common HTTP verbs:
get(path, handler)post(path, handler)put(path, handler)delete(path, handler)patch(path, handler)options(path, handler)head(path, handler)
Basic Routes
$app->get('/users', function ($req, $res) { $res->json(['users' => []]); }); $app->post('/users', function ($req, $res) { $data = $req->body(); // Create user logic $res->status(201)->json(['created' => true]); });
Dynamic Parameters
$app->get('/users/{id}', function ($req, $res) { $id = $req->params['id']; $res->json(['user_id' => $id]); }); $app->get('/posts/{category}/{slug}', function ($req, $res) { $category = $req->params['category']; $slug = $req->params['slug']; // Fetch post logic $res->json(['category' => $category, 'slug' => $slug]); });
Route Groups with Middleware
$app->middleware([$authMiddleware], function ($router) { $router->get('/profile', $profileHandler); $router->post('/settings', $settingsHandler); $router->delete('/account', $deleteHandler); });
Path-specific Middleware
$app->useFor('/api', function ($req, $res, $next) { $res->header('X-API-Version', '1.0'); $next(); });
π§© Request API
The Request object provides extensive helpers for accessing HTTP request data:
Query Parameters & Body
$app->get('/search', function ($req, $res) { $query = $req->input('q', 'default'); $page = $req->input('page', 1); $limit = $req->input('limit', 10); // Search logic here $res->json(['query' => $query, 'page' => $page]); }); $app->post('/submit', function ($req, $res) { $name = $req->post('name'); $email = $req->post('email'); $data = $req->body(); // Raw body as array $res->json(['received' => $data]); });
Headers & Cookies
$app->get('/headers', function ($req, $res) { $userAgent = $req->header('User-Agent'); $authToken = $req->header('Authorization'); $sessionId = $req->cookie('session_id'); $res->json([ 'user_agent' => $userAgent, 'auth_token' => $authToken, 'session_id' => $sessionId ]); });
Content Type Detection
$app->post('/upload', function ($req, $res) { if ($req->isJson()) { $data = $req->json(); } elseif ($req->isFormSubmission()) { $data = $req->body(); } $res->json(['data' => $data]); });
Validation
$app->post('/register', function ($req, $res) { $validated = $req->validate([ 'email' => 'required|email', 'password' => 'required|min:8', 'name' => 'required|string' ]); if ($validated->fails()) { return $res->status(422)->json(['errors' => $validated->errors()]); } // Registration logic $res->json(['message' => 'User registered successfully']); });
π§© Response API
The Response object offers fluent methods for crafting HTTP responses:
JSON Responses
$app->get('/api/users', function ($req, $res) { $users = [ ['id' => 1, 'name' => 'John'], ['id' => 2, 'name' => 'Jane'] ]; return $res->json($users); });
Status Codes & Headers
$app->post('/api/users', function ($req, $res) { // Create user logic $res->status(201) ->header('Location', '/api/users/123') ->json(['id' => 123, 'created' => true]); });
Redirects
$app->get('/old-path', function ($req, $res) { $res->redirect('/new-path', 301); }); $app->get('/dashboard', function ($req, $res) { if (!$req->cookie('auth')) { return $res->redirect('/login'); } $res->end('Welcome to dashboard'); });
Cookies
$app->post('/login', function ($req, $res) { // Auth logic $res->cookie('session', 'abc123', time() + 3600, '/', null, false, true) ->json(['logged_in' => true]); }); $app->post('/logout', function ($req, $res) { $res->cookie('session', '', time() - 3600) ->json(['logged_out' => true]); });
File Downloads
$app->get('/download/{filename}', function ($req, $res) { $filename = $req->params['filename']; $filepath = "/path/to/files/$filename"; if (file_exists($filepath)) { $res->header('Content-Type', mime_content_type($filepath)) ->sendfile($filepath); } else { $res->status(404)->end('File not found'); } });
Views (with PHP templates)
$app->setView(new \Oktaax\Views\PhpView(__DIR__ . '/views')); $app->get('/page', function ($req, $res) { $res->render('welcome', ['title' => 'Welcome Page']); });
β‘ Asynchronous Programming
Oktaax provides powerful asynchronous programming capabilities using Swoole coroutines and Promises.
Promises
use Oktaax\Core\Promise; $app->get('/async-data', function ($req, $res) { $promise = new Promise(function ($resolve, $reject) { // Simulate async operation setTimeout(function () use ($resolve) { $resolve(['data' => 'Async result']); }, 1000); }); $promise->then(function ($data) use ($res) { $res->json($data); })->catch(function ($error) use ($res) { $res->status(500)->json(['error' => $error]); }); return $promise; // Return promise for auto-resolution });
Async/Await Pattern
use function Oktaax\async; use function Oktaax\await; $app->get('/async-await', async(function ($req, $res) { $data1 = await(fetchDataFromAPI('/api/endpoint1')); $data2 = await(fetchDataFromAPI('/api/endpoint2')); $combined = array_merge($data1, $data2); $res->json($combined); }));
Promise.all for Parallel Operations
$app->get('/parallel', async(function ($req, $res) { $promises = [ fetchUserData(1), fetchUserData(2), fetchUserData(3) ]; $results = await(Promise::all($promises)); $res->json(['users' => $results]); }));
Async Middleware
$app->use(async(function ($req, $res, $next) { $user = await(authenticateUser($req->header('Authorization'))); $req->user = $user; $next(); }));
Async File Operations
$app->get('/read-file', async(function ($req, $res) { $filename = $req->input('file', 'default.txt'); try { $content = await(readFileAsync($filename)); $res->json(['content' => $content]); } catch (Exception $e) { $res->status(404)->json(['error' => 'File not found']); } }));
Database Operations (Async)
$app->get('/users/{id}', async(function ($req, $res) { $id = $req->params['id']; try { $user = await(queryDatabase("SELECT * FROM users WHERE id = ?", [$id])); $res->json($user); } catch (Exception $e) { $res->status(500)->json(['error' => 'Database error']); } }));
π Authentication & Security
CSRF Protection
$app->useCsrf('your-secret-key', 300); // 5 minutes expiry $app->get('/form', function ($req, $res) { $token = xcsrf_token(); $res->json(['csrf_token' => $token]); }); $app->post('/submit', function ($req, $res) { // CSRF token is automatically validated $res->json(['submitted' => true]); });
JWT Authentication
use Firebase\JWT\JWT; $app->post('/login', function ($req, $res) { $credentials = $req->body(); // Validate credentials if ($credentials['username'] === 'admin' && $credentials['password'] === 'password') { $payload = [ 'iss' => 'oktaax-app', 'sub' => 1, 'iat' => time(), 'exp' => time() + 3600 ]; $jwt = JWT::encode($payload, 'your-secret-key', 'HS256'); $res->json(['token' => $jwt]); } else { $res->status(401)->json(['error' => 'Invalid credentials']); } }); $app->middleware(function ($req, $res, $next) { $token = $req->header('Authorization'); if (!$token || !str_starts_with($token, 'Bearer ')) { return $res->status(401)->json(['error' => 'No token provided']); } try { $decoded = JWT::decode(substr($token, 7), 'your-secret-key', ['HS256']); $req->user = $decoded; $next(); } catch (Exception $e) { $res->status(401)->json(['error' => 'Invalid token']); } }, function ($router) { $router->get('/protected', function ($req, $res) { $res->json(['message' => 'Protected resource', 'user' => $req->user]); }); });
HTTPS Support
$app->withSSL('/path/to/cert.pem', '/path/to/key.pem'); $app->listen(443);
π οΈ Server Configuration
Basic Configuration
$app->setServer([ 'worker_num' => 4, 'max_request' => 1000, 'daemonize' => false, 'log_file' => '/var/log/oktaax.log' ]); $app->listen(3000);
Advanced Options
$app->setServer('worker_num', 8) ->setServer('task_worker_num', 2) ->setServer('max_conn', 10000) ->setServer('buffer_output_size', 32 * 1024 * 1024); // 32MB
Application Configuration
use Oktaax\Types\OktaaxConfig; $app->setConfig(new OktaaxConfig([ 'debug' => true, 'timezone' => 'UTC', 'upload' => [ 'max_size' => 10 * 1024 * 1024, // 10MB 'temp_dir' => '/tmp/uploads' ] ]));
πΈοΈ Middleware
Global Middleware
$app->use(function ($req, $res, $next) { // CORS headers $res->header('Access-Control-Allow-Origin', '*'); $res->header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS'); $res->header('Access-Control-Allow-Headers', 'Content-Type,Authorization'); // Logging $start = microtime(true); $next(); $duration = microtime(true) - $start; Console::log("Request to {$req->path()} took {$duration}s"); });
Class-based Middleware
use Oktaax\Contracts\Middleware; class AuthMiddleware implements Middleware { public function handle($req, $res, $next) { $token = $req->header('Authorization'); if (!$token) { return $res->status(401)->json(['error' => 'Unauthorized']); } // Validate token logic $req->user = ['id' => 1, 'name' => 'John']; $next(); } } class RateLimitMiddleware implements Middleware { private $requests = []; public function handle($req, $res, $next) { $ip = $req->header('X-Forwarded-For') ?? '127.0.0.1'; $now = time(); // Simple rate limiting (max 10 requests per minute) if (!isset($this->requests[$ip])) { $this->requests[$ip] = []; } $this->requests[$ip] = array_filter($this->requests[$ip], function ($time) use ($now) { return $now - $time < 60; }); if (count($this->requests[$ip]) >= 10) { return $res->status(429)->json(['error' => 'Too many requests']); } $this->requests[$ip][] = $now; $next(); } } $app->use(new AuthMiddleware()); $app->use(new RateLimitMiddleware());
Async Middleware
$app->use(async(function ($req, $res, $next) { $user = await(authenticateUser($req->header('Authorization'))); $req->user = $user; $next(); }));
π¬ WebSocket Support
Extend Oktaax with WebSocket capabilities using the HasWebsocket trait:
use Oktaax\Trait\HasWebsocket; $app = new class extends Oktaax { use HasWebsocket; };
Basic WebSocket Events
$app->ws('message', function ($server, $client) { $data = $client->data; // Received data $server->reply($client, ['echo' => $data]); }); $app->ws('broadcast', function ($server, $client) { $message = $client->data['message']; $server->broadcast(['message' => $message, 'from' => $client->fd]); });
Connection Management
$app->gate(function ($server, $request) { // Validate connection before accepting $token = $request->header('Sec-WebSocket-Protocol'); if (!$token || !validateToken($token)) { return false; // Reject connection } return true; // Accept connection }); $app->exit(function ($server, $fd) { Console::log("Client $fd disconnected"); // Cleanup logic });
Client Data Storage
$app->table(function (\Swoole\Table $table) { $table->column('user_id', \Swoole\Table::TYPE_INT); $table->column('username', \Swoole\Table::TYPE_STRING, 32); $table->create(); }, 1024); $app->ws('join', function ($server, $client) { $userId = $client->data['user_id']; $username = $client->data['username']; $server->table->set($client->fd, [ 'user_id' => $userId, 'username' => $username ]); $server->broadcast(['type' => 'user_joined', 'username' => $username]); });
Event Handling
$app->withOutEvent(function ($server, $client) { $server->reply($client, ['error' => 'Unknown event']); });
π Error Handling
Global Exception Handlers
use Oktaax\Core\Application; $app->listen(3000, '127.0.0.1', function ($url, Application $coreApp) { $coreApp->catch(\Oktaax\Exception\ValidationException::class, function ($e) { response()->status(422)->json(['errors' => $e->getErrors()]); }); $coreApp->catch(\Oktaax\Exception\HttpException::class, function ($e) { response()->status($e->getStatusCode())->json(['error' => $e->getMessage()]); }); $coreApp->catch(Exception::class, function ($e) { Console::error($e->getMessage()); response()->status(500)->json(['error' => 'Internal server error']); }); });
Custom Error Responses
$app->get('/test-error', function ($req, $res) { throw new \Oktaax\Exception\HttpException('Custom error message', 400); });
Async Error Handling
$app->get('/async-error', async(function ($req, $res) { try { $result = await(mayFailAsync()); $res->json(['result' => $result]); } catch (Exception $e) { $res->status(500)->json(['error' => $e->getMessage()]); } }));
π File Uploads
Basic File Upload
$app->post('/upload', function ($req, $res) { $files = $req->files(); if (empty($files)) { return $res->status(400)->json(['error' => 'No files uploaded']); } $uploaded = []; foreach ($files as $file) { $filename = uniqid() . '_' . $file['name']; $destination = "/uploads/$filename"; if (move_uploaded_file($file['tmp_name'], $destination)) { $uploaded[] = $filename; } } $res->json(['uploaded' => $uploaded]); });
Chunked Uploads
$app->post('/upload-chunk', function ($req, $res) { $chunk = $req->file('chunk'); $chunkNumber = $req->input('chunkNumber'); $totalChunks = $req->input('totalChunks'); $filename = $req->input('filename'); $tempDir = '/tmp/uploads/'; $chunkFile = $tempDir . $filename . '.part' . $chunkNumber; if (!is_dir($tempDir)) { mkdir($tempDir, 0755, true); } if (move_uploaded_file($chunk['tmp_name'], $chunkFile)) { // Check if all chunks are uploaded $allChunks = true; for ($i = 0; $i < $totalChunks; $i++) { if (!file_exists($tempDir . $filename . '.part' . $i)) { $allChunks = false; break; } } if ($allChunks) { // Combine chunks $finalFile = '/uploads/' . $filename; $handle = fopen($finalFile, 'wb'); for ($i = 0; $i < $totalChunks; $i++) { $chunkFile = $tempDir . $filename . '.part' . $i; fwrite($handle, file_get_contents($chunkFile)); unlink($chunkFile); } fclose($handle); $res->json(['status' => 'complete', 'file' => $filename]); } else { $res->json(['status' => 'chunk_received', 'chunk' => $chunkNumber]); } } else { $res->status(500)->json(['error' => 'Failed to save chunk']); } });
Async File Processing
$app->post('/upload-async', async(function ($req, $res) { $file = $req->file('file'); if (!$file) { return $res->status(400)->json(['error' => 'No file uploaded']); } $filename = uniqid() . '_' . $file['name']; $destination = "/uploads/$filename"; // Process file asynchronously (e.g., resize image, scan for viruses) $processed = await(processFileAsync($file['tmp_name'], $destination)); $res->json(['uploaded' => $filename, 'processed' => $processed]); }));
π Benchmarks
Compare Oktaax performance with other frameworks:
cd benchmark
chmod +x run.sh
./run.sh
The benchmark includes:
- Oktaax server
- Express.js server
- Results comparison
π Examples
The examples/ directory contains runnable examples demonstrating various features:
http-basic.php- Basic HTTP serverwebsocket.php- WebSocket servermiddleware.php- Global middlewarepromise.php- Promise usageasync-response.php- Async responsesroute-group.php- Route groupingsend-response.php- Response methodsconsole.php- Logginginvoker.php- Dependency injectionlog.php- Finally hookserror-handling.php- Exception handlingvalidation.php- Request validationfile-upload.php- File uploadsjwt-auth.php- JWT authenticationstreaming.php- Streaming responsescustom-middleware.php- Class-based middlewareconfiguration.php- Server configurationasync-database.php- Async database operationsasync-file-operations.php- Async file I/O
Run any example:
php examples/http-basic.php
π€ Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
π License
This project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ using Swoole for high-performance PHP applications.