Skip to content

Commit 94377bf

Browse files
committed
Optimize creation from constructor
1 parent 76e25b5 commit 94377bf

File tree

2 files changed

+59
-60
lines changed

2 files changed

+59
-60
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- [GH#184](https://github.com/jolicode/automapper/pull/184) Fix error when mapping from stdClass to constructor with nullable/optional arguments
1414
- [GH#185](https://github.com/jolicode/automapper/pull/185) Fix constructor with default parameter array does not work with constructor_arguments context
1515

16+
### Changed
17+
- [GH#186](https://github.com/jolicode/automapper/pull/186) Optimize creation from constructor
18+
1619
## [9.1.2] - 2024-09-03
1720
### Fixed
1821
- [GH#174](https://github.com/jolicode/automapper/pull/174) Fix race condition when writing generated mappers

src/Generator/CreateTargetStatementsGenerator.php

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,19 @@ private function constructorArguments(GeneratorMetadata $metadata): array
126126
// Find property for parameter
127127
$propertyMetadata = $metadata->getTargetPropertyWithConstructor($constructorParameter->getName());
128128

129-
$createObjectStatement = null;
129+
$propertyStatements = null;
130130
$constructArgument = null;
131131
$constructorName = null;
132132

133133
if (null !== $propertyMetadata) {
134-
[$createObjectStatement, $constructArgument, $constructorName] = $this->constructorArgument($metadata, $propertyMetadata, $constructorParameter);
134+
[$propertyStatements, $constructArgument, $constructorName] = $this->constructorArgument($metadata, $propertyMetadata, $constructorParameter);
135135
}
136136

137-
if (null === $createObjectStatement || null === $constructArgument || null === $constructorName) {
138-
[$createObjectStatement, $constructArgument, $constructorName] = $this->constructorArgumentWithoutSource($metadata, $constructorParameter);
137+
if (null === $propertyStatements || null === $constructArgument || null === $constructorName) {
138+
[$propertyStatements, $constructArgument, $constructorName] = $this->constructorArgumentWithoutSource($metadata, $constructorParameter);
139139
}
140140

141-
$createObjectStatements[] = $createObjectStatement;
141+
$createObjectStatements = [...$createObjectStatements, ...$propertyStatements];
142142
$constructArguments[$constructorName] = $constructArgument;
143143
}
144144

@@ -158,17 +158,18 @@ private function constructorArguments(GeneratorMetadata $metadata): array
158158
}
159159

160160
/**
161-
* Check if there is a constructor argument in the context, otherwise we use the transformed value.
161+
* If source missing a constructor argument, check if there is a constructor argument in the context, otherwise we use the default value or throw exception.
162162
*
163163
* ```php
164-
* if (MapperContext::hasConstructorArgument($context, $target, 'propertyName')) {
165-
* $constructArg1 = $source->propertyName ?? MapperContext::getConstructorArgument($context, $target, 'propertyName');
166-
* } else {
167-
* $constructArg1 = $source->propertyName;
168-
* }
164+
* {transformation of value}
165+
* $constructarg = $value ?? (
166+
* MapperContext::hasConstructorArgument($context, $target, 'propertyName')
167+
* ? MapperContext::getConstructorArgument($context, $target, 'propertyName')
168+
* : {defaultValueExpr} // default value or throw exception
169+
* )
169170
* ```
170171
*
171-
* @return array{Stmt, Arg, string}|array{null, null, null}
172+
* @return array{Stmt[], Arg, string}|array{null, null, null}
172173
*/
173174
private function constructorArgument(GeneratorMetadata $metadata, PropertyMetadata $propertyMetadata, \ReflectionParameter $parameter): array
174175
{
@@ -218,45 +219,39 @@ private function constructorArgument(GeneratorMetadata $metadata, PropertyMetada
218219
}
219220

220221
return [
221-
new Stmt\If_(new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'hasConstructorArgument', [
222-
new Arg($variableRegistry->getContext()),
223-
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
224-
new Arg(new Scalar\String_($propertyMetadata->target->property)),
225-
]), [
226-
'stmts' => [
227-
...$propStatements,
228-
new Stmt\Expression($argumentAssignClosure(new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'getConstructorArgument', [
229-
new Arg($variableRegistry->getContext()),
230-
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
231-
new Arg(new Scalar\String_($propertyMetadata->target->property)),
232-
]))),
233-
],
234-
'else' => new Stmt\Else_([
235-
...$propStatements,
236-
new Stmt\Expression($argumentAssignClosure($defaultValueExpr)),
237-
]),
238-
]),
222+
[
223+
...$propStatements,
224+
new Stmt\Expression($argumentAssignClosure(
225+
new Expr\Ternary(
226+
new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'hasConstructorArgument', [
227+
new Arg($variableRegistry->getContext()),
228+
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
229+
new Arg(new Scalar\String_($propertyMetadata->target->property)),
230+
]),
231+
new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'getConstructorArgument', [
232+
new Arg($variableRegistry->getContext()),
233+
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
234+
new Arg(new Scalar\String_($propertyMetadata->target->property)),
235+
]),
236+
$defaultValueExpr,
237+
),
238+
)),
239+
],
239240
new Arg($constructVar, name: new Identifier($parameter->getName())),
240241
$parameter->getName(),
241242
];
242243
}
243244

244245
/**
245-
* Check if there is a constructor argument in the context, otherwise we use the default value.
246+
* Check if there is a constructor argument in the context, otherwise we use the default value or throw exception.
246247
*
247-
* ```
248-
* if (MapperContext::hasConstructorArgument($context, $target, 'propertyName')) {
249-
* $constructArg2 = MapperContext::getConstructorArgument($context, $target, 'propertyName');
250-
* } else {
251-
* $constructArg2 = 'default value';
252-
* // or set to null if the parameter is nullable
253-
* $constructArg2 = null;
254-
* // throw an exception otherwise
255-
* throw new MissingConstructorArgumentsException('Cannot create an instance of "Foo" from mapping data because its constructor requires the following parameters to be present : "$propertyName".', 0, null, ['propertyName'], 'Foo');
256-
* }
248+
* ```php
249+
* $constructarg = MapperContext::hasConstructorArgument($context, $target, 'propertyName')
250+
* ? MapperContext::getConstructorArgument($context, $target, 'propertyName')
251+
* : {defaultValueExpr} // default value or throw exception
257252
* ```
258253
*
259-
* @return array{Stmt, Arg, string}
254+
* @return array{Stmt[], Arg, string}
260255
*/
261256
private function constructorArgumentWithoutSource(GeneratorMetadata $metadata, \ReflectionParameter $constructorParameter): array
262257
{
@@ -274,28 +269,29 @@ private function constructorArgumentWithoutSource(GeneratorMetadata $metadata, \
274269
]));
275270

276271
if ($constructorParameter->isDefaultValueAvailable()) {
277-
$defaultValueExpr = new Expr\Assign($constructVar, $this->getValueAsExpr($constructorParameter->getDefaultValue()));
272+
$defaultValueExpr = $this->getValueAsExpr($constructorParameter->getDefaultValue());
278273
} elseif ($constructorParameter->allowsNull()) {
279-
$defaultValueExpr = new Expr\Assign($constructVar, new Expr\ConstFetch(new Name('null')));
274+
$defaultValueExpr = new Expr\ConstFetch(new Name('null'));
280275
}
281276

282277
return [
283-
new Stmt\If_(new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'hasConstructorArgument', [
284-
new Arg($variableRegistry->getContext()),
285-
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
286-
new Arg(new Scalar\String_($constructorParameter->getName())),
287-
]), [
288-
'stmts' => [
289-
new Stmt\Expression(new Expr\Assign($constructVar, new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'getConstructorArgument', [
290-
new Arg($variableRegistry->getContext()),
291-
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
292-
new Arg(new Scalar\String_($constructorParameter->getName())),
293-
]))),
294-
],
295-
'else' => new Stmt\Else_([
296-
new Stmt\Expression($defaultValueExpr),
297-
]),
298-
]),
278+
[
279+
new Stmt\Expression(new Expr\Assign($constructVar,
280+
new Expr\Ternary(
281+
new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'hasConstructorArgument', [
282+
new Arg($variableRegistry->getContext()),
283+
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
284+
new Arg(new Scalar\String_($constructorParameter->getName())),
285+
]),
286+
new Expr\StaticCall(new Name\FullyQualified(MapperContext::class), 'getConstructorArgument', [
287+
new Arg($variableRegistry->getContext()),
288+
new Arg(new Scalar\String_($metadata->mapperMetadata->target)),
289+
new Arg(new Scalar\String_($constructorParameter->getName())),
290+
]),
291+
$defaultValueExpr,
292+
))
293+
),
294+
],
299295
new Arg($constructVar, name: new Identifier($constructorParameter->getName())),
300296
$constructorParameter->getName(),
301297
];

0 commit comments

Comments
 (0)