{"id":20874985,"url":"https://github.com/servicestack/servicestack-php","last_synced_at":"2026-02-14T20:36:18.054Z","repository":{"id":199427339,"uuid":"702470483","full_name":"ServiceStack/servicestack-php","owner":"ServiceStack","description":"PHP ServiceStack Client","archived":false,"fork":false,"pushed_at":"2024-11-25T05:01:45.000Z","size":538,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-10-20T18:40:11.961Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ServiceStack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-09T11:32:25.000Z","updated_at":"2024-11-25T05:01:30.000Z","dependencies_parsed_at":"2025-07-14T12:37:22.964Z","dependency_job_id":"28f9de63-1761-4cf3-9627-5bb135377a56","html_url":"https://github.com/ServiceStack/servicestack-php","commit_stats":null,"previous_names":["servicestack/servicestack-php"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/ServiceStack/servicestack-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2Fservicestack-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2Fservicestack-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2Fservicestack-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2Fservicestack-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ServiceStack","download_url":"https://codeload.github.com/ServiceStack/servicestack-php/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2Fservicestack-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29455360,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T15:52:44.973Z","status":"ssl_error","status_checked_at":"2026-02-14T15:52:11.208Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":[],"created_at":"2024-11-18T06:40:39.051Z","updated_at":"2026-02-14T20:36:18.017Z","avatar_url":"https://github.com/ServiceStack.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"![ServiceStack and PHP Banner](https://docs.servicestack.net/img/pages/servicestack-reference/php-reference.png)\n\nServiceStack's **Add ServiceStack Reference** feature allows clients to generate Native Types from directly within PhpStorm using [ServiceStack IntelliJ Plugin](https://plugins.jetbrains.com/plugin/7749-servicestack/) - providing a simple way to give clients typed access to your ServiceStack Services.\n\n[![](https://docs.servicestack.net/img/pages/servicestack-reference/python-add-servicestack-reference-youtube-splash.png)](https://youtu.be/WjbhfH45i5k)\n\n\u003e YouTube: [youtu.be/WjbhfH45i5k](https://youtu.be/WjbhfH45i5k)\n\n### First class development experience\n\n[PHP](https://www.php.net) is one of the worlds most popular programming languages thanks to its ease of use,\nplatform independence, large standard library, flexibility and fast development experience which sees it excels as\na popular language for web development and for development of popular CMS products like WordPress, Drupal and Joomla\nthanks to its flexibility, embeddability and ease of customization.\n\nTo maximize the experience for calling ServiceStack APIs within these environments ServiceStack now supports PHP as a\n1st class Add ServiceStack Reference supported language which gives PHP developers an end-to-end typed API for consuming\nServiceStack APIs, complete with IDE integration in [PhpStorm](https://www.jetbrains.com/phpstorm/) as well as\n[built-in support in x dotnet tool](https://docs.servicestack.net/dotnet-tool#addupdate-servicestack-references)\nto generate Typed and annotated PHP DTOs for a remote ServiceStack instance from a single command-line.\n\n### Ideal idiomatic Typed Message-based API\n\nTo maximize the utility of PHP DTOs and enable richer tooling support and greater development experience, PHP DTOs are generated as\nTyped [JsonSerializable](https://www.php.net/manual/en/class.jsonserializable.php) classes with\n[promoted constructors](https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion)\nand annotated with [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) - that's invaluable when scaling\nlarge PHP code-bases and greatly improves discoverability of a remote API. DTOs are also enriched with interface markers\nand Annotations which enables its optimal end-to-end typed API:\n\nThe PHP DTOs and `JsonServiceClient` library follow\n[PHP naming conventions](https://infinum.com/handbook/wordpress/coding-standards/php-coding-standards/naming)\nso they'll naturally fit into existing PHP code bases. Here's a sample of [techstacks.io](https://techstacks.io)\ngenerated PHP DTOs containing string and int Enums, an example AutoQuery and a standard Request \u0026 Response DTO showcasing\nthe rich typing annotations and naming conventions used:\n\n```php\nenum TechnologyTier : string\n{\n    case ProgrammingLanguage = 'ProgrammingLanguage';\n    case Client = 'Client';\n    case Http = 'Http';\n    case Server = 'Server';\n    case Data = 'Data';\n    case SoftwareInfrastructure = 'SoftwareInfrastructure';\n    case OperatingSystem = 'OperatingSystem';\n    case HardwareInfrastructure = 'HardwareInfrastructure';\n    case ThirdPartyServices = 'ThirdPartyServices';\n}\n\nenum Frequency : int\n{\n    case Daily = 1;\n    case Weekly = 7;\n    case Monthly = 30;\n    case Quarterly = 90;\n}\n\n// @Route(\"/technology/search\")\n#[Returns('QueryResponse')]\n/**\n * @template QueryDb of Technology\n * @template QueryDb1 of TechnologyView\n */\nclass FindTechnologies extends QueryDb implements IReturn, IGet, JsonSerializable\n{\n    public function __construct(\n        /** @var array\u003cint\u003e|null */\n        public ?array $ids=null,\n        /** @var string|null */\n        public ?string $name=null,\n        /** @var string|null */\n        public ?string $vendorName=null,\n        /** @var string|null */\n        public ?string $nameContains=null,\n        /** @var string|null */\n        public ?string $vendorNameContains=null,\n        /** @var string|null */\n        public ?string $descriptionContains=null\n    ) {\n    }\n\n    /** @throws Exception */\n    public function fromMap($o): void {\n        parent::fromMap($o);\n        if (isset($o['ids'])) $this-\u003eids = JsonConverters::fromArray('int', $o['ids']);\n        if (isset($o['name'])) $this-\u003ename = $o['name'];\n        if (isset($o['vendorName'])) $this-\u003evendorName = $o['vendorName'];\n        if (isset($o['nameContains'])) $this-\u003enameContains = $o['nameContains'];\n        if (isset($o['vendorNameContains'])) $this-\u003evendorNameContains = $o['vendorNameContains'];\n        if (isset($o['descriptionContains'])) $this-\u003edescriptionContains = $o['descriptionContains'];\n    }\n    \n    /** @throws Exception */\n    public function jsonSerialize(): mixed\n    {\n        $o = parent::jsonSerialize();\n        if (isset($this-\u003eids)) $o['ids'] = JsonConverters::toArray('int', $this-\u003eids);\n        if (isset($this-\u003ename)) $o['name'] = $this-\u003ename;\n        if (isset($this-\u003evendorName)) $o['vendorName'] = $this-\u003evendorName;\n        if (isset($this-\u003enameContains)) $o['nameContains'] = $this-\u003enameContains;\n        if (isset($this-\u003evendorNameContains)) $o['vendorNameContains'] = $this-\u003evendorNameContains;\n        if (isset($this-\u003edescriptionContains)) $o['descriptionContains'] = $this-\u003edescriptionContains;\n        return empty($o) ? new class(){} : $o;\n    }\n    public function getTypeName(): string { return 'FindTechnologies'; }\n    public function getMethod(): string { return 'GET'; }\n    public function createResponse(): mixed { return QueryResponse::create(genericArgs:['TechnologyView']); }\n}\n\n// @Route(\"/orgs/{Id}\", \"DELETE\")\nclass DeleteOrganization implements IReturnVoid, IDelete, JsonSerializable\n{\n    public function __construct(\n        /** @var int */\n        public int $id=0\n    ) {\n    }\n\n    /** @throws Exception */\n    public function fromMap($o): void {\n        if (isset($o['id'])) $this-\u003eid = $o['id'];\n    }\n    \n    /** @throws Exception */\n    public function jsonSerialize(): mixed\n    {\n        $o = [];\n        if (isset($this-\u003eid)) $o['id'] = $this-\u003eid;\n        return empty($o) ? new class(){} : $o;\n    }\n    public function getTypeName(): string { return 'DeleteOrganization'; }\n    public function getMethod(): string { return 'DELETE'; }\n    public function createResponse(): void {}\n}\n```\n\nThe smart PHP `JsonServiceClient` available in the [servicestack/client](https://packagist.org/packages/servicestack/client)\npackagist package enables the same productive, typed API development experience available in our other 1st-class supported\nclient platforms.\n\nUsing promoted constructors enables DTOs to be populated using a single constructor expression utilizing named parameters\nwhich together with the generic `JsonServiceClient` enables end-to-end typed API Requests in a single LOC:\n\n```php\nuse ServiceStack\\JsonServiceClient;\nuse dtos\\Hello;\n\n$client = new JsonServiceClient(\"https://test.servicestack.net\");\n\n/** @var HelloResponse $response */\n$response = client-\u003eget(new Hello(name:\"World\"));\n```\n\n\u003e The `HelloResponse` optional type hint doesn't change runtime behavior but enables static analysis tools and IDEs like PyCharm to provide rich intelli-sense and development time feedback.\n\n## Installation\n\nEnsure you have [PHP](https://www.php.net/manual/en/install.php) and [Composer](https://getcomposer.org/doc/00-intro.md) installed.\n\nThe only requirements for PHP apps to perform typed API Requests are the generated PHP DTOs and the generic `JsonServiceClient`\nwhich can be installed in Composer projects with:\n\n```bash\n$ composer require servicestack/client\n```\n\nOr by adding the package to your `composer.json` then installing the dependencies:\n\n```json\n{\n  \"require\": {\n    \"servicestack/client\": \"^1.0\"\n  }\n}\n```\n\n### PhpStorm ServiceStack Plugin\n\nPHP developers of [PhpStorm](https://www.jetbrains.com/phpstorm/) can get a simplified development experience for consuming\nServiceStack Services by installing the [ServiceStack Plugin](https://plugins.jetbrains.com/plugin/7749-servicestack) from the JetBrains Marketplace:\n\n[![](https://docs.servicestack.net/img/pages/servicestack-reference/pycharm-servicestack-plugin.png)](https://plugins.jetbrains.com/plugin/7749-servicestack)\n\nWhere you'll be able to right-click on a directory and click on **ServiceStack Reference** on the context menu:\n\n![](https://docs.servicestack.net/img/pages/servicestack-reference/pycharm-add-servicestack-reference.png)\n\nTo launch the **Add PHP ServiceStack Reference** dialog where you can enter the remote URL of the ServiceStack endpoint you wish to call to generate the Typed PHP DTOs for all APIs which by default will saved to `dtos.php`:\n\n![](https://docs.servicestack.net/img/pages/servicestack-reference/pycharm-add-servicestack-reference-dialog.png)\n\nThen just import the DTOs and `JsonServiceClient` to be able to consume any of the remote ServiceStack APIs:\n\n```php\n\u003c?php\n\nrequire_once __DIR__ . '/vendor/autoload.php'; // Autoload files using Composer autoload\nrequire_once 'dtos.php';\n\nuse dtos\\FindTechnologies;\nuse ServiceStack\\JsonServiceClient;\n\n$client = JsonServiceClient::create(\"https://techstacks.io\");\n\n$response = $client-\u003esend(new FindTechnologies(\n    ids: [1,2,4,6],\n    vendorName: \"Google\"));\n\nprint_r($response);\n```\n\nIf any of the the remote APIs change their DTOs can be updated by right-clicking on `dtos.php` and clicking **Update ServiceStack Reference**:\n\n![](https://docs.servicestack.net/img/pages/servicestack-reference/pycharm-update-servicestack-reference.png)\n\n### Simple command-line utility for PHP\n\nDevelopers using other PHP IDEs and Text Editors like VS Code can utilize the cross-platform [`x` command line utility](https://docs.servicestack.net/dotnet-tool) for generating PHP DTOs from the command-line.\n\nTo install first install the [latest .NET SDK](https://dotnet.microsoft.com/download) for your OS then install the [`x` dotnet tool](https://docs.servicestack.net/dotnet-tool) with:\n\n```bash\n$ dotnet tool install --global x \n```\n\n### Adding a ServiceStack Reference\n\nTo Add a PHP ServiceStack Reference just call `x php` with the URL of a remote ServiceStack instance:\n\n```bash\n$ x php https://techstacks.io\n```\n\nResult:\n\n    Saved to: dtos.php\n\nCalling `x php` with just a URL will save the DTOs using the Host name, you can override this by specifying a FileName as the 2nd argument:\n\n    $ x php https://techstacks.io Tech\n\nResult:\n\n    Saved to: Tech.dtos.php\n\n### Updating a ServiceStack Reference\n\nTo Update an existing ServiceStack Reference, call `x php` with the Filename:\n\n    $ x php dtos.php\n\nResult:\n\n    Updated: dtos.php\n\nWhich will update the File with the latest PHP Server DTOs from [techstacks.io](https://techstacks.io).\nYou can also customize how DTOs are generated by uncommenting the [PHP DTO Customization Options](https://docs.servicestack.net/#dto-customization-options) and updating them again.\n\n### Updating all PHP DTOs\n\nCalling `x php` without any arguments will update all PHP DTOs in the current directory:\n\n```bash\n$ x php\n```\n\nResult:\n\n    Updated: Tech.dtos.php\n    Updated: dtos.php\n\n### Smart Generic JsonServiceClient\n\nThe generic `JsonServiceClient` is a 1st class client with the same rich feature-set of the smart ServiceClients in other\n[1st class supported languages](https://docs.servicestack.net/add-servicestack-reference#supported-languages) sporting a terse,\ntyped flexible API with support for additional untyped params, custom URLs and HTTP Methods, dynamic response types including\nconsuming API responses in raw text and binary data formats. Clients can be decorated to support generic functionality\nusing instance and static Request, Response and Exception Filters.\n\nIt includes built-in support for a number of [ServiceStack Auth options](https://docs.servicestack.net/authentication-and-authorization)\nincluding [HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication) and stateless Bearer Token Auth Providers like\n[API Key](https://docs.servicestack.net/api-key-authprovider) and [JWT Auth](https://docs.servicestack.net/jwt-authprovider) as well as\n[stateful Sessions](https://docs.servicestack.net/sessions) used by the popular **credentials** Auth Provider and an\n`onAuthenticationRequired` callback for enabling custom authentication methods.\n\nThe built-in auth options include auto-retry support for transparently authenticating and retrying authentication required\nrequests as well as [Refresh Token Cookie](https://docs.servicestack.net/jwt-authprovider#refresh-token-cookies-supported-in-all-service-clients)\nsupport where it will transparently fetch new JWT Bearer Tokens automatically behind-the-scenes for friction-less stateless JWT support.\n\nA snapshot of these above features is captured in the high-level public API below:\n\n```php\nclass JsonServiceClient\n{\n    public static ?RequestFilter $globalRequestFilter;\n    public ?RequestFilter $requestFilter;\n    public static ?ResponseFilter $globalResponseFilter;\n    public ?ResponseFilter $responseFilter;\n    public static ?ExceptionFilter $globalExceptionFilter;\n    public ?ExceptionFilter $exceptionFilter;\n    public ?Callback $onAuthenticationRequired;\n\n    public string $baseUrl;\n    public string $replyBaseUrl;\n    public string $oneWayBaseUrl;\n    public ?string $userName;\n    public ?string $password;\n    public ?string $bearerToken;\n    public ?string $refreshToken;\n    public ?string $refreshTokenUri;\n    public bool $useTokenCookie;\n    public array $headers = [];\n    public array $cookies = [];\n\n    public function __construct(string $baseUrl);\n\n    public function setCredentials(?string $userName = null, ?string $password = null) : void;\n    public function getTokenCookie();\n    public function getRefreshTokenCookie();\n\n    public function get(IReturn|IReturnVoid|string $request, ?array $args = null): mixed;\n    public function post(IReturn|IReturnVoid|string $request, mixed $body = null, ?array $args = null): mixed;\n    public function put(IReturn|IReturnVoid|string $request, mixed $body = null, ?array $args = null): mixed;\n    public function patch(IReturn|IReturnVoid|string $request, mixed $body = null, ?array $args = null): mixed;\n    public function delete(IReturn|IReturnVoid|string $request, ?array $args = null): mixed;\n    public function options(IReturn|IReturnVoid|string $request, ?array $args = null): mixed;\n    public function head(IReturn|IReturnVoid|string $request, ?array $args = null): mixed;\n    public function send(IReturn|IReturnVoid|null $request, ?string $method = null, mixed $body = null, \n        ?array $args = null): mixed;\n\n    public function getUrl(string $path, mixed $responseAs = null, mixed $args = null): mixed;\n    public function postUrl(string $path, mixed $responseAs = null, mixed $body = null, ?array $args = null): mixed;\n    public function putUrl(string $path, mixed $responseAs = null, mixed $body = null, ?array $args = null): mixed;\n    public function patchUrl(string $path, mixed $responseAs = null, mixed $body = null, ?array $args = null): mixed;\n    public function deleteUrl(string $path, mixed $responseAs = null, mixed $args = null): mixed;\n    public function optionsUrl(string $path, mixed $responseAs = null, mixed $args = null): mixed;\n    public function headUrl(string $path, mixed $responseAs = null, mixed $args = null): mixed;\n    public function sendUrl(string $path, ?string $method = null, mixed $responseAs = null, mixed $body = null, \n        mixed $args = null): mixed\n\n    public function sendAll(array $requestDtos): mixed;       # Auto Batch Reply Requests\n    public function sendAllOneWay(array $requestDtos): void;  # Auto Batch Oneway Requests\n```\n\n### Change Default Server Configuration\n\nThe above defaults are also overridable on the ServiceStack Server by modifying the default config on the `NativeTypesFeature` Plugin, e.g:\n\n```csharp\nvar nativeTypes = this.GetPlugin\u003cNativeTypesFeature\u003e();\nnativeTypes.MetadataTypesConfig.AddResponseStatus = true;\n...\n```\n\nPHP specific functionality can be added by `PhpGenerator`\n\n```csharp\nPhpGenerator.ServiceStackImports.Add(\"MyNamespace\\\\Type\");\n```\n\n### Customize DTO Type generation\n\nAdditional PHP specific customization can be statically configured like `PreTypeFilter`, `InnerTypeFilter` \u0026 `PostTypeFilter`\n(available in all languages) can be used to inject custom code in the generated DTOs output.\n\nUse the `PreTypeFilter` to generate source code before and after a Type definition, e.g. this will append a custom `MyAnnotation`\nannotation on non enum \u0026 interface types:\n\n```csharp\nPhpGenerator.PreTypeFilter = (sb, type) =\u003e {\n    if (!type.IsEnum.GetValueOrDefault() \u0026\u0026 !type.IsInterface.GetValueOrDefault())\n    {\n        sb.AppendLine(\"#[MyAnnotation]\");\n    }\n};\n```\n\nThe `InnerTypeFilter` gets invoked just after the Type Definition which can be used to generate common members for all Types and interfaces, e.g:\n\n```csharp\nPhpGenerator.InnerTypeFilter = (sb, type) =\u003e {\n    sb.AppendLine(\"public ?int $id;\");\n    sb.AppendLine(\"public function getId(): int { return $this-\u003eid ?? ($this-\u003eid=rand()); }\");\n};\n```\n\nThere's also `PrePropertyFilter` \u0026 `PostPropertyFilter` for generating source before and after properties, e.g:\n\n```csharp\nPhpGenerator.PrePropertyFilter = (sb , prop, type) =\u003e {\n    if (prop.Name == \"Id\")\n    {\n        sb.AppendLine(\"#[IsInt]\");\n    }\n};\n```\n\n### Emit custom code\n\nTo enable greater flexibility when generating complex Typed DTOs, you can use `[Emit{Language}]` attributes to generate code before each type or property.\n\nThese attributes can be used to generate different attributes or annotations to enable client validation for different validation libraries in different languages, e.g:\n\n```csharp\n[EmitCode(Lang.Php, \"// App User\")]\n[EmitPhp(\"#[Validate]\")]\npublic class User : IReturn\u003cUser\u003e\n{\n    [EmitPhp(\"#[IsNotEmpty]\", \"#[IsEmail]\")]\n    [EmitCode(Lang.Swift | Lang.Dart, new[]{ \"@isNotEmpty()\", \"@isEmail()\" })]\n    public string Email { get; set; }\n}\n```\n\nWhich will generate `[EmitPhp]` code in PHP DTOs:\n\n```php\n// App User\n#[Validate]\nclass User implements JsonSerializable \n{\n    public function __construct(\n        #[IsNotEmpty]\n        #[IsEmail]\n        /** @var string|null */\n        public ?string email=null\n    ) {\n    }\n    //...\n}\n```\n\nWhilst the generic `[EmitCode]` attribute lets you emit the same code in multiple languages with the same syntax.\n\n### PHP Reference Example\n\nLets walk through a simple example to see how we can use ServiceStack's PHP DTO annotations in our PHP `JsonServiceClient`.\n\nFirstly we'll need to add a PHP Reference to the remote ServiceStack Service by **right-clicking** on a project folder and clicking on `ServiceStack Reference...` (as seen in the above screenshot).\n\nThis will import the remote Services dtos into your local project which looks similar to:\n\n```php\n\u003c?php namespace dtos;\n/* Options:\nDate: 2023-10-14 08:05:09\nVersion: 6.111\nTip: To override a DTO option, remove \"//\" prefix before updating\nBaseUrl: https://techstacks.io\n\n//GlobalNamespace: dtos\n//MakePropertiesOptional: False\n//AddServiceStackTypes: True\n//AddResponseStatus: False\n//AddImplicitVersion: \n//AddDescriptionAsComments: True\n//IncludeTypes: \n//ExcludeTypes: \n//DefaultImports: \n*/\n\n// @Route(\"/technology/{Slug}\")\n#[Returns('GetTechnologyResponse')]\nclass GetTechnology implements IReturn, IRegisterStats, IGet, JsonSerializable\n{\n    public function __construct(\n        /** @var string|null */\n        public ?string $slug=null\n    ) {\n    }\n\n    /** @throws Exception */\n    public function fromMap($o): void {\n        if (isset($o['slug'])) $this-\u003eslug = $o['slug'];\n    }\n    \n    /** @throws Exception */\n    public function jsonSerialize(): mixed\n    {\n        $o = [];\n        if (isset($this-\u003eslug)) $o['slug'] = $this-\u003eslug;\n        return empty($o) ? new class(){} : $o;\n    }\n    public function getTypeName(): string { return 'GetTechnology'; }\n    public function getMethod(): string { return 'GET'; }\n    public function createResponse(): mixed { return new GetTechnologyResponse(); }\n}\n\nclass GetTechnologyResponse implements JsonSerializable\n{\n    public function __construct(\n        /** @var DateTime */\n        public DateTime $created=new DateTime(),\n        /** @var Technology|null */\n        public ?Technology $technology=null,\n        /** @var array\u003cTechnologyStack\u003e|null */\n        public ?array $technologyStacks=null,\n        /** @var ResponseStatus|null */\n        public ?ResponseStatus $responseStatus=null\n    ) {\n    }\n\n    /** @throws Exception */\n    public function fromMap($o): void {\n        if (isset($o['created'])) $this-\u003ecreated = JsonConverters::from('DateTime', $o['created']);\n        if (isset($o['technology'])) $this-\u003etechnology = JsonConverters::from('Technology', $o['technology']);\n        if (isset($o['technologyStacks'])) $this-\u003etechnologyStacks = JsonConverters::fromArray('TechnologyStack', $o['technologyStacks']);\n        if (isset($o['responseStatus'])) $this-\u003eresponseStatus = JsonConverters::from('ResponseStatus', $o['responseStatus']);\n    }\n    \n    /** @throws Exception */\n    public function jsonSerialize(): mixed\n    {\n        $o = [];\n        if (isset($this-\u003ecreated)) $o['created'] = JsonConverters::to('DateTime', $this-\u003ecreated);\n        if (isset($this-\u003etechnology)) $o['technology'] = JsonConverters::to('Technology', $this-\u003etechnology);\n        if (isset($this-\u003etechnologyStacks)) $o['technologyStacks'] = JsonConverters::toArray('TechnologyStack', $this-\u003etechnologyStacks);\n        if (isset($this-\u003eresponseStatus)) $o['responseStatus'] = JsonConverters::to('ResponseStatus', $this-\u003eresponseStatus);\n        return empty($o) ? new class(){} : $o;\n    }\n}\n```\n\nBy default the generated PHP DTOs use the default `dtos` namespace which you can reference as individual classes:\n\n```php\nuse dtos\\GetTechnology;\nuse dtos\\GetTechnologyResponse;\n```\n\nOr combined within a single line with:\n\n```php\nuse dtos\\{GetTechnology,GetTechnologyResponse};\n\n$request = new GetTechnology();\n```\n\n### Making Typed API Requests\n\nMaking API Requests in PHP is the same as all other [ServiceStack's Service Clients](https://docs.servicestack.net/clients-overview)\nby sending a populated Request DTO using a `JsonServiceClient` which returns typed Response DTO.\n\nSo the only things we need to make any API Request is the `JsonServiceClient` from the `servicestack/client` package and any\nDTO's we're using from generated PHP ServiceStack Reference, e.g:\n\n```php\n\u003c?php\n\nrequire_once __DIR__ . '/vendor/autoload.php'; // Autoload files using Composer autoload\nrequire_once 'dtos.php';\n\nuse dtos\\GetTechnology;\nuse dtos\\GetTechnologyResponse;\nuse ServiceStack\\JsonServiceClient;\n\n$client = JsonServiceClient::create(\"https://techstacks.io\");\n\n/** @var GetTechnologyResponse $response */\n$response = $client-\u003eget(new GetTechnology(slug:\"ServiceStack\"));\n\n$tech = $response-\u003etechnology; // typed to Technology\n\necho \"$tech-\u003ename by $tech-\u003evendorName from $tech-\u003eproductUrl\\n\";\necho \"$tech-\u003ename TechStacks:\\n\";\nprint_r($response-\u003etechnologyStacks);\n```\n\n### PHPDoc Typed Annotations\n\nWhilst PHP is a dynamic language with limited support for static typing and generics included in the PHP language itself,\nyou can get many of the static type and intelli-sense benefits of a typed language by using\n[PHPDoc type hints](https://phpstan.org/writing-php-code/phpdoc-types) to annotate the APIs Typed Responses which lights\nup static analysis and intelli-sense benefits in smart IDEs like PhpStorm:\n\n```php\n/** @var GetTechnologyResponse $response */\n$response = $client-\u003eget(new GetTechnology(slug:\"ServiceStack\"));\necho $response-\u003etechnology-\u003ename . PHP_EOL; //intelli-sense\n\n/** @var QueryResponse\u003cTechnologyView\u003e $response */\n$response = $client-\u003eget(new FindTechnologies(ids:[2,4,8]));\n\n/** @var TechnologyView[] $results */\n$results = $response-\u003eresults;\necho $results[0]-\u003ename . PHP_EOL; //intelli-sense\n```\n\n### Constructors Initializer\n\nAll PHP Reference DTOs also implements promoted constructors making them much nicer to populate using a\nconstructor expression with named params syntax we're used to in C#, so instead of:\n\n```php\n$request = new Authenticate();\n$request-\u003eprovider = \"credentials\";\n$request-\u003euserName = $userName;\n$request-\u003epassword = $password;\n$request-\u003erememberMe = true;\n$response = $client-\u003epost($request);\n```\n\nYou can populate DTOs with a single constructor expression without any loss of PHP's Typing benefits:\n\n```php\n$response = $client-\u003epost(new Authenticate(\n    provider: \"credentials\",\n    userName: \"test\",\n    password: \"test\",\n    rememberMe: true));\n```\n\n### Sending additional arguments with Typed API Requests\n\nMany AutoQuery Services utilize [implicit conventions](https://docs.servicestack.net/autoquery-rdbms#implicit-conventions)\nto query fields that aren't explicitly defined on AutoQuery Request DTOs, these can be queried by specifying additional arguments\nwith the typed Request DTO, e.g:\n\n```php\n/** @var QueryResponse\u003cTechnologyView\u003e $response */\n$response = $client-\u003eget(new FindTechnologies(), args:[\"vendorName\" =\u003e \"ServiceStack\"]);\n```\n\n### Making API Requests with URLs\n\nIn addition to making Typed API Requests you can also call Services using relative or absolute urls, e.g:\n\n```php\n$client-\u003egetUrl(\"/technology/ServiceStack\", responseAs:new GetTechnologyResponse());\n\n$client-\u003egetUrl(\"https://techstacks.io/technology/ServiceStack\", \n    responseAs:new GetTechnologyResponse());\n\n// https://techstacks.io/technology?Slug=ServiceStack\n$args = [\"slug\" =\u003e \"ServiceStack\"]\nclient.getUrl(\"/technology\", args:$args, responseAs:new GetTechnologyResponse()); \n```\n\nas well as POST Request DTOs to custom urls:\n\n```php\n$client-\u003epostUrl(\"/custom-path\", $request, args:[\"slug\" =\u003e \"ServiceStack\"]);\n\n$client-\u003epostUrl(\"http://example.org/custom-path\", $request);\n```\n\n### Raw Data Responses\n\nThe `JsonServiceClient` also supports Raw Data responses like `string` and `byte[]` which also get a Typed API once\ndeclared on Request DTOs using the `IReturn\u003cT\u003e` marker:\n\n```csharp\npublic class ReturnString : IReturn\u003cstring\u003e {}\npublic class ReturnBytes : IReturn\u003cbyte[]\u003e {}\n```\n\nWhich can then be accessed as normal, with their Response typed to a PHP `string` or `ByteArray` for raw `byte[]` responses:\n\n```php\n/** @var string $str */ \n$str = client.get(new ReturnString());\n\n/** @var ByteArray $data */ \n$data = client-\u003eget(new ReturnBytes());\n```\n\n### Authenticating using Basic Auth\n\nBasic Auth support is implemented in `JsonServiceClient` and follows the same API made available in the C# Service Clients where the `userName/password` properties can be set individually, e.g:\n\n```php\n$client = new JsonServiceClient($baseUrl);\n$client-\u003eusername = user;\n$client-\u003epassword = pass;\n\n$response = client-\u003eget(new SecureRequest());\n```\n\nOr use `$client-\u003esetCredentials()` to have them set both together.\n\n### Authenticating using Credentials\n\nAlternatively you can authenticate using userName/password credentials by\n[adding a PHP Reference](#add-php-reference)\nto your remote ServiceStack Instance and sending a populated `Authenticate` Request DTO, e.g:\n\n```php\n$request = new Authenticate();\n$request-\u003eprovider = \"credentials\";\n$request-\u003euserName = $userName;\n$request-\u003epassword = $password;\n$request-\u003eremember_me = true;\n\n$response = client-\u003epost(request);\n```\n\nThis will populate the `JsonServiceClient` with [Session Cookies](https://docs.servicestack.net/sessions#cookie-session-ids)\nwhich will transparently be sent on subsequent requests to make authenticated requests.\n\n### Authenticating using JWT\n\nUse the `bearerToken` property to Authenticate with a [ServiceStack JWT Provider](https://docs.servicestack.net/jwt-authprovider)\nusing a JWT Token:\n\n```php\n$client-\u003ebearerToken = $jwt;\n```\n\nAlternatively you can use just a [Refresh Token](https://docs.servicestack.net/jwt-authprovider#refresh-tokens) instead:\n\n```php\n$client-\u003erefreshYoken = $refreshToken;\n```\n\nWhere the client will automatically fetch a new JWT Bearer Token using the Refresh Token for authenticated requests.\n\n### Authenticating using an API Key\n\nUse the `bearerToken` property to Authenticate with an [API Key](https://docs.servicestack.net/api-key-authprovider):\n\n```php\n$client-\u003ebearerToken = $apiKey;\n```\n\n### Transparently handle 401 Unauthorized Responses\n\nIf the server returns a 401 Unauthorized Response either because the client was Unauthenticated or the\nconfigured Bearer Token or API Key used had expired or was invalidated, you can use `onAuthenticationRequired`\ncallback to re-configure the client before automatically retrying the original request, e.g:\n\n```php\n$client = new JsonServiceClient(BASE_URL);\n$authClient = new JsonServiceClient(AUTH_URL);\n\n$client-\u003eonAuthenticationRequired = new class($client, $authClient) implements Callback {\n    public function __construct(public JsonServiceClient $client, public JsonServiceClient $authClient) {}\n    public function call(): void {\n        $this-\u003eauthClient-\u003esetCredentials(\"test\", \"test\");\n        $this-\u003eclient-\u003ebearerToken = $this-\u003eauthClient-\u003eget(new Authenticate())-\u003egetRefreshTokenCookie();\n    }\n};\n\n// Automatically retries requests returning 401 Responses with new bearerToken\n$response = client-\u003eget(new Secured());\n```\n\n### Automatically refresh Access Tokens\n\nWith the [Refresh Token support in JWT](https://docs.servicestack.net/jwt-authprovider#refresh-tokens)\nyou can use the `refresh_token` property to instruct the Service Client to automatically fetch new JWT Tokens behind\nthe scenes before automatically retrying failed requests due to invalid or expired JWTs, e.g:\n\n```php\n// Authenticate to get new Refresh Token\n$authClient = new JsonServiceClient(AUTH_URL);\n$authClient.userName = $userName;\n$authClient.password = $password;\n$authResponse = $authClient-\u003eget(new Authenticate());\n\n// Configure client with RefreshToken\n$client-\u003erefreshToken = $authResponse-\u003erefreshToken;\n\n// Call authenticated Services and clients will automatically retrieve new JWT Tokens as needed\n$response = client-\u003eget(new Secured());\n```\n\nUse the `refreshTokenUri` property when refresh tokens need to be sent to a different ServiceStack Server, e.g:\n\n```php\n$client-\u003erefreshToken = $refreshToken;\n$client-\u003erefreshTokenUri = AUTH_URL . \"/access-token\";\n```\n\n## DTO Customization Options\n\nIn most cases you'll just use the generated PHP DTO's as-is, however you can further customize how the DTO's are generated\nby overriding the default options.\n\nThe header in the generated DTO's show the different options PHP native types support with their defaults.\nDefault values are shown with the comment prefix of `//`. To override a value, remove the `//` and specify the value to\nthe right of the `:`. Any uncommented value will be sent to the server to override any server defaults.\n\nThe DTO comments allows for customizations for how DTOs are generated. The default options that were used to generate the\nDTO's are repeated in the header comments of the generated DTOs, options that are preceded by a PHP comment `//` are defaults\nfrom the server, any uncommented value will be sent to the server to override any server defaults.\n\n```php\n\u003c?php namespace dtos;\n/* Options:\nDate: 2023-10-14 08:05:09\nVersion: 6.111\nTip: To override a DTO option, remove \"//\" prefix before updating\nBaseUrl: https://techstacks.io\n\n//GlobalNamespace: dtos\n//MakePropertiesOptional: False\n//AddServiceStackTypes: True\n//AddResponseStatus: False\n//AddImplicitVersion: \n//AddDescriptionAsComments: True\n//IncludeTypes: \n//ExcludeTypes: \n//DefaultImports: \n*/\n```\n\nWe'll go through and cover each of the above options to see how they affect the generated DTO's:\n\n### Change Default Server Configuration\n\nThe above defaults are also overridable on the ServiceStack Server by modifying the default config on the\n`NativeTypesFeature` Plugin, e.g:\n\n```csharp\n//Server example in C#\nvar nativeTypes = this.GetPlugin\u003cNativeTypesFeature\u003e();\nnativeTypes.MetadataTypesConfig.AddResponseStatus = true;\n...\n```\n\nWe'll go through and cover each of the above options to see how they affect the generated DTO's:\n\n### GlobalNamespace\n\nThis lets you specify which namespace you want the DTOs to be generated in:\n\n```php\n\u003c?php namespace dtos;\n```\n\n### AddResponseStatus\n\nAutomatically add a `$responseStatus` property on all Response DTOs, regardless if it wasn't already defined:\n\n```php\nclass GetTechnologyResponse implements JsonSerializable\n{\n    ...\n    /** @var ResponseStatus|null */\n    public ?ResponseStatus $responseStatus=null\n}\n```\n\n### AddImplicitVersion\n\nLets you specify the Version number to be automatically populated in all Request DTOs sent from the client:\n\n```php\nclass GetTechnology implements IReturn, IRegisterStats, IGet, JsonSerializable\n{\n    public int $version = 1;\n    ...\n}\n```\n\nThis lets you know what Version of the Service Contract that existing clients are using making it easy to\nimplement ServiceStack's [recommended versioning strategy](http://stackoverflow.com/a/12413091/85785).\n\n### IncludeTypes\n\nIs used as a Whitelist to specify only the types you would like to have code-generated:\n\n```\n/* Options:\nIncludeTypes: GetTechnology,GetTechnologyResponse\n```\n\nWill only generate `GetTechnology` and `GetTechnologyResponse` DTOs:\n\n```php\nclass GetTechnology implements IReturn, IRegisterStats, IGet, JsonSerializable\n\n// ...\nclass GetTechnologyResponse implements JsonSerializable\n// ...\n```\n\n#### Include Generic Types\n\nUse .NET's Type Name to include Generic Types, i.e. the Type name separated by the backtick followed by the number of\ngeneric arguments, e.g:\n\n```\nIncludeTypes: IReturn`1,MyPair`2\n```\n\n#### Include Request DTO and its dependent types\n\nYou can include a Request DTO and all its dependent types with a `.*` suffix on the Request DTO, e.g:\n\n```\n/* Options:\nIncludeTypes: GetTechnology.*\n```\n\nWhich will include the `GetTechnology` Request DTO, the `GetTechnologyResponse` Response DTO and all Types that they both reference.\n\n#### Include All Types within a C# namespace\n\nIf your DTOs are grouped into different namespaces they can be all included using the `/*` suffix, e.g:\n\n```\n/* Options:\nIncludeTypes: MyApp.ServiceModel.Admin/*\n```\n\nThis will include all DTOs within the `MyApp.ServiceModel.Admin` C# namespace.\n\n#### Include All Services in a Tag Group\n\nServices [grouped by Tag](https://docs.servicestack.net/api-design#group-services-by-tag) can be used in the `IncludeTypes`\nwhere tags can be specified using braces in the format `{tag}` or `{tag1,tag2,tag3}`, e.g:\n\n```\n/* Options:\nIncludeTypes: {web,mobile}\n```\n\nOr individually:\n\n```\n/* Options:\nIncludeTypes: {web},{mobile}\n```\n\n### ExcludeTypes\nIs used as a Blacklist to specify which types you would like excluded from being generated:\n\n```\n/* Options:\nExcludeTypes: GetTechnology,GetTechnologyResponse\n```\n\nWill exclude `GetTechnology` and `GetTechnologyResponse` DTOs from being generated.\n\n### DefaultImports\n\nUse `DefaultImports` for specifying additional imports in your generated PHP DTOs.\n\n```php\n/* Options:\n...\nDefaultImports: MyType\n*/\n```\n\nWhich will include the type in the generated DTOs:\n\n```php\nuse MyType;\n```\n\n## Customize Serialization\n\nThe `servicestack/client` client lib allows for flexible serialization customization where you can change how different\n.NET Types are serialized and deserialized into native PHP types.\n\nTo illustrate this we'll walk through how serialization of properties containing binary data to Base64 is implemented.\n\nFirst we specify the PHP DTO generator to emit `ByteArray` type hint for the popular .NET binary data types:\n\n```csharp\nPhpGenerator.TypeAliases[typeof(byte[]).Name] = \"ByteArray\";\nPhpGenerator.TypeAliases[typeof(Stream).Name] = \"ByteArray\";\n```\n\nIn the PHP app we can then specify the serializers and deserializers to use for deserializing properties with the `ByteArray`\ndata type which converts binary data to/from Base64:\n\n```php\nuse JsonSerializable;\n\nclass ByteArray implements JsonSerializable\n{\n    public ?string $data;\n\n    public function __construct(?string $data=null)\n    {\n        $this-\u003edata = isset($data) ? base64_decode($data) : null;\n    }\n\n    public function jsonSerialize(): mixed\n    {\n        return base64_encode($this-\u003edata);\n    }\n}\n```\n\n## Inspect Utils\n\nTo help clients with inspecting API Responses the `servicestack/client` library also includes a number of helpful utils\nto quickly visualizing API outputs.\n\nFor a basic indented object graph you can use `Inspect::dump` to capture and `Inspect::printDump` to print the output\nof any API Response, e.g:\n\n```php\n$orgName = \"php\";\n\n$opts = [\n    \"http\" =\u003e [\n        \"header\" =\u003e \"User-Agent: gist.cafe\\r\\n\"\n    ]\n];\n$context = stream_context_create($opts);\n$json = file_get_contents(\"https://api.github.com/orgs/{$orgName}/repos\", false, $context);\n$orgRepos = array_map(function($x) {\n    $x = get_object_vars($x);\n    return [\n        \"name\"        =\u003e $x[\"name\"],\n        \"description\" =\u003e $x[\"description\"],\n        \"url\"         =\u003e $x[\"url\"],\n        \"lang\"        =\u003e $x[\"language\"],\n        \"watchers\"    =\u003e $x[\"watchers\"],\n        \"forks\"       =\u003e $x[\"forks\"],\n    ];\n}, json_decode($json));\nusort($orgRepos, function($a,$b) { return $b[\"watchers\"] - $a[\"watchers\"]; });\n\necho  \"Top 3 {$orgName} GitHub Repos:\\n\";\nInspect::printDump(array_slice($orgRepos, 0, 3));\n\necho  \"\\nTop 10 {$orgName} GitHub Repos:\\n\";\nInspect::printDumpTable(array_map(function($x) {\n    return [\n        \"name\"        =\u003e $x[\"name\"],\n        \"lang\"        =\u003e $x[\"lang\"],\n        \"watchers\"    =\u003e $x[\"watchers\"],\n        \"forks\"       =\u003e $x[\"forks\"],\n    ];\n}, array_slice($orgRepos, 0, 10)));\n```\n\nOutput:\n\n```\nTop 3 php GitHub Repos:\n[\n    {\n        name: php-src,\n        description: The PHP Interpreter,\n        url: https://api.github.com/repos/php/php-src,\n        lang: C,\n        watchers: 36122,\n        forks: 7653\n    },\n    {\n        name: web-php,\n        description: The www.php.net site,\n        url: https://api.github.com/repos/php/web-php,\n        lang: PHP,\n        watchers: 785,\n        forks: 532\n    },\n    {\n        name: php-gtk-src,\n        description: The PHP GTK Bindings,\n        url: https://api.github.com/repos/php/php-gtk-src,\n        lang: C++,\n        watchers: 201,\n        forks: 59\n    }\n]\n```\n\nFor tabular result-sets you can use `Inspect::table` to capture and `Inspect::printTable` to print API result-sets in a\nhuman-friendly markdown table, e.g:\n\n```php\necho \"\\nTop 10 $orgName Repos:\\n\"\nInspect::printTable(array_slice($orgRepos, 0, 10));\n```\n\nOutput:\n\n```\nTop 10 php GitHub Repos:\n+------------------------------------------------+\n|      name      |    lang    | watchers | forks |\n|------------------------------------------------|\n| php-src        | C          |    36122 |  7653 |\n| web-php        | PHP        |      785 |   532 |\n| php-gtk-src    | C++        |      201 |    59 |\n| web-qa         | PHP        |       68 |    39 |\n| phd            | PHP        |       68 |    44 |\n| web-bugs       | PHP        |       58 |    68 |\n| presentations  | HTML       |       45 |    27 |\n| web-doc-editor | JavaScript |       43 |    37 |\n| systems        | C          |       41 |    27 |\n| web-wiki       | PHP        |       35 |    29 |\n+------------------------------------------------+\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservicestack%2Fservicestack-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fservicestack%2Fservicestack-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservicestack%2Fservicestack-php/lists"}