You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
+ say to "composer autoload" to load files from directory with generated classes first and then load classes from main directory.
21
+
8
22
## Quick start
9
23
10
24
Install library
11
25
```bash
12
26
composer require mrsuh/php-generics
13
27
```
14
28
15
-
Add directory for generated files(`"cache/"`) to autoload (directory must be placed before the main directory)
29
+
Add directory(`"cache/"`) to composer autoload PSR-4 for generated classes. It should be placed before the main directory.
16
30
composer.json
17
31
```json
18
32
{
@@ -24,9 +38,25 @@ composer.json
24
38
}
25
39
```
26
40
41
+
Generate concrete classes from generic classes with `composer dump-generics` command
42
+
```bash
43
+
composer dump-generics -vv
44
+
```
45
+
46
+
Generate vendor/autoload.php with `composer dump-autoload` command
47
+
```bash
48
+
composer dump-autoload
49
+
```
50
+
27
51
## Example
28
52
29
-
Add generic class
53
+
!! You can find repository with this example [here](https://github.com/mrsuh/php-generics-example).
54
+
55
+
For example, you need to add several PHP files:
56
+
+ generic class `Box`
57
+
+ class `Usage` for use generic class
58
+
+ script with composer autoload and `Usage` class
59
+
30
60
src/Box.php
31
61
```php
32
62
<?php
@@ -47,7 +77,6 @@ class Box<T> {
47
77
}
48
78
```
49
79
50
-
Add usage generic class
51
80
src/Usage.php
52
81
```php
53
82
<?php
@@ -69,7 +98,6 @@ class Usage {
69
98
}
70
99
```
71
100
72
-
Add test with Usage class and composer autoload
73
101
bin/test.php
74
102
```php
75
103
<?php
@@ -82,12 +110,21 @@ $usage = new Usage();
82
110
$usage->run();
83
111
```
84
112
85
-
Generate concrete classes from generics
113
+
Generate concrete classes from generic classes with `composer dump-generics` command
86
114
```bash
87
115
composer dump-generics -vv
88
116
```
89
117
90
-
Dump autoload classes
118
+
What the `composer dump-generics` command does?
119
+
+ finds all generic uses in classes (`src/Usage.php` for example).
120
+
+ generates concrete classes from generic classes with unique names based on name and arguments of generic class.
121
+
+ replaces generic class names to concrete class names in places of use.
122
+
123
+
In this case should be generated:
124
+
+ 2 concrete classes of generics `BoxForInt` and `BoxForString`;
125
+
+ 1 concrete class `Usage` with replaced generics class names to concrete class names.
126
+
127
+
Generate vendor/autoload.php with `composer dump-autoload` command
91
128
```bash
92
129
composer dump-autoload
93
130
```
@@ -97,22 +134,254 @@ Run bin/test.php script
97
134
php bin/test.php
98
135
```
99
136
100
-
See the [tests](./tests) folder for more examples.
137
+
Composer autoload first checks the "cache" directory and then the "src" directory to load the classes.
138
+
139
+
## Feature
140
+
141
+
#### What syntax is used?
142
+
143
+
The [RFC](https://github.com/PHPGenerics/php-generics-rfc) does not define a specific syntax so i took [this one](https://github.com/PHPGenerics/php-generics-rfc/issues/45) implemented by Nikita Popov
144
+
145
+
Syntax example:
146
+
```php
147
+
<?php
148
+
149
+
namespace App;
150
+
151
+
class Generic<inT:Iface = int,outV:Iface = string> {
152
+
153
+
public function test(T $var): V {
154
+
155
+
}
156
+
}
157
+
```
158
+
159
+
#### Syntax problems
160
+
161
+
I had to upgrade [nikic/php-parser](https://github.com/nikic/PHP-Parser) for parse code with new syntax.<br>
162
+
You can see [here](https://github.com/mrsuh/PHP-Parser/pull/1/files#diff-14ec37995c001c0c9808ab73668d64db5d1acc1ab0f60a360dcb9c611ecd57ea) the grammar changes that had to be made for support generics.
163
+
164
+
Parser use [PHP implementation](https://github.com/ircmaxell/PHP-Yacc) of [YACC](https://wikipedia.org/wiki/Yacc). <br>
165
+
The YACC([LALR](https://wikipedia.org/wiki/LALR(1))) algorithm and current PHP syntax make it impossible to describe the full syntax of generics due to collisions.
166
+
167
+
Collision example:
168
+
```php
169
+
<?php
170
+
171
+
const FOO = 'FOO';
172
+
const BAR = 'BAR';
173
+
174
+
var_dump(new \DateTime<FOO,BAR>('now')); // is it generic?
175
+
var_dump( (new \DateTime < FOO) , ( BAR > 'now') ); // no, it doesn't
Therefore, nested generics are not currently supported.
181
+
```php
182
+
<?php
183
+
184
+
namespace App;
185
+
186
+
class Usage {
187
+
public function run() {
188
+
$map = new Map<Key<int>, Value<string>>();//not supported
189
+
}
190
+
}
191
+
```
192
+
193
+
#### Parameter names have not special restrictions
194
+
195
+
```php
196
+
<?php
197
+
198
+
namespace App;
199
+
200
+
class GenericClass<T,varType,myCoolLongParaterName> {
201
+
private T $var1;
202
+
private varType $var2;
203
+
private myCoolLongParaterName $var3;
204
+
}
205
+
```
206
+
207
+
#### Several generic parameters support
208
+
209
+
```php
210
+
<?php
211
+
212
+
namespace App;
213
+
214
+
class Map<keyType,valueType> {
215
+
216
+
private array $map;
217
+
218
+
public function set(keyType $key, valueType $value): void {
219
+
$this->map[$key] = $value;
220
+
}
221
+
222
+
public function get(keyType $key): ?valueType {
223
+
return $this->map[$key] ?? null;
224
+
}
225
+
}
226
+
```
227
+
228
+
#### Default generic parameter support
229
+
230
+
```php
231
+
<?php
232
+
233
+
namespace App;
234
+
235
+
class Map<keyType = string,valueType = int> {
236
+
237
+
private array $map = [];
238
+
239
+
public function set(keyType $key, valueType $value): void {
240
+
$this->map[$key] = $value;
241
+
}
242
+
243
+
public function get(keyType $key): ?valueType {
244
+
return $this->map[$key] ?? null;
245
+
}
246
+
}
247
+
```
248
+
249
+
```php
250
+
<?php
251
+
252
+
namespace App;
253
+
254
+
class Usage {
255
+
public function run() {
256
+
$map = new Map<>();//be sure to add "<>"
257
+
$map->set('key', 1);
258
+
var_dump($map->get('key'));
259
+
}
260
+
}
261
+
```
262
+
263
+
#### Where in class can generics be used?
264
+
265
+
+ extends
266
+
+ implements
267
+
+ trait use
268
+
+ property type
269
+
+ method argument type
270
+
+ method return type
271
+
+ instanceof
272
+
+ new
273
+
+ class constants
274
+
275
+
An example of class that uses generics:
276
+
```php
277
+
<?php
278
+
279
+
namespace App;
280
+
281
+
use App\Entity\Cat;
282
+
use App\Entity\Bird;
283
+
use App\Entity\Dog;
284
+
285
+
class Test extends GenericClass<Cat> implements GenericInterface<Bird> {
286
+
287
+
use GenericTrait<Dog>;
288
+
289
+
private GenericClass<int>|GenericClass<Dog> $var;
290
+
291
+
public function test(GenericInterface<int>|GenericInterface<Dog> $var): GenericClass<string>|GenericClass<Bird> {
292
+
293
+
var_dump($var instanceof GenericInterface<int>);
294
+
295
+
var_dump(new GenericClass<int>::class);
296
+
297
+
var_dump(new GenericClass<array>::CONSTANT);
298
+
299
+
return new GenericClass<float>();
300
+
}
301
+
}
302
+
```
303
+
304
+
#### Where in generic class can parameters be used?
305
+
306
+
+ extends
307
+
+ implements
308
+
+ trait use
309
+
+ property type
310
+
+ method argument type
311
+
+ method return type
312
+
+ instanceof
313
+
+ new
314
+
+ class constants
315
+
316
+
And example of generic class:
317
+
```php
318
+
<?php
319
+
320
+
namespace App;
321
+
322
+
class Test<T,V> extends GenericClass<T> implements GenericInterface<V> {
323
+
324
+
use GenericTrait<T>;
325
+
use T;
326
+
327
+
private T|GenericClass<V> $var;
328
+
329
+
public function test(T|GenericInterface<V> $var): T|GenericClass<V> {
330
+
331
+
var_dump($var instanceof GenericInterface<V>);
332
+
333
+
var_dump($var instanceof T);
334
+
335
+
var_dump(new GenericClass<T>::class);
336
+
337
+
var_dump(T::class);
338
+
339
+
var_dump(new GenericClass<T>::CONSTANT);
340
+
341
+
var_dump(T::CONSTANT);
342
+
343
+
$obj1 = new T();
344
+
$obj2 = new GenericClass<V>();
345
+
346
+
return $obj2;
347
+
}
348
+
}
349
+
```
350
+
351
+
#### How fast is it?
352
+
353
+
All concrete classes are pre-generated and can be cached(should not affect performance).
354
+
355
+
Generating many concrete classes should negatively impact performance when:
356
+
+ resolves concrete classes;
357
+
+ storing concrete classes in memory;
358
+
+ type checking for each concrete class.
359
+
360
+
I think it's all individual for a specific case.
361
+
362
+
#### Doesn't work without composer autoload
363
+
364
+
Autoload magic of concrete classes works with composer autoload only. <br>
365
+
Nothing will work because of syntax error if you include file by "require"
366
+
367
+
#### Reflection
368
+
369
+
PHP does type checks in [runtime](https://github.com/PHPGenerics/php-generics-rfc/issues/43). <br>
370
+
Therefore, all generics arguments [must me available](https://github.com/PHPGenerics/php-generics-rfc/blob/cc7219792a5b35226129d09536789afe20eac029/generics.txt#L426-L430) through reflection in runtime. <br>
371
+
It can't be, because information about generics arguments is erased after concrete classes are generated.
0 commit comments