Skip to content

Commit a82b640

Browse files
authored
Merge pull request #112 from DoclerLabs/php81
php 8.1 features
2 parents 84f5211 + 309064d commit a82b640

File tree

61 files changed

+2650
-52
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2650
-52
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [10.8.0] - 2024.05.17
8+
### Added
9+
- Readonly properties (php 8.1)
10+
- Enums (php 8.1)
11+
712
## [10.7.1] - 2024.05.07
813
### Fixed
914
- Missing DateTimeInterface import for php 8.1+

docker-compose.yml

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.7'
2-
31
services:
42
php:
53
build:

example/PetStoreClient/README.md

+13-13
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,28 @@ $result = $client->updatePet($request);
4141

4242
### pet
4343
Endpoints:
44-
- **updatePet** - Update an existing pet by Id
45-
- **addPet** - Add a new pet to the store
46-
- **findPetsByStatus** - Multiple status values can be provided with comma separated strings
47-
- **findPetsByTags** - Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
48-
- **getPetById** - Returns a single pet
44+
- **updatePet** - Update an existing pet by Id
45+
- **addPet** - Add a new pet to the store
46+
- **findPetsByStatus** - Multiple status values can be provided with comma separated strings
47+
- **findPetsByTags** - Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
48+
- **getPetById** - Returns a single pet
4949
- **updatePetWithForm**
5050
- **deletePet**
5151

5252
### store
5353
Endpoints:
54-
- **getInventory** - Returns a map of status codes to quantities
55-
- **placeOrder** - Place a new order in the store
56-
- **getOrderById** - For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
57-
- **deleteOrder** - For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
54+
- **getInventory** - Returns a map of status codes to quantities
55+
- **placeOrder** - Place a new order in the store
56+
- **getOrderById** - For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
57+
- **deleteOrder** - For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
5858

5959
### user
6060
Endpoints:
61-
- **createUser** - This can only be done by the logged in user.
62-
- **createUsersWithListInput** - Creates list of users with given input array
61+
- **createUser** - This can only be done by the logged in user.
62+
- **createUsersWithListInput** - Creates list of users with given input array
6363
- **loginUser**
6464
- **logoutUser**
6565
- **getUserByName**
66-
- **updateUser** - This can only be done by the logged in user.
67-
- **deleteUser** - This can only be done by the logged in user.
66+
- **updateUser** - This can only be done by the logged in user.
67+
- **deleteUser** - This can only be done by the logged in user.
6868

src/Ast/Builder/CodeBuilder.php

+35-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace DoclerLabs\ApiClientGenerator\Ast\Builder;
66

77
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
8+
use DoclerLabs\ApiClientGenerator\Entity\FieldType;
89
use DoclerLabs\ApiClientGenerator\Entity\ImportCollection;
910
use InvalidArgumentException;
1011
use PhpParser\BuilderFactory;
@@ -16,6 +17,7 @@
1617
use PhpParser\Node\Expr\Array_;
1718
use PhpParser\Node\Expr\ArrayDimFetch;
1819
use PhpParser\Node\Expr\ArrayItem;
20+
use PhpParser\Node\Expr\ArrowFunction;
1921
use PhpParser\Node\Expr\Assign;
2022
use PhpParser\Node\Expr\BinaryOp;
2123
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
@@ -102,11 +104,29 @@ public function closure(
102104
return new Closure($subNodes);
103105
}
104106

107+
public function arrowFunction(Expr $expr, array $params, string $returnType, bool $isStatic = true): ArrowFunction
108+
{
109+
if (!empty($params)) {
110+
$subNodes['params'] = $params;
111+
}
112+
113+
$subNodes['expr'] = $expr;
114+
$subNodes['returnType'] = $returnType;
115+
$subNodes['static'] = $isStatic;
116+
117+
return new ArrowFunction($subNodes);
118+
}
119+
105120
public function class(string $name): ClassBuilder
106121
{
107122
return new ClassBuilder($name);
108123
}
109124

125+
public function enum(string $name): EnumBuilder
126+
{
127+
return new EnumBuilder($name);
128+
}
129+
110130
public function array(array $items): Array_
111131
{
112132
$arrayItems = [];
@@ -141,14 +161,15 @@ public function localProperty(
141161
string $type,
142162
string $docType,
143163
bool $nullable = false,
144-
Expr $default = null
164+
Expr $default = null,
165+
bool $readonly = false
145166
): Property {
146167
$property = $this
147168
->property($name)
148169
->makePrivate();
149170

150171
if (!empty($type) && $this->phpVersion->isPropertyTypeHintSupported()) {
151-
if ($nullable) {
172+
if ($nullable && $type !== FieldType::PHP_TYPE_MIXED) {
152173
$property->setDefault(null);
153174
$type = '?' . $type;
154175
}
@@ -163,6 +184,13 @@ public function localProperty(
163184
$property->setDefault($default);
164185
}
165186

187+
if (
188+
$readonly
189+
&& $this->phpVersion->isReadonlyPropertySupported()
190+
) {
191+
$property->makeReadonly();
192+
}
193+
166194
return $property->getNode();
167195
}
168196

@@ -176,6 +204,11 @@ public function localNullsafePropertyFetch(string $propertyName): NullsafeProper
176204
return new NullsafePropertyFetch($this->var('this'), BuilderHelpers::normalizeIdentifierOrExpr($propertyName));
177205
}
178206

207+
public function nullsafePropertyFetch(Expr $var, string $propertyName): NullsafePropertyFetch
208+
{
209+
return new NullsafePropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($propertyName));
210+
}
211+
179212
public function localMethodCall(string $methodName, array $args = []): MethodCall
180213
{
181214
return $this->methodCall($this->var('this'), $methodName, $args);

src/Ast/Builder/EnumBuilder.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoclerLabs\ApiClientGenerator\Ast\Builder;
6+
7+
use PhpParser\Builder\Enum_;
8+
9+
class EnumBuilder extends Enum_
10+
{
11+
public function getName(): string
12+
{
13+
return $this->name;
14+
}
15+
}

src/Ast/Builder/MethodBuilder.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace DoclerLabs\ApiClientGenerator\Ast\Builder;
66

77
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
8+
use DoclerLabs\ApiClientGenerator\Entity\FieldType;
89
use PhpParser\Builder;
910
use PhpParser\Builder\Method;
1011
use PhpParser\Comment\Doc;
@@ -73,7 +74,11 @@ public function setReturnType($type, $isNullable = false): self
7374
}
7475

7576
if ($isNullable) {
76-
if ($this->phpVersion->isNullableTypeHintSupported() && is_string($type)) {
77+
if (
78+
$this->phpVersion->isNullableTypeHintSupported()
79+
&& is_string($type)
80+
&& $type !== FieldType::PHP_TYPE_MIXED
81+
) {
7782
return parent::setReturnType(sprintf('?%s', $type));
7883
}
7984

src/Ast/PhpVersion.php

+15
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ public function isMixedTypehintSupported(): bool
8989
return $this->isVersionGreaterThanOrEqualTo80();
9090
}
9191

92+
public function isEnumSupported(): bool
93+
{
94+
return $this->isVersionGreaterThanOrEqualTo81();
95+
}
96+
97+
public function isReadonlyPropertySupported(): bool
98+
{
99+
return $this->isVersionGreaterThanOrEqualTo81();
100+
}
101+
92102
private function isVersionGreaterThanOrEqualTo71(): bool
93103
{
94104
return $this->phpVersion >= self::VERSION_PHP71;
@@ -103,4 +113,9 @@ private function isVersionGreaterThanOrEqualTo80(): bool
103113
{
104114
return $this->phpVersion >= self::VERSION_PHP80;
105115
}
116+
117+
private function isVersionGreaterThanOrEqualTo81(): bool
118+
{
119+
return $this->phpVersion >= self::VERSION_PHP81;
120+
}
106121
}

src/Entity/Field.php

+34-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace DoclerLabs\ApiClientGenerator\Entity;
66

7+
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
78
use DoclerLabs\ApiClientGenerator\Entity\Constraint\ConstraintCollection;
89
use DoclerLabs\ApiClientGenerator\Naming\CaseCaster;
910
use DoclerLabs\ApiClientGenerator\Naming\SchemaCollectionNaming;
@@ -30,6 +31,7 @@ class Field
3031
private mixed $discriminator = null;
3132

3233
public function __construct(
34+
private PhpVersion $phpVersion,
3335
private string $name,
3436
private FieldType $type,
3537
private ConstraintCollection $constraints,
@@ -152,6 +154,11 @@ public function isDate(): bool
152154
return $this->type->isString() && $isDateFormat;
153155
}
154156

157+
public function isEnum(): bool
158+
{
159+
return !empty($this->enumValues);
160+
}
161+
155162
public function isObject(): bool
156163
{
157164
return $this->type->isObject();
@@ -174,6 +181,13 @@ public function isArrayOfObjects(): bool
174181
&& $this->getArrayItem()->isObject();
175182
}
176183

184+
public function isArrayOfEnums(): bool
185+
{
186+
return $this->isArray()
187+
&& $this->getArrayItem() !== null
188+
&& $this->getArrayItem()->isEnum();
189+
}
190+
177191
public function getDefault(): mixed
178192
{
179193
return $this->default;
@@ -220,12 +234,23 @@ public function getPhpClassName(): string
220234
return 'DateTimeInterface';
221235
}
222236

237+
if ($this->isEnum() && $this->phpVersion->isEnumSupported()) {
238+
return $this->referenceName . (str_ends_with($this->referenceName, 'Enum') ? '' : 'Enum');
239+
}
240+
223241
throw new RuntimeException('Call of getPhpClassName on the non-composite field.');
224242
}
225243

226244
public function getPhpTypeHint(): string
227245
{
228-
if ($this->isComposite() || $this->isDate()) {
246+
if (
247+
$this->isComposite()
248+
|| $this->isDate()
249+
|| (
250+
$this->isEnum()
251+
&& $this->phpVersion->isEnumSupported()
252+
)
253+
) {
229254
return $this->getPhpClassName();
230255
}
231256

@@ -240,7 +265,14 @@ public function getPhpDocType(bool $allowNullable = true): string
240265

241266
$nullableSuffix = '';
242267
$arraySuffix = '';
243-
if ($this->isComposite() || $this->isDate()) {
268+
if (
269+
$this->isComposite()
270+
|| $this->isDate()
271+
|| (
272+
$this->isEnum()
273+
&& $this->phpVersion->isEnumSupported()
274+
)
275+
) {
244276
$typeHint = $this->getPhpClassName();
245277
} else {
246278
$typeHint = $this->type->toPhpType();

src/Generator/ClientGenerator.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ protected function generateProperties(): array
120120
}
121121

122122
return [
123-
$this->builder->localProperty('client', 'ClientInterface', 'ClientInterface'),
124-
$this->builder->localProperty('container', 'ContainerInterface', 'ContainerInterface'),
123+
$this->builder->localProperty('client', 'ClientInterface', 'ClientInterface', readonly: true),
124+
$this->builder->localProperty('container', 'ContainerInterface', 'ContainerInterface', readonly: true),
125125
];
126126
}
127127

@@ -145,6 +145,11 @@ protected function generateConstructor(): ClassMethod
145145
$parameter->makePrivate();
146146
}
147147
}
148+
if ($this->phpVersion->isReadonlyPropertySupported()) {
149+
foreach ($parameters as $parameter) {
150+
$parameter->makeReadonly();
151+
}
152+
}
148153

149154
$parameters = array_map(
150155
static fn (ParameterBuilder $parameter): ParameterNode => $parameter->getNode(),

0 commit comments

Comments
 (0)