Skip to content

Commit

Permalink
fix: Doctrine MongoDB ODM proxy class resolving (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi authored Sep 17, 2024
1 parent 396f9e0 commit 7a0d971
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 1.6.1

* fix: remove phpstan config remnant
* fix: Doctrine MongoDB ODM proxy class resolving (issue [#106](https://github.com/rekalogika/mapper/issues/106))

## 1.6.0

Expand Down
60 changes: 43 additions & 17 deletions src/Util/ClassUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,47 +24,73 @@
private function __construct() {}

/**
* @template T of object
* @param class-string<T> $class
* @return class-string<T>
* @copyright Kévin Dunglas
* @see https://github.com/api-platform/core/blob/main/src/Metadata/Util/ClassInfoTrait.php
*/
public static function determineRealClassFromPossibleProxy(string $class): string
public static function getRealClassName(string $className): string
{
$inputClass = $class;
// __CG__: Doctrine Common Marker for Proxy (ODM < 2.0 and ORM < 3.0)
// __PM__: Ocramius Proxy Manager (ODM >= 2.0)
$positionCg = strrpos($className, '\\__CG__\\');
$positionPm = strrpos($className, '\\__PM__\\');

$pos = strrpos($class, '\\__CG__\\');
if (false === $positionCg && false === $positionPm) {
return $className;
}

if ($pos === false) {
$pos = strrpos($class, '\\__PM__\\');
if (false !== $positionCg) {
return substr($className, $positionCg + 8);
}

if ($pos !== false) {
$class = substr($class, $pos + 8);
$className = ltrim($className, '\\');
$pos = strrpos($className, '\\');

if ($pos === false) {
throw new UnexpectedValueException(\sprintf(
'Unable to determine the real class name from the proxy class "%s"',
$className,
));
}

if (!class_exists($class)) {
return substr(
$className,
8 + $positionPm,
$pos - ($positionPm + 8),
);
}

/**
* @template T of object
* @param class-string<T> $class
* @return class-string<T>
*/
public static function determineRealClassFromPossibleProxy(string $class): string
{
$realClass = self::getRealClassName($class);

if (!class_exists($realClass)) {
throw new UnexpectedValueException(\sprintf(
'Trying to resolve the real class from possible proxy class "%s", got "%s", but the class does not exist',
$inputClass,
$class,
$realClass,
));
}

/** @psalm-suppress DocblockTypeContradiction */
if (!is_a($inputClass, $class, true)) {
if (!is_a($class, $realClass, true)) {
/** @psalm-suppress NoValue */
throw new UnexpectedValueException(\sprintf(
'Trying to resolve the real class from possible proxy class "%s", got "%s", but the proxy "%s" is not a subclass of "%s"',
$inputClass,
$class,
$inputClass,
$realClass,
$class,
$realClass,
));
}

/** @var class-string<T> $class */
/** @var class-string<T> $realClass */

return $class;
return $realClass;
}

/**
Expand Down
52 changes: 52 additions & 0 deletions tests/UnitTest/ClassUtilTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/*
* This file is part of rekalogika/mapper package.
*
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

namespace Rekalogika\Mapper\Tests\UnitTest;

use PHPUnit\Framework\TestCase;
use Rekalogika\Mapper\Util\ClassUtil;

class ClassUtilTest extends TestCase
{
/**
* @dataProvider provideProxyClassToRealClass
*/
public function testProxyClassToRealClass(string $proxyClass, string $realClass): void
{
$this->assertSame(
$realClass,
ClassUtil::getRealClassName($proxyClass),
);
}

/**
* @return iterable<array-key,array{string,string}>
*/
public static function provideProxyClassToRealClass(): iterable
{
yield 'DoctrineORM' => [
'Proxies\__CG__\Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity',
'Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity',
];

yield 'MongoDB' => [
'MongoDBODMProxies\__PM__\Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity\Generated93deedc1e7b56ba9c8d5a337a376eda9',
'Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity',
];

yield 'NoProxy' => [
'Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity',
'Rekalogika\Mapper\Tests\Fixtures\Doctrine\SimpleEntity',
];
}
}

0 comments on commit 7a0d971

Please sign in to comment.