{"id":36976154,"url":"https://github.com/foxdie/plankton","last_synced_at":"2026-01-13T22:09:04.326Z","repository":{"id":62506810,"uuid":"95522566","full_name":"foxdie/plankton","owner":"foxdie","description":"PHP REST API microframework","archived":false,"fork":false,"pushed_at":"2020-05-27T08:37:54.000Z","size":141,"stargazers_count":11,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-10T04:19:28.864Z","etag":null,"topics":["api","client","middleware","php","rest","restfull","server"],"latest_commit_sha":null,"homepage":"","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/foxdie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-27T05:55:19.000Z","updated_at":"2024-06-13T01:07:56.000Z","dependencies_parsed_at":"2022-11-02T10:01:52.573Z","dependency_job_id":null,"html_url":"https://github.com/foxdie/plankton","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/foxdie/plankton","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxdie%2Fplankton","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxdie%2Fplankton/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxdie%2Fplankton/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxdie%2Fplankton/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foxdie","download_url":"https://codeload.github.com/foxdie/plankton/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxdie%2Fplankton/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28402192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["api","client","middleware","php","rest","restfull","server"],"created_at":"2026-01-13T22:09:03.638Z","updated_at":"2026-01-13T22:09:04.318Z","avatar_url":"https://github.com/foxdie.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Plankton: a RESTful API microframework\n\n## Requirements\n\n - PHP \u003e= 7.2\n - PHP cURL extension\n - Apache HTTP Server \u003e= 2.4\n - Apache mod_rewrite enabled\n\n## Installation\n\ncomposer require foxdie/rest\n\n## Table of content\n- [Client](#client)\n  * [Creating a client](#creating-a-client)\n  * [GET example](#get-example)\n    + [using callback](#using-callback)\n    + [using magic](#using-magic)\n  * [POST example](#post-example)\n    + [using callback](#using-callback-1)\n    + [using magic](#using-magic-1)\n  * [PUT, PATCH and DELETE examples](#put--patch-and-delete-examples)\n  * [Content types](#content-types)\n  * [Custom request example](#custom-request-example)\n  * [Magic calls](#magic-calls)\n    + [Spinal case](#spinal-case)\n    + [Examples](#examples)\n  * [Authentication strategy](#authentication-strategy)\n    + [anonymous auth](#anonymous-auth)\n    + [basic auth](#basic-auth)\n    + [client credentials](#client-credentials)\n- [Server](#server)\n  * [Creating a server](#creating-a-server)\n  * [Handling requests](#handling-requests)\n    + [Using a config file](#using-a-config-file)\n      - [Example of config file](#example-of-config-file)\n      - [Configure the server](#configure-the-server)\n    + [Using annotations](#using-annotations)\n      - [@Route annotation](#-route-annotation)\n      - [@Method annotation](#-method-annotation)\n      - [@Exception annotation](#-exception-annotation)\n  * [Registering controllers](#registering-controllers)\n  * [Creating middlewares](#creating-middlewares)\n  * [Registering the middlewares](#registering-the-middlewares)\n- [OAuth2](#oauth2)\n  * [Client Credentials Grant](#client-credentials-grant)\n    + [Client](#client-1)\n    + [Server](#server-1)\n      - [Creating your own Access Token Provider](#creating-your-own-access-token-provider)\n- [Logging](#logging)\n  * [Client side](#client-side)\n    + [Simple logger](#simple-logger)\n    + [XML logger](#xml-logger)\n    + [Custom logger](#custom-logger)\n  * [Server side](#server-side)\n\n## Client\n\n### Creating a client\n```php\nuse Plankton\\Client\\Client;\n\t\n$client = new Client(API_ENDPOINT);\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/simple-client.php\n\n### GET example\n```php\n$response = $client-\u003eget(\"/users\");\n```\n\n#### using callback\n```php\n$client-\u003eget(\"/users\", function(Response $response){\n\techo $response;\n});\n```\n#### using magic\n```php\n$response = $client-\u003egetUsers();\n```\n### POST example\n```php\n$response = $client-\u003epost(\"/users\", [\"email\" =\u003e \"foo@bar.com\"]);\n```\n#### using callback\n```php\n$client-\u003epost(\"/users\", [\"email\" =\u003e \"foo@bar.com\"], function(Response $response){\n\techo $response-\u003egetLocation();\n});\n```\n#### using magic\n```php\n$response = $client-\u003epostUsers([\"email\" =\u003e \"foo@bar.com\"]);\n```\n### PUT, PATCH and DELETE examples\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/simple-client.php\n\n### Magic calls\n\n#### Spinal case\nIf you want to use magic calls, your routes must use the spinal case\nExample:\n\n\t$client-\u003egetUserAccounts()\nwill match the following route:\n\n\tGET /user-accounts\ncamel case and snake case are not supported\n\n#### Examples\n| call | route |\n| --- | --- |\n| $client-\u003egetUsers(); | GET /users |\n| $client-\u003egroups(1)-\u003egetUsers(); | GET /groups/1/users |\n| $client-\u003egroups(1)-\u003egetUsers(2); | GET /groups/1/users/2 |\n| $client-\u003epostUsers([]); | POST /users |\n| $client-\u003egroups(1)-\u003epostUsers([]); | POST /groups/1/users |\n| $client-\u003edeleteUsers(1); | DELETE /users/1 |\n| $client-\u003eusers(1)-\u003edelete(); | DELETE /users/1 |\n| $client-\u003egroups(1)-\u003edeleteUsers(2); | DELETE /groups/1/users/2 |\n| $client-\u003egroups(1)-\u003eusers(2)-\u003edelete(); | DELETE /groups/1/users/2 |\n| $client-\u003egroups(1)-\u003eusers()-\u003edelete(2); | DELETE /groups/1/users/2 |\n\n### Content types\nWhen you are using magic calls (e.g. `$client-\u003epostUsers([]);`) or one of the methods `Client::post()`, `Client::put()`, `Client::patch()`, a `Content-Type` header is automatically added to the request. The `Content-Type` is automatically guessed according to the data you send to the server :\n\n| data type | Content-Type |\n| --- | --- |\n| array | application/x-www-form-urlencoded |\n| object | application/json |\n| valid json string | application/json |\n| valid xml string | application/xml |\n| string | text/plain |\n\n\n\nHowever, you still can set the `Content-Type` manually with a [customized request](#custom-request-example)\n\n### Custom request example\n```php\nuse Plankton\\Client\\Client;\nuse Plankton\\Request;\n\n$request = new Request(API_ENDPOINT . \"/users\");\n$request\n    -\u003esetMethod(Request::METHOD_POST)\n    -\u003esetParameter(\"foo\", \"bar\")\n    -\u003esetHeader(\"User-Agent\", \"Mozilla/5.0\")\n    -\u003esetContentType(Request::CONTENT_TYPE_JSON)\n    -\u003esetData([\"email\" =\u003e \"foo@bar.com\"]);\n\n$client = new Client(API_ENDPOINT);\n$client-\u003esend($request, function(Response $response){\n    // ...\n});\n```\n\n#### Automatic data conversion\n\nFor readability reasons, you can use `arrays` or `objects` with the `Request::setData()` method, regardless of the content-type you use. The data will be automatically converted according to the rules below :\n\n| Content-Type | Data type | Conversion |\n| --- | --- | --- |\n| Request::CONTENT_TYPE_JSON | array | json string |\n| Request::CONTENT_TYPE_JSON | object | json string |\n| other | array | URL-encoded query string |\n| other | object | URL-encoded query string |\n\n\n### Authentication strategy\t\n\n#### anonymous auth\n```php\n$client = new Client(API_ENDPOINT);\n```\n#### basic auth\n```php\nuse Plankton\\Client\\Strategy\\BasicAuthentication;\n\n$client = new Client(API_ENDPOINT, new BasicAuthentication(USER, PASSWORD));\n```\n#### client credentials\n```php\nuse Plankton\\Client\\Strategy\\ClientCredentialsAuthentication;\n\n$client = new Client(API_ENDPOINT, new ClientCredentialsAuthentication(\n\tCLIENT_ID, \n\tCLIENT_SECRET,\n\tAUTHENTICATION_URL\n));\n```\nThe authorize and access/refresh token requests will be performed automatically.\nThe 3rd parameter is optionnal, the default value is \"/token\"\n\n## Server\n\n### Creating a server\n```php\nuse Plankton\\Server\\Server;\n\n$server = new Server();\n$server-\u003erun();\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/simple-server.php\n\n### Creating controllers\nYou must create at least one controller which extends the abstract class Plankton\\Server\\Controller\n```php\t\nuse Plankton\\Server\\Controller;\n\nclass APIController extends Controller{\n\tpublic function getUsers(int $id, Request $request): Response{\n\t}\n\t\n\tpublic function postUsers(Request $request): Response{\n\t}\n}\n```\nYour controller will contain one public method for each action of your API.\n\nYou can create routes in 2 different ways:\n- using a config file\n- using annotations\n\n#### Using a config file\nThis will automatically disable the annotation parser. The routes are described in a YAML file\n\n##### Example of config file\n```yml\nroutes:\n    get-user:\n        path: /users/{id}\n        method: GET\n        controller: Test\\Controller\\APIController::getUser\n    create-user:\n        path: /users\n        method: POST\n        controller: Test\\Controller\\APIController::createUser\n```\t        \nFull example here: https://github.com/foxdie/plankton/blob/master/Test/config/server.yml\n\n##### Configuring the server\n```php\nuse Plankton\\Server\\{Server, Config};\n\n$server = new Server(new Config(CONFIG_PATH));\n```\nFull example here: https://github.com/foxdie/plankton/blob/master/Test/public/config-server.php    \n   \n#### Using annotations\n```php\nuse Plankton\\Server\\Controller;\n\nclass APIController extends Controller{\n\t/**\n\t * @Route(/users/{id})\n\t * @Method(GET)\n\t */\n\tpublic function getUser(int $id, Request $request): Response{\n\t}\n\t\n\t/**\n\t * @Route(/users)\n\t * @Method(POST)\n\t */\n\tpublic function createUser(Request $request): Response{\n\t}\n}\n```\nThe routes will be created automatically according to the annotations @Route and @Method.\n\nFull example here : https://github.com/foxdie/rest/blob/master/Test/Controller/APIController.php\n\n##### @Route annotation\n- accepts regular expresssions\n- accepts placeholders: they will be passed as argument in the same order as they appear\n- the spinal case is strongly recommended\n\nYou can add a route prefix to your controller:\n```php\t\n/**\n * @Route(/users)\n */\nclass APIController extends Controller{\n\t/**\n\t * @Route(/{id})\n\t * @Method(GET)\n\t */\n\tpublic function getUser(int $id, Request $request): Response{\n\t}\n\t\n\t/**\n\t * @Route(/)\n\t * @Method(POST)\n\t */\n\tpublic function createUser(Request $request): Response{\n\t}\n}\n```\n##### @Method annotation\nPossible values are:\n- GET\n- POST\n- PUT\n- PATCH\n- DELETE\n\n##### @Exception annotation\n```php\nclass APIController extends Controller{\n\t/**\n\t * This will catch any \\CustomNameSpace\\CustomException\n\t * @Exception(CustomNameSpace\\CustomException)\n\t */\n\tpublic function catchCustomException(Exception $e, Request $request): Response{\n\t}\n\t\n\t/**\n\t * This will catch all other exceptions\n\t * @Exception(*)\n\t */\n\tpublic function catchException(Exception $e, Request $request): Response{\n\t}\n}\n```\n\n### Registering controllers\n```php\nuse Plankton\\Server\\Server;\n\n$server = new Server();\n$server\n\t-\u003eregisterController(new APIController());\n\t-\u003eregisterController(...);\n\t-\u003erun();\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/simple-server.php\n\n### Creating middlewares\n(this is optionnal)\nYou must implement the Plankton\\Server\\Middleware interface.\nThe middlewares can handle both incoming requests and outgoing responses.\n```php\nuse Plankton\\Server\\{Request, Response};\nuse Plankton\\Server\\{Middleware, RequestDispatcher};\n\nclass BasicAuthenticationMiddleware implements Middleware{\n\tpublic function process(Request $request, RequestDispatcher $dispatcher): Response{\n\t\t// ...\n\t\treturn $dispatcher-\u003eprocess($request);\n\t}\n}\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/Middleware/BasicAuthenticationMiddleware.php\n\n### Registering the middlewares\n```php\nuse Plankton\\Server\\Server;\n\n$server = new Server();\n$server\n\t-\u003eaddMiddleware(new BasicAuthenticationMiddleware())\n\t-\u003eaddMiddleware(...)\n\t-\u003eregisterController(new APIController())\n\t-\u003erun();\n```\n## OAuth2\n\n### Client Credentials Grant\n\n#### Client\n```php\nuse Plankton\\Client\\Client;\nuse Plankton\\Client\\Strategy\\ClientCredentialsAuthentication;\nuse Plankton\\Response;\n\n$client = new Client(API_ENDPOINT, new ClientCredentialsAuthentication(\n\tCLIENT_ID, \n\tCLIENT_SECRET,\n\tAUTHENTICATION_URL\n));\n```\nFull example here: \t\nhttps://github.com/foxdie/rest/blob/master/Test/public/oauth2-client.php\n\n#### Server\n```php\nuse Plankton\\Server\\Server;\nuse OAuth2\\Middleware\\ClientCredentialsMiddleware;\nuse OAuth2\\Provider\\MemoryProvider;\nuse Test\\Controller\\APIController;\n\n// Access Token provider\n$provider = new MemoryProvider();\n$provider-\u003eaddClient(CLIENT_ID, CLIENT_SECRET);\n\n$server = new Server();\n$server\n\t-\u003eaddMiddleware(new ClientCredentialsMiddleware($provider))\n\t-\u003eregisterController(new APIController())\n\t-\u003erun();\n```\nFull example here:\nhttps://github.com/foxdie/rest/blob/master/Test/public/oauth2-server.php\n\n##### Creating your own Access Token Provider\nAll you have to do is to implement the AccessTokenProvider interface:\n```php\nuse Plankton\\OAuth2\\Provider\\AccessTokenProvider;\nuse Plankton\\OAuth2\\Token\\{AccessToken, BearerToken};\n\nclass PDOProvider implements AccessTokenProvider{\n\t/**\n\t * return a new/issued Access Token if you find a client matching the authentication parameters (id + secret)\n\t */\n\tpublic function getAccessToken(string $client_id, string $client_secret): ?AccessToken{\n\t}\n\n\t/**\n\t * return a new Access Token if the Refresh Token is valid\n\t */\n\tpublic function refreshToken(string $refreshToken): ?AccessToken{\n\t}\n\n\t/**\n\t * authorize or not the given Access Token\n\t */\n\tpublic function isValidAccessToken(string $token): bool{\n\t}\n}\n```\n\n## Logging\n\n### Client side\n\n#### Simple logger\n```php\nuse Plankton\\Logging\\SimpleLogger;\n\n$client-\u003esetLogger(new SimpleLogger());\n\n// ... do some requests\n\nforeach ($client-\u003egetLogger()-\u003egetLogs() as $request) {\n\t$response = $client-\u003egetLogger()-\u003egetLogs()[$request];\n}\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/simple-client.php\n\n#### XML logger\n```php\nuse Plankton\\Logging\\XMLLogger;\n\n$client-\u003esetLogger(new XMLLogger());\n\n// ... do some requests\n\nheader(\"Content-type: text/xml\");\necho $client-\u003egetLogger()-\u003egetLogs()-\u003easXML();\n```\nFull example here: https://github.com/foxdie/rest/blob/master/Test/public/oauth2-client.php\n\n#### Custom logger\nYou have to implement the Plankton\\Request\\Logger interface:\n```php\nuse Plankton\\{Request,Response};\nuse Plankton\\Request\\Logger\n\nclass CustomLogger implements Logger{\n\tpublic function log(Request $request, Response $response = NULL): void{\n\t}\n}\n```\n\n### Server side\nYou can easily log requests and responses by [adding a middleware](#creating-middlewares):\n```php\nuse Plankton\\{Request,Response};\nuse Plankton\\Server\\{Middleware, RequestDispatcher};\n\nclass LogMiddleware implements Middleware{\n\tpublic function process(Request $request, RequestDispatcher $dispatcher): Response{\t\t\n\t\t$response = $dispatcher-\u003eprocess($request);\n\t\t\n\t\t// log $request and $response here\n\t\t\n\t\treturn $response\n\t}\n}\n```\n\nand then register the middleware(#registering-the-middlewares)\n```php\nuse Plankton\\Server\\Server;\nuse Test\\Controller\\APIController;\nuse Test\\Middleware\\LogMiddleware;\n\n$server = new Server();\n$server\n\t-\u003eaddMiddleware(new LogMiddleware())\n\t-\u003eregisterController(new APIController())\n\t-\u003erun();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxdie%2Fplankton","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoxdie%2Fplankton","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxdie%2Fplankton/lists"}