From 9b4d60897dfc7e9b165712428539e694ec596c80 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 14 Mar 2012 20:02:49 +0100 Subject: [PATCH] [DDC-1698] Add autoloader especially for the non PSR-0 Proxy class names. This is necessary when you want to deserialize your proxy classes from the session. --- lib/Doctrine/ORM/Proxy/Autoloader.php | 78 +++++++++++++++++++ lib/Doctrine/ORM/Proxy/ProxyException.php | 10 ++- .../Tests/ORM/Proxy/AutoloaderTest.php | 62 +++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 lib/Doctrine/ORM/Proxy/Autoloader.php create mode 100644 tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php diff --git a/lib/Doctrine/ORM/Proxy/Autoloader.php b/lib/Doctrine/ORM/Proxy/Autoloader.php new file mode 100644 index 000000000..876b3f225 --- /dev/null +++ b/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +/** + * Special Autoloader for Proxy classes because them not being PSR-0 compatible. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolve proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name + * 2. Remove namespace seperators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * @return string + */ + static public function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw ProxyException::notProxyClass($className, $proxyNamespace); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1)); + return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php'; + } + + /** + * Register and return autoloader callback for the given proxy dir and + * namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param Closure $notFoundCallback Invoked when the proxy file is not found. + * @return Closure + */ + static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, "\\"); + $autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + $notFoundCallback($proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + spl_autoload_register($autoloader); + return $autoloader; + } +} + diff --git a/lib/Doctrine/ORM/Proxy/ProxyException.php b/lib/Doctrine/ORM/Proxy/ProxyException.php index 29462780d..c09937253 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyException.php +++ b/lib/Doctrine/ORM/Proxy/ProxyException.php @@ -44,4 +44,12 @@ class ProxyException extends \Doctrine\ORM\ORMException { return new self("You must configure a proxy namespace. See docs for details"); } -} \ No newline at end of file + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf( + "The class %s is not part of the proxy namespace %s", + $className, $proxyNamespace + )); + } + +} diff --git a/tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php b/tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php new file mode 100644 index 000000000..ff5bf0f5b --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Tests\ORM\Proxy; + +use Doctrine\Tests\OrmTestCase; +use Doctrine\ORM\Proxy\Autoloader; + +/** + * @group DDC-1698 + */ +class AutoloaderTest extends OrmTestCase +{ + static public function dataResolveFile() + { + return array( + array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp/__CG__RealClass.php'), + array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp/__CG__RealClass.php'), + array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp/__CG__OtherRealClass.php'), + ); + } + + /** + * @dataProvider dataResolveFile + */ + public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile) + { + $actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + $this->assertEquals($expectedProxyFile, $actualProxyFile); + } + + public function testAutoload() + { + if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) { + unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php"); + } + + $autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) { + file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true)); + unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php"); + } +} +