luracast / restler
Restler is a simple and effective multi-format Web API Server framework written in PHP. Just deal with your business logic in php, Restler will take care of the REST!
Installs: 520 214
Dependents: 8
Suggesters: 0
Security: 1
Stars: 1 366
Watchers: 82
Forks: 312
Open Issues: 33
pkg:composer/luracast/restler
Requires
- php: ^8
- ext-json: *
- guzzlehttp/guzzle: ^7.5
- hansott/psr7-cookies: ^3.0
- logicalsteps/async: ^2.0
- psr/container: ^2.0
- ralouphie/getallheaders: ^3
- ringcentral/psr7: ^1.3
Requires (Dev)
- ext-pdo: *
- ext-posix: *
- avadim/fast-excel-writer: ^4
- behat/behat: ^3.13
- bref/bref: ^2
- bshaffer/oauth2-server-php: ^1
- firebase/php-jwt: ^6
- gabordemooij/redbean: ^5
- illuminate/view: ^8 || ^9 || ^10
- mustache/mustache: ^2
- psy/psysh: @stable
- react/event-loop: ^1.5
- react/http: ^1.10
- rector/rector: ^0.18
- rize/uri-template: ^0.3
- swoole/ide-helper: ^5
- symplify/easy-coding-standard: ^12
- twig/twig: ^3
- webonyx/graphql-php: ^15
- workerman/workerman: ^4
Suggests
- ext-openswoole: Alternative to ext-swoole for high-performance async server support (install either ext-swoole OR ext-openswoole, not both)
- ext-pdo_sqlite: For OAuth2 server examples and session storage
- ext-swoole: For high-performance async server support (install either ext-swoole OR ext-openswoole, not both)
- dev-master
- v6.x-dev
- 6.0.0
- v5.x-dev
- 5.0.13
- 5.0.12
- 5.0.11
- 5.0.10
- 5.0.9
- 5.0.8
- 5.0.7
- 5.0.6
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- v4.x-dev
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- v3.x-dev
- 3.1.1
- 3.1.0
- 3.0.0
- 3.0.0-RC6
- 3.0.0-RC5
- 3.0.0-RC4
- dev-features/proxy-api
- dev-features/swagger2.0
- dev-features/explorer
- dev-features/swagger1.2
- dev-features/AOP
This package is auto-updated.
Last update: 2025-12-01 14:16:28 UTC
README
Write your API logic in PHP. Get JSON, XML, CSV, Excel, GraphQL, OpenAPI docs, and more—automatically.
Production-ready since 2010. Battle-tested. Now rebuilt for modern PHP 8+ with async superpowers.
class Products { function get(int $id): array { return Database::findProduct($id); } } $api = new Restler(); $api->addAPIClass(Products::class); $api->handle();
That's it. You just created a REST API that:
- ✅ Automatically validates
$idas an integer - ✅ Returns JSON by default (
.xml,.csv,.yamlalso work) - ✅ Handles routing (
GET /products/123) - ✅ Generates OpenAPI/Swagger docs
- ✅ Provides proper HTTP status codes
- ✅ Supports content negotiation
No controllers. No routing files. No configuration. Just PHP.
Why Restler in 2025?
🎯 Zero Boilerplate
While other frameworks make you write controllers, routes, DTOs, validation rules, and transformers—Restler uses PHP reflection to do it all automatically. Write business logic, not plumbing.
// Laravel/Symfony: 50+ lines of controllers, routes, requests, resources // Restler: 5 lines class Users { function create(string $email, string $name): User { return User::create(compact('email', 'name')); } }
⚡ Async Performance
Run on Swoole/OpenSwoole for 10-20x throughput vs traditional PHP-FPM. Or use ReactPHP for true async I/O. Deploy to AWS Lambda for serverless scale.
# Traditional PHP-FPM: ~1,000 req/sec # Swoole/OpenSwoole: ~15,000 req/sec (same code!) composer swoole-server
🌐 Multi-Format by Default
Your API automatically speaks JSON, XML, YAML, CSV, Excel, and HTML. Perfect for:
- Mobile apps → JSON
- Legacy systems → XML
- Data exports → CSV/Excel
- Admin panels → HTML with Blade/Twig
curl api.example.com/products/123 # JSON curl api.example.com/products/123.xml # XML curl api.example.com/products.csv # CSV export curl api.example.com/products.xlsx # Excel download
🚀 Modern PHP 8+
Built for PHP 8 with attributes, union types, named arguments, and strict typing. PSR-7 and PSR-11 compliant.
use Luracast\Restler\Attribute\{Get, Post}; class Orders { #[Get('orders/{id}')] function getOrder(int $id): Order|null { return Order::find($id); } #[Post('orders')] function createOrder(string $product, int $quantity = 1): Order { return Order::create(compact('product', 'quantity')); } }
📚 Auto-Generated Docs
OpenAPI 3.0 (Swagger) docs generated from your PHPDoc comments. Interactive API explorer included.
class Products { /** * Get product details * * @param int $id Product ID * @return Product product information * @throws 404 Product not found */ function get(int $id): Product { return Product::findOrFail($id); } } // Visit /explorer for interactive Swagger UI
Real-World Use Cases
🏢 Internal APIs & Microservices
Perfect for building internal APIs that need to integrate with various systems. Multi-format support means you can serve JSON to your React app and XML to that ancient CRM system—from the same endpoint.
📱 Mobile Backend
Low latency on Swoole, automatic validation, built-in rate limiting, and OAuth2 support. Everything you need for a production mobile backend.
📊 Data Export APIs
Built-in CSV and Excel streaming support. Export millions of rows without running out of memory using generators.
function exportUsers(): Generator { foreach (User::cursor() as $user) { yield $user->toArray(); } } // GET /users.csv streams all users as CSV // GET /users.xlsx downloads Excel file
🔗 Legacy System Integration
Need to modernize an old PHP app? Add Restler to get a REST API instantly. Works alongside existing code—no rewrite needed.
Quick Start
Install
composer require luracast/restler:^6.0
Create Your First API (3 files)
1. API Class (api/Hello.php)
<?php class Hello { function sayHello(string $name = 'World'): string { return "Hello, $name!"; } function getTime(): array { return ['time' => date('Y-m-d H:i:s'), 'timezone' => date_default_timezone_get()]; } }
2. Gateway (public/index.php)
<?php require __DIR__ . '/../vendor/autoload.php'; $api = new Luracast\Restler\Restler(); $api->addAPIClass('Hello'); $api->handle();
3. URL Rewriting (.htaccess or nginx.conf)
# Apache RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L]
Test It
# Start server php -S localhost:8080 -t public # Try your API curl http://localhost:8080/hello/sayHello/Restler # Output: "Hello, Restler!" curl http://localhost:8080/hello/getTime # Output: {"time":"2025-01-15 10:30:45","timezone":"UTC"} curl http://localhost:8080/hello/getTime.xml # Output: <?xml version="1.0"?><response><time>2025-01-15 10:30:45</time>...
That's it! You have a working REST API with automatic routing, validation, and multi-format support.
Production Deployment
Traditional (Apache/Nginx + PHP-FPM)
Standard deployment. Works everywhere. ~1,000-2,000 req/sec depending on hardware.
// Enable production mode for route caching $api = new Restler(productionMode: true);
High Performance (Swoole/OpenSwoole)
10-20x faster than PHP-FPM. Persistent connections, coroutines, built-in HTTP server.
# Install extension (choose one) pecl install swoole # Original pecl install openswoole # Fork with same API # Run server php public/index_swoole.php
Benchmarks: 15,000+ req/sec on modest hardware (vs ~1,000 for PHP-FPM)
Async I/O (ReactPHP)
True non-blocking async operations. Perfect for I/O-heavy workloads (database, HTTP calls, etc.).
composer react-server
Serverless (AWS Lambda)
Zero-downtime deploys, automatic scaling, pay-per-request pricing.
# Uses Bref for Laravel-style Lambda deployment
vendor/bin/bref deploy
See public/index_lambda.php for complete example.
Event-Driven (Workerman)
Alternative to Swoole with pure PHP implementation (no extension required).
php public/index_workerman.php start
What's New in v6
🔥 Breaking Changes from v5
- PHP 8.0+ required (was PHP 7.4)
- PSR-7 HTTP messages (modern request/response objects)
- PSR-11 Container (standard dependency injection)
- Stricter typing (full PHP 8 type hints)
✨ New Features
Modern PHP 8 Support
- ✅ Attributes (
#[Get],#[Post], etc.) - ✅ Union types (
string|int,User|null) - ✅ Named arguments
- ✅ Constructor property promotion
- ✅ Match expressions
- ✅ Enums
Async & Performance
- ✅ Swoole/OpenSwoole integration (10-20x faster)
- ✅ ReactPHP async server
- ✅ Workerman event-driven server
- ✅ AWS Lambda serverless support
- ✅ Generator streaming for large datasets (CSV, Excel)
- ✅ Route caching & opcode optimization
Enhanced Security
- ✅ Secure session serialization (JSON, not
unserialize()) - ✅ JSONP callback validation (XSS prevention)
- ✅ Template injection protection
- ✅ Configurable CORS with proper defaults
- ✅ Built-in rate limiting
- ✅ OAuth 2.0 server support
Developer Experience
- ✅ GraphQL support
- ✅ Excel export (XLSX streaming)
- ✅ OpenAPI 3.0 spec generation
- ✅ Interactive API Explorer (Swagger UI)
- ✅ Better error messages
- ✅ Blade, Twig, Mustache template engines
- ✅ Modern DI container with auto-wiring
Multi-Format Support
All formats work automatically—just add file extension to URL:
- JSON (default)
- XML
- YAML
- CSV (with streaming)
- Excel (XLSX)
- HTML (with templates)
- AMF (Flash/Flex)
- Plist (iOS/macOS)
Advanced Examples
API Versioning
// v1/Users.php namespace v1; class Users { function get(int $id): array { return ['id' => $id, 'name' => 'John']; } } // v2/Users.php namespace v2; class Users { function get(int $id): User { return User::with('profile')->find($id); } } // index.php $api->addAPIClass('v1\\Users', 'v1/users'); $api->addAPIClass('v2\\Users', 'v2/users');
Usage:
curl api.example.com/v1/users/123 # Old format curl api.example.com/v2/users/123 # New format with profile
Authentication & Rate Limiting
use Luracast\Restler\Contracts\AuthenticationInterface; class ApiKeyAuth implements AuthenticationInterface { public function __isAuthenticated(): bool { $key = $_SERVER['HTTP_X_API_KEY'] ?? null; return $key && ApiKey::validate($key); } } $api = new Restler(); $api->addAuthenticationClass(ApiKeyAuth::class); $api->addFilterClass(RateLimit::class); // Built-in rate limiting $api->addAPIClass(ProtectedAPI::class); $api->handle();
Database Integration
class Products { /** * List all products with pagination * * @param int $page Page number (default: 1) * @param int $limit Items per page (default: 20) */ function index(int $page = 1, int $limit = 20): array { return Product::paginate($limit, page: $page)->toArray(); } /** * Create new product * * @param string $name Product name * @param float $price Product price * @param string $category Category name */ function post(string $name, float $price, string $category): Product { return Product::create(compact('name', 'price', 'category')); } /** * Update product * * @param int $id Product ID {@from path} */ function put(int $id, string $name = null, float $price = null): Product { $product = Product::findOrFail($id); if ($name) $product->name = $name; if ($price) $product->price = $price; $product->save(); return $product; } /** * Delete product * * @param int $id Product ID {@from path} */ function delete(int $id): array { Product::findOrFail($id)->delete(); return ['message' => 'Product deleted']; } }
Automatic REST endpoints:
GET /products→ index()POST /products→ post()GET /products/123→ (auto-routes to index with $id)PUT /products/123→ put()DELETE /products/123→ delete()
File Uploads
class Media { /** * Upload file * * @param array $file Upload file {@from body} {@type file} */ function post(array $file): array { $path = Storage::put('uploads', $file['tmp_name']); return [ 'filename' => $file['name'], 'url' => Storage::url($path), 'size' => $file['size'] ]; } }
Streaming Large Datasets
class Reports { /** * Export all users (memory efficient) */ function exportUsers(): Generator { // Processes millions of rows without memory issues foreach (User::cursor() as $user) { yield [ 'id' => $user->id, 'email' => $user->email, 'created' => $user->created_at ]; } } } // GET /reports/exportUsers.csv → streams CSV // GET /reports/exportUsers.xlsx → streams Excel
Custom Routing
class Products { /** * @url GET products/featured */ function getFeatured(): array { return Product::where('featured', true)->get(); } /** * @url GET products/search/{query} * @param string $query Search term {@from path} */ function search(string $query): array { return Product::where('name', 'LIKE', "%$query%")->get(); } /** * @url POST products/{id}/publish * @param int $id Product ID {@from path} */ function publish(int $id): array { $product = Product::findOrFail($id); $product->published = true; $product->save(); return ['message' => 'Published successfully']; } }
GraphQL Support
use Luracast\Restler\OpenApi3\Explorer; $api = new Restler(); $api->addAPIClass(GraphQLEndpoint::class, 'graphql'); $api->addAPIClass(Explorer::class, 'explorer'); $api->handle(); // POST /graphql // Query and mutation support built-in
CORS Configuration
use Luracast\Restler\Defaults; Defaults::$accessControlAllowOrigin = 'https://app.example.com'; Defaults::$accessControlAllowMethods = 'GET, POST, PUT, DELETE'; Defaults::$accessControlAllowHeaders = 'Content-Type, Authorization'; Defaults::$accessControlMaxAge = 86400; $api = new Restler(); $api->addAPIClass(MyAPI::class); $api->handle();
Interactive API Explorer
Restler includes a built-in Swagger UI explorer:
use Luracast\Restler\OpenApi3\Explorer; $api = new Restler(); $api->addAPIClass(Products::class); $api->addAPIClass(Users::class); $api->addAPIClass(Explorer::class, 'explorer'); // Add explorer $api->handle();
Visit http://localhost:8080/explorer to:
- Browse all endpoints
- See request/response schemas
- Test APIs interactively
- Download OpenAPI spec
The explorer is auto-generated from your PHPDoc comments—no manual work needed.
Testing
Restler includes 18+ working examples and a full Behat test suite.
Run Built-in Tests
# Start server composer serve # Run tests (in another terminal) composer test
Results: 310/310 scenarios passing ✅
Write Your Own Tests
# features/products.feature Feature: Product API Scenario: Get product by ID When I request "GET /products/123" Then the response status code should be 200 And the response is JSON And the response has a "name" property Scenario: Create product When I request "POST /products" with body: """ {"name": "Widget", "price": 19.99} """ Then the response status code should be 201 And the response has a "id" property
Example Projects
Try the included examples:
_001_helloworld- Minimal API_003_multiformat- JSON, XML, YAML, CSV_005_protected_api- Authentication_007_crud- Full CRUD with database_009_rate_limiting- Rate limiting_011_versioning- API versioning_013_html- HTML responses with templates_015_oauth2_server- OAuth 2.0 server_018_graphql- GraphQL integration
composer serve
# Visit http://localhost:8080/examples/
Performance Tips
1. Enable Production Mode
// Caches routes, disables debug mode $api = new Restler(productionMode: true); // Clear route cache after code changes: // rm cache/routes.php
2. Use Swoole/OpenSwoole
10-20x performance improvement over PHP-FPM. Same code, no changes needed.
pecl install swoole php public/index_swoole.php
3. Use Generators for Large Datasets
// Bad: Loads everything into memory function getUsers(): array { return User::all()->toArray(); // 💥 100MB+ } // Good: Streams data function getUsers(): Generator { foreach (User::cursor() as $user) { yield $user->toArray(); // ✅ Constant memory } }
4. Cache Expensive Operations
use Luracast\Restler\Defaults; // Enable route caching Defaults::$cacheDirectory = __DIR__ . '/cache'; // Use your own caching for data function getPopularProducts(): array { return Cache::remember('popular_products', 3600, function() { return Product::orderBy('views', 'desc')->take(10)->get(); }); }
5. Optimize Database Queries
// Use eager loading to avoid N+1 queries function index(): array { return Order::with(['user', 'items.product'])->get(); }
Framework Comparison
| Feature | Restler v6 | Laravel | Symfony | Slim |
|---|---|---|---|---|
| Auto-routing from methods | ✅ | ❌ | ❌ | ❌ |
| Multi-format (JSON/XML/CSV/Excel) | ✅ | Partial | Partial | ❌ |
| Auto OpenAPI docs | ✅ | Via package | Via package | Via package |
| Swoole support | ✅ | Via package | Via package | ❌ |
| Zero config | ✅ | ❌ | ❌ | Partial |
| Lines of code for CRUD API | ~20 | ~100 | ~150 | ~50 |
| Learning curve | Very Low | Medium | High | Low |
| Best for | APIs | Full-stack | Enterprise | Microservices |
Restler Philosophy: Write business logic. Get the REST for free.
Migration from v5
Upgrading from Restler v5? Most code works unchanged. Key changes:
Requirements
- PHP 8.0+ (was 7.4+)
- Add PSR-7 and PSR-11 to composer.json (auto-installed)
Type Hints (Recommended)
// v5 - Still works but add types for better validation public function getUser($id) { return User::find($id); } // v6 - Recommended public function getUser(int $id): ?User { return User::find($id); }
Breaking Changes
- Removed deprecated reflection methods (internal only)
- Session serialization now uses JSON (more secure)
- Some internal class reorganization
Full migration guide: MIGRATION.md
Documentation
- Annotations Reference - All PHPDoc annotations (
@url,@param, etc.) - Parameter Handling - Request parameter mapping
- Request Lifecycle - How Restler processes requests
- Forms - HTML form handling
- Composer Integration - Advanced setup
- Security Guide - Best practices
- Migration Guide - Upgrading from v5
- Changelog - Version history
FAQ
When should I use Restler?
✅ Great for:
- Internal APIs and microservices
- Mobile app backends
- Data export APIs (CSV, Excel)
- Rapid prototyping
- Modernizing legacy PHP apps
- APIs that need multiple formats (JSON + XML + CSV)
- High-performance requirements (with Swoole)
❌ Not ideal for:
- Full-stack web apps with server-side rendering (use Laravel/Symfony)
- GraphQL-first APIs (though GraphQL support is included)
- Non-PHP environments
How is this different from Laravel/Symfony?
Restler is laser-focused on APIs. Laravel and Symfony are full-stack frameworks that can build APIs, but require significantly more boilerplate. Restler uses reflection to eliminate boilerplate entirely.
Example: A CRUD API in Laravel requires routes, controllers, form requests, and resources (~100 lines). In Restler it's ~20 lines.
Is Swoole support production-ready?
Yes! Swoole has been production-ready since 2018. Used by companies like Alibaba, Tencent, and Baidu. OpenSwoole is a fork with the same stability. Both work identically with Restler.
Does it work with [my favorite ORM/database]?
Yes! Restler is database-agnostic. Use Eloquent, Doctrine, RedBeanPHP, PDO, or anything else. Examples included for all major ORMs.
Can I use it with Docker/Kubernetes?
Absolutely. Dockerfile examples included. Works great in containers, especially with Swoole for high performance.
Is it really production-ready?
Yes! Restler has been used in production since 2010. v6 is a complete rewrite for modern PHP 8+ with security improvements and async support. Currently powers APIs handling millions of requests daily.
What's the performance like?
- PHP-FPM: ~1,000-2,000 req/sec (typical)
- Swoole/OpenSwoole: ~15,000-20,000 req/sec (same hardware)
- AWS Lambda: Automatic scaling, cold start ~100ms
Actual numbers depend on your application logic and hardware.
Support & Community
- 📖 Documentation: Full docs
- 🐛 Bug Reports: GitHub Issues
- 🔒 Security: SECURITY.md
- 🌟 Star us on GitHub if you find Restler useful!
Contributing
We welcome contributions! Whether it's:
- 🐛 Bug fixes
- ✨ New features
- 📖 Documentation improvements
- 🧪 Test coverage
- 💡 Ideas and suggestions
See CONTRIBUTING.md for guidelines.
Development Setup
git clone https://github.com/Luracast/Restler.git cd Restler git checkout v6 composer install composer serve # Start dev server # Run tests composer test
License
Restler is open-source software licensed under the MIT License.
Free for commercial and personal use.
Credits
Created and maintained by Luracast
Special thanks to all contributors who have helped make Restler better over the years!
Ready to build better APIs?
composer require luracast/restler:^6.0
Write PHP. Get REST. 🚀