{"id":18550293,"url":"https://github.com/xp-forge/rest-client","last_synced_at":"2025-10-12T06:32:51.950Z","repository":{"id":32930296,"uuid":"146742239","full_name":"xp-forge/rest-client","owner":"xp-forge","description":"REST Client","archived":false,"fork":false,"pushed_at":"2025-01-18T16:42:09.000Z","size":274,"stargazers_count":0,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-12T23:02:12.609Z","etag":null,"topics":["basic-authentication","bearer-authentication","brotli","bzip2","compression","file-upload","gzip","http","json","no-xml","php7","php8","rest","rest-client","xp-framework"],"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/xp-forge.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":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-08-30T11:51:35.000Z","updated_at":"2025-01-18T16:38:00.000Z","dependencies_parsed_at":"2024-02-04T10:22:57.518Z","dependency_job_id":"5a8143a3-90a5-405f-b569-b78e0f56c085","html_url":"https://github.com/xp-forge/rest-client","commit_stats":null,"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Frest-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Frest-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Frest-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Frest-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xp-forge","download_url":"https://codeload.github.com/xp-forge/rest-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246927808,"owners_count":20856193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["basic-authentication","bearer-authentication","brotli","bzip2","compression","file-upload","gzip","http","json","no-xml","php7","php8","rest","rest-client","xp-framework"],"created_at":"2024-11-06T21:04:07.938Z","updated_at":"2025-10-12T06:32:51.945Z","avatar_url":"https://github.com/xp-forge.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Rest Client\n========================================================================\n\n[![Build status on GitHub](https://github.com/xp-forge/rest-client/workflows/Tests/badge.svg)](https://github.com/xp-forge/rest-client/actions)\n[![XP Framework Module](https://raw.githubusercontent.com/xp-framework/web/master/static/xp-framework-badge.png)](https://github.com/xp-framework/core)\n[![BSD Licence](https://raw.githubusercontent.com/xp-framework/web/master/static/licence-bsd.png)](https://github.com/xp-framework/core/blob/master/LICENCE.md)\n[![Requires PHP 7.4+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_4plus.svg)](http://php.net/)\n[![Supports PHP 8.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-8_0plus.svg)](http://php.net/)\n[![Latest Stable Version](https://poser.pugx.org/xp-forge/rest-client/version.svg)](https://packagist.org/packages/xp-forge/rest-client)\n\nREST client\n\nUsage\n-----\n\nThe `Endpoint` class serves as the entry point to this API. Create a new instance of it with the REST service's endpoint URL and then invoke its `resource()` method to work with the resources.\n\n### Creating: post\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://api.example.com/');\n$result= $api-\u003eresource('users')-\u003epost(['name' =\u003e 'Test'], 'application/json');\n\n// Get location from created response, raising an UnexpectedStatus\n// exception for any statuscode outside of the range 200-299.\n$url= $result-\u003elocation();\n\n// Same as above, but handle 201 *AND* 200 status codes - see\n// https://stackoverflow.com/questions/1860645\n$id= $result-\u003ematch([\n  200 =\u003e fn($r) =\u003e $r-\u003evalue()['id'],\n  201 =\u003e fn($r) =\u003e (int)basename($r-\u003elocation())\n]);\n```\n\n### Reading: get / head\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://api.example.com/');\n\n// Test for existance with HEAD, raising UnexpectedStatus exceptions\n// for any status code other than 200 and 404.\n$exists= $api-\u003eresource('users/1549')-\u003ehead()-\u003ematch([\n  200 =\u003e true,\n  404 =\u003e false\n]);\n\n// Return user object, raising an UnexpectedStatus exception for any\n// statuscode outside of the range 200-299.\n$user= $api-\u003eresource('users/self')-\u003eget()-\u003evalue();\n\n// Same as above, but returns NULL for 404s instead of an exception\n$user= $api-\u003eresource('users/{0}', [$id])-\u003eget()-\u003eoptional();\n\n// Pass parameters\n$list= $api-\u003eresource('user')-\u003eget(['page' =\u003e 1, 'per_page' =\u003e 50])-\u003evalue();\n\n// Access pagination via `Link: \u003c...\u003e; rel=\"next\"` header\n$resource= 'groups';\ndo {\n  $result= $this-\u003eendpoint-\u003eresource($resource)-\u003eget(['per_page' =\u003e 200]);\n  foreach ($result-\u003evalue() as $group) {\n    yield $group['id'] =\u003e $group;\n  }\n} while ($resource= $result-\u003elink('next'));\n```\n\n### Updating: put / patch\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://api.example.com/');\n$resource= $api-\u003eresource('users/self')\n  -\u003esending('application/json')\n  -\u003eaccepting('application/json')\n;\n\n// Default content type and accept types set on resource used\n$updated= $resource-\u003eput(['name' =\u003e 'Tested', 'login' =\u003e $mail])-\u003evalue();\n\n// Resources can be reused!\n$updated= $resource-\u003epatch(['name' =\u003e 'Changed'])-\u003evalue();\n```\n\n### Deleting: delete\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://api.example.com/');\n\n// Pass segments, map 204 to true, 404 to null, raise UnexpectedStatus\n// exception otherwise\n$api-\u003eresource('users/{id}', $user)-\u003edelete()-\u003ematch([\n  204 =\u003e true,\n  404 =\u003e null\n]);\n```\n\n### Uploads\n\nMultipart file uploads are initiated by the `upload()` method, may include parameters and can upload from any input stream.\n\n```php\nuse io\\File;\nuse io\\streams\\MemoryInputStream;\nuse webservices\\rest\\Endpoint;\n\n$stream= new MemoryInputStream('Hello');\n$file= new File(...);\n$endpoint= new Endpoint($url);\n\n$result= $endpoint-\u003eresource('files')-\u003eupload()\n  -\u003epass('tc', 'accepted')\n  -\u003etransfer('letter', $stream, 'letter.txt', 'text/plain')\n  -\u003etransfer('cv', $file-\u003ein(), $file-\u003efilename)\n  -\u003efinish()\n  \n;\n```\n\n### Deserialization\n\nAutomatic result deserialization is supported by passing a type to the `value()` method.\n\n```php\nuse com\\example\\api\\types\\User;\n\n$result= $api-\u003eresource('users/{0}', [$id])-\u003eget();\n\n// If a type is passed, the result will be unmarshalled to an object\n$map= $result-\u003evalue();\n$object= $result-\u003evalue(User::class);\n\n// Same for optional, but map and object will be NULL for 404s\n$map= $result-\u003eoptional();\n$object= $result-\u003eoptional(User::class);\n\n// Works with any type from the XP typesystem, e.g. arrays of objects\n$list= $api-\u003eresource('users')-\u003eget()-\u003evalue('org.example.User[]');\n```\n\n### Error handling\n\nOperations on the `Result` class raise `UnexpectedStatus` exceptions. Here's how to access their status and reason:\n\n```php\nuse webservices\\rest\\UnexpectedStatus;\nuse util\\cmd\\Console;\n\n// In unexpected cases\ntry {\n  $user= $api-\u003eresource('users/self')-\u003eget()-\u003evalue();\n} catch (UnexpectedStatus $e) {\n  Console::writeLine('Unexpected ', $e-\u003estatus(), ': ', $e-\u003ereason());\n}\n\n// More graceful handling\n$result= $api-\u003eresource('users/self')-\u003eget();\nif ($error= $result-\u003eerror()) {\n  Console::writeLine('Unexpected ', $result-\u003estatus(), ': ', $error);\n} else {\n  $user= $result-\u003evalue();\n}\n```\n\n### Authentication\n\nBasic authentication is supported by embedding the credentials in the endpoint URL:\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://user:pass@api.example.com/');\n```\n\nBearer tokens can also be embedded in the endpoint URL:\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= new Endpoint('https://token@api.example.com/');\n```\n\nOther header-based authentication values can be passed along as follows:\n\n```php\nuse webservices\\rest\\Endpoint;\n\n$api= (new Endpoint('https://api.example.com/'))-\u003ewith(['X-API-Key' =\u003e $key]);\n```\n\n### Compression\n\nThis library handlees compressed data transparently, sending an *Accept-Encoding* header containing compression algorithms supported in the PHP setup (*based on loaded extensions like e.g. [zlib](https://www.php.net/zlib)*) and using the *Content-Encoding* response header to determine which algorithm to select.\n\n```php\nuse webservices\\rest\\Endpoint;\nuse io\\streams\\Compression;\n\n// Detect supported compression algorithms and set \"Accept-Encoding\" accordingly\n$endpoint= new Endpoint($api);\n\n// Send \"Accept-Encoding: identity\", indicating the server should not compress\n$endpoint= (new Endpoint($api))-\u003ecompressing(Compression::$NONE);\n\n// Send \"Accept-Encoding: gzip, br\"\n$endpoint= (new Endpoint($api))-\u003ecompressing(['gzip', 'br']);\n\n// Do not send an \"Accept-Encoding\" header, i.e. no preference is expressed\n$endpoint= (new Endpoint($api))-\u003ecompressing(null);\n```\n\n### Testability\n\nThis library also includes facilities to ease writing unittests for code making REST API calls. By using the *TestEndpoint* class and supplying it with routes it should respond to, various scenarios can be easily tested without the need for HTTP protocol and I/O overhead.\n\n```php\nuse webservices\\rest\\TestEndpoint;\n\n$endpoint= new TestEndpoint([\n  '/users/6100' =\u003e function($call) {\n    return $call-\u003erespond(200, 'OK', ['Content-Type' =\u003e 'application/json'], '{\n      \"id\": 6100,\n      \"username\": \"binford\"\n    }');\n  },\n  'POST /users' =\u003e function($call) {\n    return $call-\u003erespond(201, 'Created', ['Location' =\u003e '/users/6100']);\n  },\n]);\n\n$response= $endpoint-\u003eresource('/users/me')-\u003eget();\n// Responds with HTTP status 200 and the above JSON payload\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Frest-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxp-forge%2Frest-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Frest-client/lists"}