Skip to content

Commit c6ed47b

Browse files
committed
Use reflection (alt)
1 parent 00cf8cd commit c6ed47b

27 files changed

+716
-320
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ None
99
### New features
1010

1111
- The `InheritsAttributes` attribute can be used on classes that inherit their attributes from traits, properties, or methods, and were previously ignored by the collection process.
12+
- The plugin can generate a file that uses reflection to create attributes instead of embedding their arguments.
1213

1314
### Backward Incompatible Changes
1415

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ARG PHP_VERSION=8.0
2-
FROM php:${PHP_VERSION}-cli-bookworm
2+
FROM php:${PHP_VERSION}-cli-buster
33

44
RUN <<-EOF
55
docker-php-ext-enable opcache

README.md

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ to _discover_ attribute targets in a codebase—for known targets you can use re
1515
#### Features
1616

1717
- Little configuration
18-
- No reflection in the generated file
18+
- No reflection in the generated file (as an option)
1919
- No impact on performance
2020
- No dependency (except Composer of course)
2121
- A single interface to get attribute targets: classes, methods, and properties
@@ -73,28 +73,29 @@ var_dump($attributes->methodsAttributes);
7373
var_dump($attributes->propertyAttributes);
7474
```
7575

76+
> [!NOTE]
77+
> The plugin supports class, method, and property targets. [Contribute](CONTRIBUTING.md) if you're
78+
> interested in expending its support.
79+
7680

7781

7882
## Getting started
7983

8084

85+
8186
### Installation
8287

8388
```shell
8489
composer require olvlvl/composer-attribute-collector
8590
```
8691

87-
The plugin is currently experimental and its interface subject to change. At the moment, it only
88-
supports class, method, and property targets. Please [contribute](CONTRIBUTING.md) if you're interested in
89-
shaping its future.
90-
9192

9293

9394
### Sample configuration
9495

9596
The plugin only inspects paths and files specified in the configuration with the direction
96-
`include`. That's usually your "src" directory. Add this section to your `composer.json` file to
97-
enable the generation of the attributes file on autoload dump.
97+
`include`. That is usually your "src" directory. Add this section to your `composer.json` file to
98+
enable the generation of the "attributes" file on autoload dump.
9899

99100
Check the [Configuration options](#configuration) for more details.
100101

@@ -114,7 +115,7 @@ Check the [Configuration options](#configuration) for more details.
114115

115116
### Autoloading
116117

117-
You can require the attributes file using `require_once 'vendor/attributes.php';` but you might
118+
You can require the "attributes" file using `require_once 'vendor/attributes.php';` but you might
118119
prefer using Composer's autoloading feature:
119120

120121
```json
@@ -134,7 +135,7 @@ prefer using Composer's autoloading feature:
134135
### Including paths or files ([root-only][])
135136

136137
Use the `include` property to define the paths or files to inspect for attributes. Without this
137-
property, the attributes file will be empty.
138+
property, the "attributes" file will be empty.
138139

139140
The specified paths are relative to the `composer.json` file, and the `{vendor}` placeholder is
140141
replaced with the path to the vendor folder.
@@ -178,6 +179,35 @@ set the environment variable `COMPOSER_ATTRIBUTE_COLLECTOR_USE_CACHE` to `1`, `y
178179
Cache items are persisted in the `.composer-attribute-collector` directory, you might want to add
179180
it to your `.gitignore` file.
180181

182+
```shell
183+
COMPOSER_ATTRIBUTE_COLLECTOR_USE_CACHE=1 composer dump-autoload
184+
```
185+
186+
### Use reflection
187+
188+
> [!NOTE]
189+
> New in v2.1
190+
191+
By default, the "attributes" file embeds arguments to instantiate attributes without using
192+
reflection. This can cause issues when the arguments are sophisticated types that don't support var
193+
export. Alternatively, attributes can be instantiated using reflection.
194+
195+
The API remains the same whether reflections are used or not. You can safely switch between modes
196+
and see what works best for you.
197+
198+
Use the `use-reflection` property for the "attributes" file to use reflection.
199+
200+
```json
201+
{
202+
"extra": {
203+
"composer-attribute-collector": {
204+
"use-reflection": true
205+
}
206+
}
207+
}
208+
```
209+
210+
181211

182212

183213
## Test drive with the Symfony Demo
@@ -261,9 +291,9 @@ to Composer to find classes. Anything that works with Composer should work with
261291

262292
**Can I use the plugin during development?**
263293

264-
Yes, you can use the plugin during development, but keep in mind the attributes file is only
265-
generated after the autoloader is dumped. If you modify attributes you'll have to run
266-
`composer dump` to refresh the attributes file.
294+
Yes, you can use the plugin during development, but keep in mind the "attributes" file is only
295+
generated after the autoloader is dumped. If you modify attributes you will have to run
296+
`composer dump` to refresh the "attributes" file.
267297

268298
As a workaround you could have watchers on the directories that contain classes with attributes to
269299
run `XDEBUG_MODE=off composer dump` when you make changes. [PhpStorm offers file watchers][phpstorm-watchers].

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
},
3636
"require-dev": {
3737
"composer/composer": ">=2.4",
38-
"phpstan/phpstan": "^1.9",
38+
"phpstan/phpstan": "^2.0",
3939
"phpunit/phpunit": "^9.5"
4040
},
4141
"extra": {

phpcs.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
<exclude-pattern>tests/bootstrap.php</exclude-pattern>
1919
</rule>
2020
<rule ref="Generic.Files.LineLength.TooLong">
21+
<exclude-pattern>src/Static/TransientCollectionRenderer.php</exclude-pattern>
22+
<exclude-pattern>src/Reflexive/TransientCollectionRenderer.php</exclude-pattern>
2123
<exclude-pattern>tests/*</exclude-pattern>
2224
</rule>
2325
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
parameters:
33
bootstrapFiles:
44
- tests/bootstrap.php
5-
level: max
5+
level: 9
66
paths:
77
- src

src/Attributes.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
<?php
22

3-
/*
4-
* (c) Olivier Laviale <olivier.laviale@gmail.com>
5-
*
6-
* For the full copyright and license information, please view the LICENSE
7-
* file that was distributed with this source code.
8-
*/
9-
103
namespace olvlvl\ComposerAttributeCollector;
114

125
use Closure;

src/ClassAttributeCollector.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@
1313
*/
1414
class ClassAttributeCollector
1515
{
16+
/**
17+
* @param bool $ignoreArguments
18+
* Attribute arguments aren't used when generating a file that uses reflection.
19+
* Setting `$ignoreArguments` to `true` ignores arguments during the attribute collection.
20+
*/
1621
public function __construct(
1722
private IOInterface $io,
23+
private bool $ignoreArguments,
1824
) {
1925
}
2026

@@ -26,8 +32,6 @@ public function __construct(
2632
* array<TransientTargetMethod>,
2733
* array<TransientTargetProperty>,
2834
* }
29-
* Where `0` is an array of class attributes, `1` is an array of method attributes,
30-
* and `2` is an array of property attributes.
3135
*
3236
* @throws ReflectionException
3337
*/
@@ -51,7 +55,7 @@ public function collectAttributes(string $class): array
5155

5256
$classAttributes[] = new TransientTargetClass(
5357
$attribute->getName(),
54-
$attribute->getArguments(),
58+
$this->getArguments($attribute),
5559
);
5660
}
5761

@@ -64,13 +68,12 @@ public function collectAttributes(string $class): array
6468
}
6569

6670
$method = $methodReflection->name;
67-
assert($method !== '');
6871

6972
$this->io->debug("Found attribute {$attribute->getName()} on $class::$method");
7073

7174
$methodAttributes[] = new TransientTargetMethod(
7275
$attribute->getName(),
73-
$attribute->getArguments(),
76+
$this->getArguments($attribute),
7477
$method,
7578
);
7679
}
@@ -91,7 +94,7 @@ public function collectAttributes(string $class): array
9194

9295
$propertyAttributes[] = new TransientTargetProperty(
9396
$attribute->getName(),
94-
$attribute->getArguments(),
97+
$this->getArguments($attribute),
9598
$property,
9699
);
97100
}
@@ -128,4 +131,18 @@ private static function isAttributeIgnored(ReflectionAttribute $attribute): bool
128131

129132
return isset($ignored[$attribute->getName()]);
130133
}
134+
135+
/**
136+
* @param ReflectionAttribute<object> $attribute
137+
*
138+
* @return array<string, mixed>
139+
*/
140+
private function getArguments(ReflectionAttribute $attribute): array
141+
{
142+
if ($this->ignoreArguments) {
143+
return [];
144+
}
145+
146+
return $attribute->getArguments();
147+
}
131148
}

0 commit comments

Comments
 (0)