Skip to content

Commit

Permalink
fix: allow usage of imported class names in custom transformers (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil authored Jan 12, 2024
1 parent 7f532d2 commit b6a407d
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [GH#10](https://github.com/jolicode/automapper/pull/10) Introduce custom transformers
- [GH#26](https://github.com/jolicode/automapper/pull/26) Fix mappings involving DateTimeInterface type

### Fixed
- [GH#33](https://github.com/jolicode/automapper/pull/33) Allow usage of imported class names in custom transformers

### Changed
- [GH#27](https://github.com/jolicode/automapper/pull/27) Use PhpStanExtractor instead of PhpDocExtractor

Expand Down
29 changes: 25 additions & 4 deletions src/Extractor/ClassMethodToCallbackExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
use AutoMapper\Exception\InvalidArgumentException;
use AutoMapper\Exception\LogicException;
use AutoMapper\Exception\RuntimeException;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser;
use PhpParser\ParserFactory;

Expand Down Expand Up @@ -48,10 +51,13 @@ public function extract(string $class, string $method, array $inputParameters):
}

$statements = $this->parser->parse($fileContents);

if (null === $statements) {
throw new RuntimeException("Couldn't parse file \"{$fileName}\" for class \"{$class}\".");
}

$statements = $this->resolveFullyQualifiedClassNames($statements);

$namespaceStatement = self::findUnique(Stmt\Namespace_::class, $statements, $fileName);
/** @var Stmt\Class_ $classStatement */
$classStatement = self::findUnique(Stmt\Class_::class, $namespaceStatement->stmts, $fileName);
Expand Down Expand Up @@ -80,18 +86,18 @@ public function extract(string $class, string $method, array $inputParameters):
}

/**
* @template T of Stmt
* @template T of Node
*
* @param class-string<T> $searchedStatementClass
* @param Stmt[] $statements
* @param Node[] $statements
*
* @return T
*/
private static function findUnique(string $searchedStatementClass, array $statements, string $fileName): Stmt
private static function findUnique(string $searchedStatementClass, array $statements, string $fileName): Node
{
$foundStatements = array_filter(
$statements,
static fn (Stmt $statement): bool => $statement instanceof $searchedStatementClass,
static fn (Node $statement): bool => $statement instanceof $searchedStatementClass,
);

if (\count($foundStatements) > 1) {
Expand All @@ -100,4 +106,19 @@ private static function findUnique(string $searchedStatementClass, array $statem

return array_values($foundStatements)[0] ?? throw new InvalidArgumentException("No \"{$searchedStatementClass}\" found in file \"{$fileName}\".");
}

/**
* Transform all statements with imported class names, into FQCNs.
*
* @param Node[] $statements
*
* @return Node[]
*/
private function resolveFullyQualifiedClassNames(array $statements): array
{
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new NameResolver());

return $nodeTraverser->traverse($statements);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/**
* @author Baptiste Leduc <[email protected]>
*/
class AstExtractorTest extends TestCase
class ClassMethodToCallbackExtractorTest extends TestCase
{
/**
* @dataProvider extractSimpleMethodProvider
Expand All @@ -30,7 +30,7 @@ public function testExtractSimpleMethod(string $varName): void

$this->assertEquals(<<<PHP
(function (mixed \$object) : mixed {
if (\$object instanceof Foo) {
if (\$object instanceof \AutoMapper\Tests\Extractor\Fixtures\Foo) {
\$object->bar = 'Hello World!';
}
return \$object;
Expand All @@ -53,7 +53,7 @@ public function testExtractMethodWithTwoVariables(): void

$this->assertEquals(<<<PHP
(function (mixed \$object, string \$someString) : mixed {
if (\$object instanceof Foo) {
if (\$object instanceof \AutoMapper\Tests\Extractor\Fixtures\Foo) {
\$object->bar = 'Hello World!';
\$object->baz = \$someString;
}
Expand Down Expand Up @@ -98,7 +98,7 @@ public function testInvalidExtractedMethodParameters(): void
private function assertGeneratedCodeIsRunnable(string $generatedCode, string $varName): void
{
$codeToEval = <<<PHP
if(!class_exists(Foo::class))
if(!class_exists(\AutoMapper\Tests\Extractor\Fixtures\Foo::class))
{
class Foo
{
Expand All @@ -107,7 +107,7 @@ class Foo
}
}
\${$varName} = new Foo();
\${$varName} = new \AutoMapper\Tests\Extractor\Fixtures\Foo();
\${$varName}->bar = 'Hello';
{$generatedCode}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace AutoMapper\Tests\Fixtures\Transformer\CustomTransformer;

use AutoMapper\Tests\Fixtures\AddressDTO;
use AutoMapper\Transformer\CustomTransformer\CustomModelTransformerInterface;
use Symfony\Component\PropertyInfo\Type;

Expand All @@ -19,7 +20,7 @@ public function supports(array $sourceTypes, array $targetTypes): bool
*/
public function transform(object|array $source): mixed
{
$addressDTO = new \AutoMapper\Tests\Fixtures\AddressDTO();
$addressDTO = new AddressDTO();
$addressDTO->city = "{$source['city']} from custom model transformer";

return $addressDTO;
Expand All @@ -45,7 +46,7 @@ private function sourceIsArray(array $sourceTypes): bool
private function targetIsAddressDTO(array $targetTypes): bool
{
foreach ($targetTypes as $targetType) {
if ($targetType->getClassName() === \AutoMapper\Tests\Fixtures\AddressDTO::class) {
if ($targetType->getClassName() === AddressDTO::class) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace AutoMapper\Tests\Fixtures\Transformer\CustomTransformer;

use AutoMapper\Tests\Fixtures\Address;
use AutoMapper\Tests\Fixtures\AddressDTO;
use AutoMapper\Transformer\CustomTransformer\CustomModelTransformerInterface;
use Symfony\Component\PropertyInfo\Type;
Expand All @@ -22,7 +23,7 @@ public function transform(object|array $source): mixed
{
$source->city = "{$source->city} from custom model transformer";

return \AutoMapper\Tests\Fixtures\Address::fromDTO($source);
return Address::fromDTO($source);
}

/**
Expand All @@ -45,7 +46,7 @@ private function sourceIsAddressDTO(array $sourceTypes): bool
private function targetIsAddress(array $targetTypes): bool
{
foreach ($targetTypes as $targetType) {
if ($targetType->getClassName() === \AutoMapper\Tests\Fixtures\Address::class) {
if ($targetType->getClassName() === Address::class) {
return true;
}
}
Expand Down

0 comments on commit b6a407d

Please sign in to comment.