Skip to content

Commit

Permalink
Remove dependency on doctrine/common
Browse files Browse the repository at this point in the history
We were only relying on it for functionality related to proxies, which
is very little code.
  • Loading branch information
greg0ire committed Oct 15, 2023
1 parent 3c34265 commit b1d7fcf
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 5 deletions.
8 changes: 4 additions & 4 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Upgrade to 3.0

## BC BREAK: `Doctrine\ORM\Proxy\Autoloader` no longer extends `Doctrine\Common\Proxy\Autoloader`

Make sure to use the former when writing a type declaration or an `instanceof` check.

## Minor BC BREAK: Changed order of arguments passed to `OneToOne`, `ManyToOne` and `Index` mapping PHP attributes

To keep PHP mapping attributes consistent, order of arguments passed to above attributes has been changed
Expand Down Expand Up @@ -328,10 +332,6 @@ Use `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver` and
The second argument to `UnderscoreNamingStrategy::__construct()` was dropped,
the strategy can no longer be unaware of numbers.

## BC BREAK: Remove `Doctrine\ORM\Proxy\Autoloader`

Use `Doctrine\Common\Proxy\Autoloader` instead.

## BC BREAK: Remove `Doctrine\ORM\Tools\DisconnectedClassMetadataFactory`

No replacement is provided.
Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"composer-runtime-api": "^2",
"ext-ctype": "*",
"doctrine/collections": "^2.1",
"doctrine/common": "^3.3",
"doctrine/dbal": "^3.6 || ^4",
"doctrine/deprecations": "^0.5.3 || ^1",
"doctrine/event-manager": "^1.2 || ^2",
Expand Down
86 changes: 86 additions & 0 deletions lib/Doctrine/ORM/Proxy/Autoloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Proxy;

use Closure;

use function file_exists;
use function ltrim;
use function spl_autoload_register;
use function str_replace;
use function strlen;
use function strpos;
use function substr;

use const DIRECTORY_SEPARATOR;

/**
* Special Autoloader for Proxy classes, which are not PSR-0 compliant.
*/
final class Autoloader
{
/**
* Resolves proxy class name to a filename based on the following pattern.
*
* 1. Remove Proxy namespace from class name.
* 2. Remove namespace separators from remaining class name.
* 3. Return PHP filename from proxy-dir with the result from 2.
*
* @psalm-param class-string $className
*
* @throws NotAProxyClass
*/
public static function resolveFile(string $proxyDir, string $proxyNamespace, string $className): string
{
if (strpos($className, $proxyNamespace) !== 0) {
throw new NotAProxyClass($className, $proxyNamespace);

Check warning on line 38 in lib/Doctrine/ORM/Proxy/Autoloader.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Proxy/Autoloader.php#L38

Added line #L38 was not covered by tests
}

// remove proxy namespace from class name
$classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace));

// remove namespace separators from remaining class name
$fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace);

return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php';
}

/**
* Registers and returns autoloader callback for the given proxy dir and namespace.
*
* @param Closure(string, string, class-string): void|null $notFoundCallback Invoked when the proxy file is not found.
*
* @return Closure(string): void
*/
public static function register(
string $proxyDir,
string $proxyNamespace,
Closure|null $notFoundCallback = null,
): Closure {
$proxyNamespace = ltrim($proxyNamespace, '\\');

$autoloader = /** @param class-string $className */ static function (string $className) use ($proxyDir, $proxyNamespace, $notFoundCallback): void {
if ($proxyNamespace === '') {
return;

Check warning on line 66 in lib/Doctrine/ORM/Proxy/Autoloader.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Proxy/Autoloader.php#L66

Added line #L66 was not covered by tests
}

if (strpos($className, $proxyNamespace) !== 0) {
return;

Check warning on line 70 in lib/Doctrine/ORM/Proxy/Autoloader.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Proxy/Autoloader.php#L70

Added line #L70 was not covered by tests
}

$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);

if ($notFoundCallback && ! file_exists($file)) {
$notFoundCallback($proxyDir, $proxyNamespace, $className);
}

require $file;
};

spl_autoload_register($autoloader);

return $autoloader;
}
}
22 changes: 22 additions & 0 deletions lib/Doctrine/ORM/Proxy/NotAProxyClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Proxy;

use Doctrine\ORM\Exception\ORMException;
use InvalidArgumentException;

use function sprintf;

final class NotAProxyClass extends InvalidArgumentException implements ORMException
{
public function __construct(string $className, string $proxyNamespace)

Check warning on line 14 in lib/Doctrine/ORM/Proxy/NotAProxyClass.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Proxy/NotAProxyClass.php#L14

Added line #L14 was not covered by tests
{
parent::__construct(sprintf(
'The class "%s" is not part of the proxy namespace "%s"',
$className,
$proxyNamespace,
));

Check warning on line 20 in lib/Doctrine/ORM/Proxy/NotAProxyClass.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Proxy/NotAProxyClass.php#L16-L20

Added lines #L16 - L20 were not covered by tests
}
}
14 changes: 14 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,20 @@
<code>string</code>
</MoreSpecificReturnType>
</file>
<file src="lib/Doctrine/ORM/Proxy/Autoloader.php">
<ArgumentTypeCoercion>
<code>$autoloader</code>
</ArgumentTypeCoercion>
<LessSpecificReturnStatement>
<code>$autoloader</code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code>Closure(string): void</code>
</MoreSpecificReturnType>
<UnresolvableInclude>
<code>require $file</code>
</UnresolvableInclude>
</file>
<file src="lib/Doctrine/ORM/Proxy/ProxyFactory.php">
<ArgumentTypeCoercion>
<code>$classMetadata</code>
Expand Down
57 changes: 57 additions & 0 deletions tests/Doctrine/Tests/Proxy/AutoloaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Proxy;

use Doctrine\ORM\Proxy\Autoloader;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

use function class_exists;
use function file_exists;
use function file_put_contents;
use function sys_get_temp_dir;
use function unlink;

use const DIRECTORY_SEPARATOR;

class AutoloaderTest extends TestCase
{
/** @return iterable<string, array{string, string, class-string, string}> */
public static function dataResolveFile(): iterable
{
return [
['/tmp', 'MyProxy', 'MyProxy\RealClass', '/tmp' . DIRECTORY_SEPARATOR . 'RealClass.php'],
['/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'],
['/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'],
['/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__OtherRealClass.php'],
];
}

/** @param class-string $className */
#[DataProvider('dataResolveFile')]
public function testResolveFile(
string $proxyDir,
string $proxyNamespace,
string $className,
string $expectedProxyFile,
): void {
$actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
self::assertEquals($expectedProxyFile, $actualProxyFile);
}

public function testAutoload(): void
{
if (file_exists(sys_get_temp_dir() . '/AutoloaderTestClass.php')) {
unlink(sys_get_temp_dir() . '/AutoloaderTestClass.php');
}

$autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', static function ($proxyDir, $proxyNamespace, $className): void {
file_put_contents(sys_get_temp_dir() . '/AutoloaderTestClass.php', '<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ');
});

self::assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true));
unlink(sys_get_temp_dir() . '/AutoloaderTestClass.php');
}
}

0 comments on commit b1d7fcf

Please sign in to comment.