Skip to content

Commit b6a67a1

Browse files
committed
perf: various optimizations for Laravel/Symfony
1 parent 8981672 commit b6a67a1

File tree

5 files changed

+65
-36
lines changed

5 files changed

+65
-36
lines changed

src/Laravel/ApiPlatformProvider.php

+1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ public function register(): void
247247
);
248248
});
249249

250+
$this->app->singleton(ModelMetadata::class);
250251
$this->app->bind(LoaderInterface::class, AttributeLoader::class);
251252
$this->app->bind(ClassMetadataFactoryInterface::class, ClassMetadataFactory::class);
252253
$this->app->singleton(ClassMetadataFactory::class, function (Application $app) {

src/Laravel/Eloquent/Metadata/ModelMetadata.php

+21-28
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Illuminate\Database\Eloquent\Model;
1717
use Illuminate\Database\Eloquent\Relations\Relation;
1818
use Illuminate\Support\Collection;
19-
use Illuminate\Support\Facades\Gate;
2019
use Illuminate\Support\Str;
2120

2221
/**
@@ -26,6 +25,16 @@
2625
*/
2726
final class ModelMetadata
2827
{
28+
/**
29+
* @var array<class-string, Collection<string, mixed>>
30+
*/
31+
private $attributesLocalCache = [];
32+
33+
/**
34+
* @var array<class-string, Collection<int, mixed>>
35+
*/
36+
private $relationsLocalCache = [];
37+
2938
/**
3039
* The methods that can be called in a model to indicate a relation.
3140
*
@@ -45,31 +54,25 @@ final class ModelMetadata
4554
'morphedByMany',
4655
];
4756

48-
/**
49-
* Gets the first policy associated with this model.
50-
*/
51-
public function getPolicy(Model $model): ?string
52-
{
53-
$policy = Gate::getPolicyFor($model::class);
54-
55-
return $policy ? $policy::class : null;
56-
}
57-
5857
/**
5958
* Gets the column attributes for the given model.
6059
*
6160
* @return Collection<string, mixed>
6261
*/
6362
public function getAttributes(Model $model): Collection
6463
{
64+
if (isset($this->attributesLocalCache[$model::class])) {
65+
return $this->attributesLocalCache[$model::class];
66+
}
67+
6568
$connection = $model->getConnection();
6669
$schema = $connection->getSchemaBuilder();
6770
$table = $model->getTable();
6871
$columns = $schema->getColumns($table);
6972
$indexes = $schema->getIndexes($table);
7073
$relations = $this->getRelations($model);
7174

72-
return collect($columns)
75+
return $this->attributesLocalCache[$model::class] = collect($columns)
7376
->reject(
7477
fn ($column) => $relations->contains(
7578
fn ($relation) => $relation['foreign_key'] === $column['name']
@@ -112,7 +115,7 @@ private function isColumnPrimaryKey(array $indexes, string $column): bool
112115
*
113116
* @return Collection<int, mixed>
114117
*/
115-
public function getVirtualAttributes(Model $model, array $columns): Collection
118+
private function getVirtualAttributes(Model $model, array $columns): Collection
116119
{
117120
$class = new \ReflectionClass($model);
118121

@@ -155,7 +158,11 @@ public function getVirtualAttributes(Model $model, array $columns): Collection
155158
*/
156159
public function getRelations(Model $model): Collection
157160
{
158-
return collect(get_class_methods($model))
161+
if (isset($this->relationsLocalCache[$model::class])) {
162+
return $this->relationsLocalCache[$model::class];
163+
}
164+
165+
return $this->relationsLocalCache[$model::class] = collect(get_class_methods($model))
159166
->map(fn ($method) => new \ReflectionMethod($model, $method))
160167
->reject(
161168
fn (\ReflectionMethod $method) => $method->isStatic()
@@ -207,20 +214,6 @@ public function getRelations(Model $model): Collection
207214
->values();
208215
}
209216

210-
/**
211-
* Gets the Events that the model dispatches.
212-
*
213-
* @return Collection<int, mixed>
214-
*/
215-
public function getEvents(Model $model): Collection
216-
{
217-
return collect($model->dispatchesEvents())
218-
->map(fn (string $class, string $event) => [
219-
'event' => $event,
220-
'class' => $class,
221-
])->values();
222-
}
223-
224217
/**
225218
* Gets the cast type for the given column.
226219
*/

src/Metadata/Resource/Factory/ConcernsResourceNameCollectionFactory.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ public function create(): ResourceNameCollection
4747
}
4848

4949
foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($this->paths) as $className => $reflectionClass) {
50+
if (!$reflectionClass->hasMethod('apiResource')) {
51+
continue;
52+
}
53+
54+
$m = $reflectionClass->getMethod('apiResource');
55+
5056
if (
51-
$reflectionClass->hasMethod('apiResource')
52-
&& ($m = $reflectionClass->getMethod('apiResource'))
53-
&& $m->isPublic()
57+
$m->isPublic()
5458
&& $m->isStatic()
5559
) {
5660
$classes[$className] = true;

src/Metadata/Resource/Factory/LinkFactory.php

+17-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
*/
2727
final class LinkFactory implements LinkFactoryInterface, PropertyLinkFactoryInterface
2828
{
29+
/**
30+
* @var array<class-string, string[]>
31+
*/
32+
private $localIdentifiersPerResourceClassCache = [];
33+
2934
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
3035
{
3136
}
@@ -138,8 +143,17 @@ public function completeLink(Link $link): Link
138143
return $link;
139144
}
140145

146+
/**
147+
* @param class-string $resourceClass
148+
*
149+
* @return string[]
150+
*/
141151
private function getIdentifiersFromResourceClass(string $resourceClass): array
142152
{
153+
if (isset($this->localIdentifiersPerResourceClassCache[$resourceClass])) {
154+
return $this->localIdentifiersPerResourceClassCache[$resourceClass];
155+
}
156+
143157
$hasIdProperty = false;
144158
$identifiers = [];
145159
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
@@ -155,14 +169,14 @@ private function getIdentifiersFromResourceClass(string $resourceClass): array
155169
}
156170

157171
if ($hasIdProperty && !$identifiers) {
158-
return ['id'];
172+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['id'];
159173
}
160174

161175
if (!$hasIdProperty && !$identifiers && enum_exists($resourceClass)) {
162-
return ['value'];
176+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['value'];
163177
}
164178

165-
return $identifiers;
179+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = $identifiers;
166180
}
167181

168182
/**

src/Metadata/Util/ReflectionClassRecursiveIterator.php

+19-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,26 @@
2222
*/
2323
final class ReflectionClassRecursiveIterator
2424
{
25+
/**
26+
* @var array<string, array<class-string, \ReflectionClass>>
27+
*/
28+
private static array $localCache;
29+
2530
private function __construct()
2631
{
2732
}
2833

29-
public static function getReflectionClassesFromDirectories(array $directories): \Iterator
34+
/**
35+
* @return array<class-string, \ReflectionClass>
36+
*/
37+
public static function getReflectionClassesFromDirectories(array $directories): array
3038
{
39+
$id = hash('xxh3', implode('', $directories));
40+
if (isset(self::$localCache[$id])) {
41+
return self::$localCache[$id];
42+
}
43+
44+
$includedFiles = [];
3145
foreach ($directories as $path) {
3246
$iterator = new \RegexIterator(
3347
new \RecursiveIteratorIterator(
@@ -61,12 +75,15 @@ public static function getReflectionClassesFromDirectories(array $directories):
6175
$sortedInterfaces = get_declared_interfaces();
6276
sort($sortedInterfaces);
6377
$declared = [...$sortedClasses, ...$sortedInterfaces];
78+
$ret = [];
6479
foreach ($declared as $className) {
6580
$reflectionClass = new \ReflectionClass($className);
6681
$sourceFile = $reflectionClass->getFileName();
6782
if (isset($includedFiles[$sourceFile])) {
68-
yield $className => $reflectionClass;
83+
$ret[$className] = $reflectionClass;
6984
}
7085
}
86+
87+
return self::$localCache[$id] = $ret;
7188
}
7289
}

0 commit comments

Comments
 (0)