Skip to content

Commit

Permalink
Avoid checking circular reference if there is no possibilities of it
Browse files Browse the repository at this point in the history
  • Loading branch information
joelwurtz committed Jan 29, 2019
1 parent 534636f commit 3753afc
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/AutoMapper/.gitignore
Expand Up @@ -4,3 +4,4 @@ composer.lock
Tests/generated/*
!Tests/generated/.gitkeep
Tests/fixtures/*/generated
Bundle/Tests/Resources/var
2 changes: 1 addition & 1 deletion src/AutoMapper/AbstractAutoMapper.php
Expand Up @@ -93,7 +93,7 @@ public function getConfiguration(string $source, string $target): ?MapperConfigu
return null;
}

$this->register($this->mapperConfigurationFactory->create($source, $target));
$this->register($this->mapperConfigurationFactory->create($this, $source, $target));
}

return $this->configurations[$source][$target];
Expand Down
2 changes: 2 additions & 0 deletions src/AutoMapper/AutoMapperRegisterInterface.php
Expand Up @@ -5,4 +5,6 @@
interface AutoMapperRegisterInterface
{
public function register(MapperConfigurationInterface $configuration): void;

public function getConfiguration(string $source, string $target): ?MapperConfigurationInterface;
}
Expand Up @@ -64,6 +64,7 @@ private function createMapperConfigurationDefinition(ContainerBuilder $container
$serviceName = 'Mapping_' . $config['source'] . '_' . $config['target'];
$definition = $container->register($serviceName, MapperConfiguration::class);
$definition->setFactory([new Reference(MapperConfigurationFactory::class), 'create']);
$definition->addArgument(new Reference(AutoMapper::class));
$definition->addArgument($config['source']);
$definition->addArgument($config['target']);
$definition->addTag('jane_auto_mapper.mapper_configuration');
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Bundle/Resources/config/services.xml
Expand Up @@ -34,7 +34,7 @@
<argument type="service" id="Jane\AutoMapper\Compiler\SourceTargetPropertiesMappingExtractor" />
<argument type="service" id="Jane\AutoMapper\Compiler\FromSourcePropertiesMappingExtractor" />
<argument type="service" id="Jane\AutoMapper\Compiler\FromTargetPropertiesMappingExtractor" />
<argument type="service" id="serializer.mapping.class_metadata_factory" />
<argument>Symfony_Mapper_</argument>
</service>

<service id="Jane\AutoMapper\Compiler\FileLoader">
Expand Down
5 changes: 5 additions & 0 deletions src/AutoMapper/Bundle/Tests/Resources/app/AppKernel.php
Expand Up @@ -49,6 +49,11 @@ public function indexAction()
{
return new Response();
}

public function getProjectDir()
{
return __DIR__ . '/..';
}
}

class UserConfigurationPass implements ConfigurationPassInterface
Expand Down
5 changes: 3 additions & 2 deletions src/AutoMapper/Compiler/Compiler.php
Expand Up @@ -40,14 +40,15 @@ public function compile(MapperConfigurationInterface $mapperConfiguration)
$constructStatements = [];
$injectMapperStatements = [];
$addedDependencies = [];
$canHaveCircularDependency = $mapperConfiguration->canHaveCircularDependency() && $mapperConfiguration->getSource() !== 'array';

$statements = [
new Stmt\If_(new Expr\BinaryOp\Identical(new Expr\ConstFetch(new Name('null')), $sourceInput), [
'stmts' => [new Stmt\Return_($sourceInput)],
]),
];

if ($mapperConfiguration->getSource() !== 'array') {
if ($canHaveCircularDependency) {
$statements[] = new Stmt\Expression(new Expr\Assign($hashVariable, new Expr\BinaryOp\Concat(new Expr\FuncCall(new Name('spl_object_hash'), [
new Arg($sourceInput),
]),
Expand Down Expand Up @@ -96,7 +97,7 @@ public function compile(MapperConfigurationInterface $mapperConfiguration)
}

if (\count($addedDependencies) > 0) {
if ($mapperConfiguration->getSource() !== 'array') {
if ($canHaveCircularDependency) {
$statements[] = new Stmt\Expression(new Expr\Assign(
$contextVariable,
new Expr\MethodCall($contextVariable, 'withReference', [
Expand Down
37 changes: 36 additions & 1 deletion src/AutoMapper/MapperConfiguration.php
Expand Up @@ -13,9 +13,12 @@ class MapperConfiguration extends AbstractMapperConfiguration

private $customMapping = [];

public function __construct(PropertiesMappingExtractorInterface $mappingExtractor, string $source, string $target, string $classPrefix = 'Mapper_')
private $autoMapperRegister;

public function __construct(AutoMapperRegisterInterface $autoMapperRegister, PropertiesMappingExtractorInterface $mappingExtractor, string $source, string $target, string $classPrefix = 'Mapper_')
{
$this->mappingExtractor = $mappingExtractor;
$this->autoMapperRegister = $autoMapperRegister;

parent::__construct($source, $target, $classPrefix);
}
Expand Down Expand Up @@ -103,4 +106,36 @@ public function isTargetCloneable(): bool

return $reflection->isCloneable() && !$reflection->hasMethod('__clone');
}

public function canHaveCircularDependency(): bool
{
$checked = [];

return $this->checkCircularMapperConfiguration($this, $checked);
}

protected function checkCircularMapperConfiguration(MapperConfigurationInterface $configuration, &$checked)
{
foreach ($configuration->getPropertiesMapping() as $propertyMapping) {
foreach ($propertyMapping->getTransformer()->getDependencies() as $dependency) {
if (isset($checked[$dependency->getName()])) {
continue;
}

$checked[$dependency->getName()] = true;

if ($dependency->getSource() === $this->getSource() && $dependency->getTarget() === $this->getTarget()) {
return true;
}

$subConfiguration = $this->autoMapperRegister->getConfiguration($dependency->getSource(), $dependency->getTarget());

if (null !== $subConfiguration && true === $this->checkCircularMapperConfiguration($subConfiguration, $checked)) {
return true;
}
}
}

return false;
}
}
4 changes: 2 additions & 2 deletions src/AutoMapper/MapperConfigurationFactory.php
Expand Up @@ -25,7 +25,7 @@ public function __construct(
$this->classPrefix = $classPrefix;
}

public function create($source, $target): MapperConfigurationInterface
public function create(AutoMapperRegisterInterface $autoMapperRegister, $source, $target): MapperConfiguration
{
$extractor = $this->sourceTargetPropertiesMappingExtractor;

Expand All @@ -37,6 +37,6 @@ public function create($source, $target): MapperConfigurationInterface
$extractor = $this->fromSourcePropertiesMappingExtractor;
}

return new MapperConfiguration($extractor, $source, $target, $this->classPrefix);
return new MapperConfiguration($autoMapperRegister, $extractor, $source, $target, $this->classPrefix);
}
}
2 changes: 2 additions & 0 deletions src/AutoMapper/MapperConfigurationInterface.php
Expand Up @@ -28,4 +28,6 @@ public function isConstructorAllowed(): bool;
public function isTargetCloneable(): bool;

public function getDateTimeFormat(): string;

public function canHaveCircularDependency(): bool;
}

0 comments on commit 3753afc

Please sign in to comment.