{"id":14985385,"url":"https://github.com/luracast/restler","last_synced_at":"2026-10-19T13:44:35.305Z","repository":{"id":45243107,"uuid":"1588366","full_name":"Luracast/Restler","owner":"Luracast","description":"Zero-boilerplate REST API framework for PHP 8+. Auto-generates routes, docs, validation.","archived":false,"fork":false,"pushed_at":"2026-08-22T00:40:47.000Z","size":9950,"stargazers_count":1385,"open_issues_count":33,"forks_count":314,"subscribers_count":82,"default_branch":"master","last_synced_at":"2026-10-14T12:34:46.046Z","etag":null,"topics":["async","composer","framework","html-format","json-format","jsonp","openapi","openapi3","php","php-fpm","phpdoc-comments","reactphp","rest-api","restler","swoole"],"latest_commit_sha":null,"homepage":"http://luracast.com/products/restler/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Luracast.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://www.paypal.com/paypalme/luracast"]}},"created_at":"2011-04-08T17:39:58.000Z","updated_at":"2026-09-29T04:54:48.000Z","dependencies_parsed_at":"2026-02-09T21:05:40.364Z","dependency_job_id":null,"html_url":"https://github.com/Luracast/Restler","commit_stats":{"total_commits":1414,"total_committers":45,"mean_commits":31.42222222222222,"dds":"0.10042432814710045","last_synced_commit":"dde49c566e5bb4266634ef2748c79747134d8c4e"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/Luracast/Restler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Luracast%2FRestler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Luracast%2FRestler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Luracast%2FRestler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Luracast%2FRestler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Luracast","download_url":"https://codeload.github.com/Luracast/Restler/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Luracast%2FRestler/sbom","scorecard":{"id":86927,"data":{"date":"2025-08-11","repo":{"name":"github.com/Luracast/Restler","commit":"dde49c566e5bb4266634ef2748c79747134d8c4e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Code-Review","score":0,"reason":"Found 2/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU Lesser General Public License v2.1: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 5 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-15T07:07:21.938Z","repository_id":45243107,"created_at":"2025-08-15T07:07:21.938Z","updated_at":"2025-08-15T07:07:21.938Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":344872684,"owners_count":38357849,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-10-03T21:59:58.778Z","status":"online","status_checked_at":"2026-10-19T02:00:06.414Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","composer","framework","html-format","json-format","jsonp","openapi","openapi3","php","php-fpm","phpdoc-comments","reactphp","rest-api","restler","swoole"],"created_at":"2024-09-24T14:10:51.131Z","updated_at":"2026-10-19T13:44:35.298Z","avatar_url":"https://github.com/Luracast.png","language":"PHP","funding_links":["https://www.paypal.com/paypalme/luracast"],"categories":[],"sub_categories":[],"readme":"# ![Restler](public/examples/resources/restler.svg) Restler v6\n\n[![Latest Stable Version](https://poser.pugx.org/luracast/restler/v/stable.png)](https://packagist.org/packages/luracast/restler)\n[![Total Downloads](https://poser.pugx.org/luracast/restler/downloads.png)](https://packagist.org/packages/luracast/restler)\n[![License](https://poser.pugx.org/luracast/restler/license.png)](https://packagist.org/packages/luracast/restler)\n\n### **Write your API logic in PHP. Get routing, validation, and OpenAPI docs—automatically.**\n\n\u003e **Production-ready since 2010. Battle-tested. Now rebuilt for modern PHP 8+ with async superpowers.**\n\n```php\nuse Luracast\\Restler\\Restler;\nuse Luracast\\Restler\\Routes;\n\nclass Products {\n    function get(int $id): array {\n        return Database::findProduct($id);\n    }\n}\n\nRoutes::mapApiClasses([Products::class]);\n(new Restler)-\u003ehandle();\n```\n\n**That's it.** You just created a REST API that:\n- ✅ Automatically validates `$id` as an integer\n- ✅ Returns JSON by default\n- ✅ Handles routing (`GET /products/123`)\n- ✅ Generates OpenAPI/Swagger docs\n- ✅ Provides proper HTTP status codes\n- ✅ Supports content negotiation (configure other formats as needed)\n\n**No controllers. No routing files. No configuration. Just PHP.**\n\n---\n\n## Why Restler in 2025?\n\n### 🎯 **Zero Boilerplate**\nWhile 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.\n\n```php\n// Laravel/Symfony: 50+ lines of controllers, routes, requests, resources\n// Restler: 5 lines\nclass Users {\n    function create(string $email, string $name): User {\n        return User::create(compact('email', 'name'));\n    }\n}\n```\n\n### ⚡ **Async Performance**\nRun 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.\n\n```bash\n# Traditional PHP-FPM: ~1,000 req/sec\n# Swoole/OpenSwoole: ~15,000 req/sec (same code!)\ncomposer swoole-server\n```\n\n### 🌐 **Multi-Format Output**\nJSON is the default format. Configure additional formats (XML, CSV, Excel, HTML) via `Routes::setOverridingResponseMediaTypes()`. Perfect for:\n- **Mobile apps** → JSON (default)\n- **Legacy systems** → XML\n- **Data exports** → CSV/Excel\n- **Admin panels** → HTML with Blade/Twig\n\n```bash\ncurl api.example.com/products/123       # JSON (default)\ncurl api.example.com/products/123.xml   # XML (if configured)\ncurl api.example.com/products.csv       # CSV (if configured)\ncurl api.example.com/products.xlsx      # Excel (if configured)\n```\n\n### 🚀 **Modern PHP 8+**\nBuilt for PHP 8 with attributes, union types, named arguments, and strict typing. PSR-7 and PSR-11 compliant.\n\n```php\nuse Luracast\\Restler\\Attribute\\{Get, Post};\n\nclass Orders {\n    #[Get('orders/{id}')]\n    function getOrder(int $id): Order|null {\n        return Order::find($id);\n    }\n\n    #[Post('orders')]\n    function createOrder(string $product, int $quantity = 1): Order {\n        return Order::create(compact('product', 'quantity'));\n    }\n}\n```\n\n### 📚 **Auto-Generated Docs**\nOpenAPI 3.0 (Swagger) docs generated from your PHPDoc comments. Interactive API explorer included.\n\n```php\nclass Products {\n    /**\n     * Get product details\n     *\n     * @param int $id Product ID\n     * @return Product product information\n     * @throws 404 Product not found\n     */\n    function get(int $id): Product {\n        return Product::findOrFail($id);\n    }\n}\n// Visit /explorer for interactive Swagger UI\n```\n\n---\n\n## Real-World Use Cases\n\n### 🏢 **Internal APIs \u0026 Microservices**\nPerfect 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.\n\n### 📱 **Mobile Backend**\nLow latency on Swoole, automatic validation, built-in rate limiting, and OAuth2 support. Everything you need for a production mobile backend.\n\n### 📊 **Data Export APIs**\nBuilt-in CSV and Excel streaming support. Export millions of rows without running out of memory using generators.\n\n```php\nfunction exportUsers(): Generator {\n    foreach (User::cursor() as $user) {\n        yield $user-\u003etoArray();\n    }\n}\n// GET /users.csv streams all users as CSV\n// GET /users.xlsx downloads Excel file\n```\n\n### 🔗 **Legacy System Integration**\nNeed to modernize an old PHP app? Add Restler to get a REST API instantly. Works alongside existing code—no rewrite needed.\n\n---\n\n## Quick Start\n\n### Install\n\n```bash\ncomposer require luracast/restler:^6.0\n```\n\n### Create Your First API (3 files)\n\n**1. API Class** (`api/Hello.php`)\n```php\n\u003c?php\nclass Hello {\n    function sayHello(string $name = 'World'): string {\n        return \"Hello, $name!\";\n    }\n\n    function getTime(): array {\n        return ['time' =\u003e date('Y-m-d H:i:s'), 'timezone' =\u003e date_default_timezone_get()];\n    }\n}\n```\n\n**2. Gateway** (`public/index.php`)\n```php\n\u003c?php\nrequire __DIR__ . '/../vendor/autoload.php';\n\nLuracast\\Restler\\Routes::mapApiClasses([Hello::class]);\n(new Luracast\\Restler\\Restler)-\u003ehandle();\n```\n\n**3. URL Rewriting** (`.htaccess` or `nginx.conf`)\n```apache\n# Apache\nRewriteEngine On\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^(.*)$ index.php [QSA,L]\n```\n\n### Test It\n\n```bash\n# Start server\nphp -S localhost:8080 -t public\n\n# Try your API\ncurl http://localhost:8080/hello/sayHello/Restler\n# Output: \"Hello, Restler!\"\n\ncurl http://localhost:8080/hello/getTime\n# Output: {\"time\":\"2025-01-15 10:30:45\",\"timezone\":\"UTC\"}\n\ncurl http://localhost:8080/hello/getTime.xml\n# Output: \u003c?xml version=\"1.0\"?\u003e\u003cresponse\u003e\u003ctime\u003e2025-01-15 10:30:45\u003c/time\u003e...\n```\n\n**That's it!** You have a working REST API with automatic routing, validation, and multi-format support.\n\n---\n\n## Production Deployment\n\n### Traditional (Apache/Nginx + PHP-FPM)\n\nStandard deployment. Works everywhere. ~1,000-2,000 req/sec depending on hardware.\n\n```php\n// Enable production mode for route caching\n$api = new Restler(productionMode: true);\n```\n\n### High Performance (Swoole/OpenSwoole)\n\n**10-20x faster** than PHP-FPM. Persistent connections, coroutines, built-in HTTP server.\n\n```bash\n# Install extension (choose one)\npecl install swoole        # Original\npecl install openswoole    # Fork with same API\n\n# Run server\nphp public/index_swoole.php\n```\n\n**Benchmarks**: 15,000+ req/sec on modest hardware (vs ~1,000 for PHP-FPM)\n\n### Async I/O (ReactPHP)\n\nTrue non-blocking async operations. Perfect for I/O-heavy workloads (database, HTTP calls, etc.).\n\n```bash\ncomposer react-server\n```\n\n### Serverless (AWS Lambda)\n\nZero-downtime deploys, automatic scaling, pay-per-request pricing.\n\n```bash\n# Uses Bref for Laravel-style Lambda deployment\nvendor/bin/bref deploy\n```\n\nSee `public/index_lambda.php` for complete example.\n\n### Event-Driven (Workerman)\n\nAlternative to Swoole with pure PHP implementation (no extension required).\n\n```bash\nphp public/index_workerman.php start\n```\n\n---\n\n## What's New in v6\n\n### 🔥 **Breaking Changes from v5**\n- **PHP 8.0+ required** (was PHP 7.4)\n- **PSR-7 HTTP messages** (modern request/response objects)\n- **PSR-11 Container** (standard dependency injection)\n- **Stricter typing** (full PHP 8 type hints)\n\n[**Migration Guide →**](MIGRATION.md)\n\n### ✨ **New Features**\n\n#### Modern PHP 8 Support\n- ✅ Attributes (`#[Get]`, `#[Post]`, etc.)\n- ✅ Union types (`string|int`, `User|null`)\n- ✅ Named arguments\n- ✅ Constructor property promotion\n- ✅ Match expressions\n- ✅ Enums\n\n#### Async \u0026 Performance\n- ✅ **Swoole/OpenSwoole** integration (10-20x faster)\n- ✅ **ReactPHP** async server\n- ✅ **Workerman** event-driven server\n- ✅ **AWS Lambda** serverless support\n- ✅ **Generator streaming** for large datasets (CSV, Excel)\n- ✅ Route caching \u0026 opcode optimization\n\n#### Enhanced Security\n- ✅ Secure session serialization (JSON, not `unserialize()`)\n- ✅ JSONP callback validation (XSS prevention)\n- ✅ Template injection protection\n- ✅ Configurable CORS with proper defaults\n- ✅ Built-in rate limiting\n- ✅ OAuth 2.0 server support\n\n#### Developer Experience\n- ✅ **GraphQL** support\n- ✅ **Excel export** (XLSX streaming)\n- ✅ **OpenAPI 3.0** spec generation\n- ✅ Interactive **API Explorer** (Swagger UI)\n- ✅ Better error messages\n- ✅ **Blade, Twig, Mustache** template engines\n- ✅ Modern DI container with auto-wiring\n\n#### Multi-Format Support\nAll formats work automatically—just add file extension to URL:\n- JSON (default)\n- XML\n- YAML\n- CSV (with streaming)\n- Excel (XLSX)\n- HTML (with templates)\n- AMF (Flash/Flex)\n- Plist (iOS/macOS)\n\n---\n\n## Advanced Examples\n\n### API Versioning\n\n```php\n// v1/Users.php\nnamespace v1;\nclass Users {\n    function get(int $id): array {\n        return ['id' =\u003e $id, 'name' =\u003e 'John'];\n    }\n}\n\n// v2/Users.php\nnamespace v2;\nclass Users {\n    function get(int $id): User {\n        return User::with('profile')-\u003efind($id);\n    }\n}\n\n// index.php\nuse Luracast\\Restler\\Routes;\n\nRoutes::mapApiClasses([\n    'v1/users' =\u003e 'v1\\\\Users',\n    'v2/users' =\u003e 'v2\\\\Users'\n]);\n```\n\n**Usage:**\n```bash\ncurl api.example.com/v1/users/123  # Old format\ncurl api.example.com/v2/users/123  # New format with profile\n```\n\n### Authentication \u0026 Rate Limiting\n\n```php\nuse Luracast\\Restler\\Contracts\\AuthenticationInterface;\n\nclass ApiKeyAuth implements AuthenticationInterface {\n    public function __isAuthenticated(): bool {\n        $key = $_SERVER['HTTP_X_API_KEY'] ?? null;\n        return $key \u0026\u0026 ApiKey::validate($key);\n    }\n}\n\nuse Luracast\\Restler\\Restler;\nuse Luracast\\Restler\\Routes;\n\nRoutes::addAuthenticator(ApiKeyAuth::class);\nRoutes::setFilters(RateLimit::class);  // Built-in rate limiting\nRoutes::mapApiClasses([ProtectedAPI::class]);\n(new Restler)-\u003ehandle();\n```\n\n### Database Integration\n\n```php\nclass Products {\n    /**\n     * List all products with pagination\n     *\n     * @param int $page Page number (default: 1)\n     * @param int $limit Items per page (default: 20)\n     */\n    function index(int $page = 1, int $limit = 20): array {\n        return Product::paginate($limit, page: $page)-\u003etoArray();\n    }\n\n    /**\n     * Create new product\n     *\n     * @param string $name Product name\n     * @param float $price Product price\n     * @param string $category Category name\n     */\n    function post(string $name, float $price, string $category): Product {\n        return Product::create(compact('name', 'price', 'category'));\n    }\n\n    /**\n     * Update product\n     *\n     * @param int $id Product ID {@from path}\n     */\n    function put(int $id, string $name = null, float $price = null): Product {\n        $product = Product::findOrFail($id);\n        if ($name) $product-\u003ename = $name;\n        if ($price) $product-\u003eprice = $price;\n        $product-\u003esave();\n        return $product;\n    }\n\n    /**\n     * Delete product\n     *\n     * @param int $id Product ID {@from path}\n     */\n    function delete(int $id): array {\n        Product::findOrFail($id)-\u003edelete();\n        return ['message' =\u003e 'Product deleted'];\n    }\n}\n```\n\n**Automatic REST endpoints:**\n- `GET /products` → index()\n- `POST /products` → post()\n- `GET /products/123` → (auto-routes to index with $id)\n- `PUT /products/123` → put()\n- `DELETE /products/123` → delete()\n\n### File Uploads\n\n```php\nclass Media {\n    /**\n     * Upload file\n     *\n     * @param array $file Upload file {@from body} {@type file}\n     */\n    function post(array $file): array {\n        $path = Storage::put('uploads', $file['tmp_name']);\n        return [\n            'filename' =\u003e $file['name'],\n            'url' =\u003e Storage::url($path),\n            'size' =\u003e $file['size']\n        ];\n    }\n}\n```\n\n### Streaming Large Datasets\n\n```php\nclass Reports {\n    /**\n     * Export all users (memory efficient)\n     */\n    function exportUsers(): Generator {\n        // Processes millions of rows without memory issues\n        foreach (User::cursor() as $user) {\n            yield [\n                'id' =\u003e $user-\u003eid,\n                'email' =\u003e $user-\u003eemail,\n                'created' =\u003e $user-\u003ecreated_at\n            ];\n        }\n    }\n}\n\n// GET /reports/exportUsers.csv → streams CSV\n// GET /reports/exportUsers.xlsx → streams Excel\n```\n\n### Custom Routing\n\n```php\nclass Products {\n    /**\n     * @url GET products/featured\n     */\n    function getFeatured(): array {\n        return Product::where('featured', true)-\u003eget();\n    }\n\n    /**\n     * @url GET products/search/{query}\n     * @param string $query Search term {@from path}\n     */\n    function search(string $query): array {\n        return Product::where('name', 'LIKE', \"%$query%\")-\u003eget();\n    }\n\n    /**\n     * @url POST products/{id}/publish\n     * @param int $id Product ID {@from path}\n     */\n    function publish(int $id): array {\n        $product = Product::findOrFail($id);\n        $product-\u003epublished = true;\n        $product-\u003esave();\n        return ['message' =\u003e 'Published successfully'];\n    }\n}\n```\n\n### GraphQL Support\n\n```php\nuse Luracast\\Restler\\Restler;\nuse Luracast\\Restler\\Routes;\nuse Luracast\\Restler\\OpenApi3\\Explorer;\n\nRoutes::mapApiClasses([\n    'graphql' =\u003e GraphQLEndpoint::class,\n    'explorer' =\u003e Explorer::class\n]);\n(new Restler)-\u003ehandle();\n\n// POST /graphql\n// Query and mutation support built-in\n```\n\n### CORS Configuration\n\n```php\nuse Luracast\\Restler\\Defaults;\n\nDefaults::$accessControlAllowOrigin = 'https://app.example.com';\nDefaults::$accessControlAllowMethods = 'GET, POST, PUT, DELETE';\nDefaults::$accessControlAllowHeaders = 'Content-Type, Authorization';\nDefaults::$accessControlMaxAge = 86400;\n\nuse Luracast\\Restler\\Restler;\nuse Luracast\\Restler\\Routes;\n\nRoutes::mapApiClasses([MyAPI::class]);\n(new Restler)-\u003ehandle();\n```\n\n---\n\n## Interactive API Explorer\n\nRestler includes a built-in Swagger UI explorer:\n\n```php\nuse Luracast\\Restler\\Restler;\nuse Luracast\\Restler\\Routes;\nuse Luracast\\Restler\\OpenApi3\\Explorer;\n\nRoutes::mapApiClasses([\n    Products::class,\n    Users::class,\n    'explorer' =\u003e Explorer::class  // Add explorer\n]);\n(new Restler)-\u003ehandle();\n```\n\nVisit `http://localhost:8080/explorer` to:\n- Browse all endpoints\n- See request/response schemas\n- Test APIs interactively\n- Download OpenAPI spec\n\nThe explorer is auto-generated from your PHPDoc comments—no manual work needed.\n\n---\n\n## Testing\n\nRestler includes **18+ working examples** and a full **Behat** test suite.\n\n### Run Built-in Tests\n\n```bash\n# Start server\ncomposer serve\n\n# Run tests (in another terminal)\ncomposer test\n```\n\n**Results:** 310/310 scenarios passing ✅\n\n### Write Your Own Tests\n\n```gherkin\n# features/products.feature\nFeature: Product API\n\n  Scenario: Get product by ID\n    When I request \"GET /products/123\"\n    Then the response status code should be 200\n    And the response is JSON\n    And the response has a \"name\" property\n\n  Scenario: Create product\n    When I request \"POST /products\" with body:\n      \"\"\"\n      {\"name\": \"Widget\", \"price\": 19.99}\n      \"\"\"\n    Then the response status code should be 201\n    And the response has a \"id\" property\n```\n\n### Example Projects\n\nTry the included examples:\n- `_001_helloworld` - Minimal API\n- `_003_multiformat` - JSON, XML, YAML, CSV\n- `_005_protected_api` - Authentication\n- `_007_crud` - Full CRUD with database\n- `_009_rate_limiting` - Rate limiting\n- `_011_versioning` - API versioning\n- `_013_html` - HTML responses with templates\n- `_015_oauth2_server` - OAuth 2.0 server\n- `_018_graphql` - GraphQL integration\n\n```bash\ncomposer serve\n# Visit http://localhost:8080/examples/\n```\n\n---\n\n## Performance Tips\n\n### 1. Enable Production Mode\n\n```php\n// Caches routes, disables debug mode\n$api = new Restler(productionMode: true);\n\n// Clear route cache after code changes:\n// rm cache/routes.php\n```\n\n### 2. Use Swoole/OpenSwoole\n\n**10-20x performance improvement** over PHP-FPM. Same code, no changes needed.\n\n```bash\npecl install swoole\nphp public/index_swoole.php\n```\n\n### 3. Use Generators for Large Datasets\n\n```php\n// Bad: Loads everything into memory\nfunction getUsers(): array {\n    return User::all()-\u003etoArray();  // 💥 100MB+\n}\n\n// Good: Streams data\nfunction getUsers(): Generator {\n    foreach (User::cursor() as $user) {\n        yield $user-\u003etoArray();  // ✅ Constant memory\n    }\n}\n```\n\n### 4. Cache Expensive Operations\n\n```php\nuse Luracast\\Restler\\Defaults;\n\n// Enable route caching\nDefaults::$cacheDirectory = __DIR__ . '/cache';\n\n// Use your own caching for data\nfunction getPopularProducts(): array {\n    return Cache::remember('popular_products', 3600, function() {\n        return Product::orderBy('views', 'desc')-\u003etake(10)-\u003eget();\n    });\n}\n```\n\n### 5. Optimize Database Queries\n\n```php\n// Use eager loading to avoid N+1 queries\nfunction index(): array {\n    return Order::with(['user', 'items.product'])-\u003eget();\n}\n```\n\n---\n\n## Framework Comparison\n\n| Feature | Restler v6 | Laravel | Symfony | Slim |\n|---------|-----------|---------|---------|------|\n| **Auto-routing from methods** | ✅ | ❌ | ❌ | ❌ |\n| **Multi-format (JSON/XML/CSV/Excel)** | ✅ | Partial | Partial | ❌ |\n| **Auto OpenAPI docs** | ✅ | Via package | Via package | Via package |\n| **Swoole support** | ✅ | Via package | Via package | ❌ |\n| **Zero config** | ✅ | ❌ | ❌ | Partial |\n| **Lines of code for CRUD API** | ~20 | ~100 | ~150 | ~50 |\n| **Learning curve** | Very Low | Medium | High | Low |\n| **Best for** | APIs | Full-stack | Enterprise | Microservices |\n\n**Restler Philosophy:** Write business logic. Get the REST for free.\n\n---\n\n## Migration from v5\n\nUpgrading from Restler v5? Most code works unchanged. Key changes:\n\n### Requirements\n- PHP 8.0+ (was 7.4+)\n- Add PSR-7 and PSR-11 to composer.json (auto-installed)\n\n### Type Hints (Recommended)\n```php\n// v5 - Still works but add types for better validation\npublic function getUser($id) {\n    return User::find($id);\n}\n\n// v6 - Recommended\npublic function getUser(int $id): ?User {\n    return User::find($id);\n}\n```\n\n### Breaking Changes\n- Removed deprecated reflection methods (internal only)\n- Session serialization now uses JSON (more secure)\n- Some internal class reorganization\n\n**Full migration guide:** [MIGRATION.md](MIGRATION.md)\n\n---\n\n## Documentation\n\n- **[Annotations Reference](ANNOTATIONS.md)** - All PHPDoc annotations (`@url`, `@param`, etc.)\n- **[Parameter Handling](PARAM.md)** - Request parameter mapping\n- **[Request Lifecycle](STAGES.md)** - How Restler processes requests\n- **[Forms](FORMS.md)** - HTML form handling\n- **[Composer Integration](COMPOSE.md)** - Advanced setup\n- **[Security Guide](SECURITY.md)** - Best practices\n- **[Migration Guide](MIGRATION.md)** - Upgrading from v5\n- **[Changelog](CHANGELOG.md)** - Version history\n\n---\n\n## FAQ\n\n### When should I use Restler?\n\n✅ **Great for:**\n- Internal APIs and microservices\n- Mobile app backends\n- Data export APIs (CSV, Excel)\n- Rapid prototyping\n- Modernizing legacy PHP apps\n- APIs that need multiple formats (JSON + XML + CSV)\n- High-performance requirements (with Swoole)\n\n❌ **Not ideal for:**\n- Full-stack web apps with server-side rendering (use Laravel/Symfony)\n- GraphQL-first APIs (though GraphQL support is included)\n- Non-PHP environments\n\n### How is this different from Laravel/Symfony?\n\nRestler 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.\n\n**Example:** A CRUD API in Laravel requires routes, controllers, form requests, and resources (~100 lines). In Restler it's ~20 lines.\n\n### Is Swoole support production-ready?\n\nYes! 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.\n\n### Does it work with [my favorite ORM/database]?\n\nYes! Restler is database-agnostic. Use Eloquent, Doctrine, RedBeanPHP, PDO, or anything else. Examples included for all major ORMs.\n\n### Can I use it with Docker/Kubernetes?\n\nAbsolutely. Dockerfile examples included. Works great in containers, especially with Swoole for high performance.\n\n### Is it really production-ready?\n\nYes! 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.\n\n### What's the performance like?\n\n- **PHP-FPM:** ~1,000-2,000 req/sec (typical)\n- **Swoole/OpenSwoole:** ~15,000-20,000 req/sec (same hardware)\n- **AWS Lambda:** Automatic scaling, cold start ~100ms\n\nActual numbers depend on your application logic and hardware.\n\n---\n\n## Support \u0026 Community\n\n- **📖 Documentation:** [Full docs](https://github.com/Luracast/Restler/tree/master)\n- **🐛 Bug Reports:** [GitHub Issues](https://github.com/Luracast/Restler/issues)\n- **🔒 Security:** [SECURITY.md](SECURITY.md)\n- **🌟 Star us on GitHub** if you find Restler useful!\n\n---\n\n## Contributing\n\nWe welcome contributions! Whether it's:\n- 🐛 Bug fixes\n- ✨ New features\n- 📖 Documentation improvements\n- 🧪 Test coverage\n- 💡 Ideas and suggestions\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/Luracast/Restler.git\ncd Restler\ngit checkout v6\ncomposer install\ncomposer serve  # Start dev server\n\n# Run tests\ncomposer test\n```\n\n---\n\n## License\n\nRestler is open-source software licensed under the **[MIT License](LICENSE)**.\n\nFree for commercial and personal use.\n\n---\n\n## Credits\n\n**Created and maintained by [Luracast](https://luracast.com)**\n\nSpecial thanks to all [contributors](https://github.com/Luracast/Restler/graphs/contributors) who have helped make Restler better over the years!\n\n\n\n\u003cdiv align=\"center\"\u003e\n\n### **Ready to build better APIs?**\n\n```bash\ncomposer require luracast/restler:^6.0\n```\n\n**Write PHP. Get REST. 🚀**\n\n[Get Started](#quick-start) • [Examples](#examples) • [Documentation](#documentation) • [Star on GitHub ⭐](https://github.com/Luracast/Restler)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluracast%2Frestler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluracast%2Frestler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluracast%2Frestler/lists"}