Skip to content

Commit

Permalink
feature #163 Get the autoloader class from current registered autoloa…
Browse files Browse the repository at this point in the history
…d functions (silvester)

This PR was squashed before being merged into the 1.0-dev branch (closes #163).

Discussion
----------

Get the autoloader class from current registered autoload functions

If the default folder for vendor is changed the AutloaderUtil class is no more able to find the ClassLoader. Testing the failure can be easily done by changing the **vendor-dir** in composer.json. After adjusting the autoload require in console file, all makers that use AutoloadUtil will fail.

Motivation behind this is development with docker. The application is a lot faster on windows and mac if the vendor folder is not a shared folder.

Commits
-------

bc04e9a Get the autoloader class from current registered autoload functions
  • Loading branch information
weaverryan committed May 2, 2018
2 parents f9df943 + bc04e9a commit 0de5134
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 40 deletions.
4 changes: 1 addition & 3 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
<argument>%kernel.project_dir%</argument>
</service>

<service id="maker.autoloader_util" class="Symfony\Bundle\MakerBundle\Util\AutoloaderUtil">
<argument>%kernel.project_dir%</argument>
</service>
<service id="maker.autoloader_util" class="Symfony\Bundle\MakerBundle\Util\AutoloaderUtil" />

<service id="maker.event_registry" class="Symfony\Bundle\MakerBundle\EventRegistry">
<argument type="service" id="event_dispatcher" />
Expand Down
39 changes: 24 additions & 15 deletions src/Util/AutoloaderUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Bundle\MakerBundle\Util;

use Composer\Autoload\ClassLoader;
use Symfony\Component\Debug\DebugClassLoader;

/**
* @author Ryan Weaver <[email protected]>
Expand All @@ -20,13 +21,10 @@
*/
class AutoloaderUtil
{
private static $classLoader;
private $rootDir;

public function __construct(string $rootDir)
{
$this->rootDir = $rootDir;
}
/**
* @var ClassLoader
*/
private $classLoader;

/**
* Returns the relative path to where a new class should live.
Expand Down Expand Up @@ -76,16 +74,27 @@ public function getNamespacePrefixForClass(string $className): string

private function getClassLoader(): ClassLoader
{
if (null === self::$classLoader) {
$autoloadPath = $this->rootDir.'/vendor/autoload.php';

if (!file_exists($autoloadPath)) {
throw new \Exception(sprintf('Could not find the autoload file: "%s"', $autoloadPath));
if (null === $this->classLoader) {
$autoloadFunctions = spl_autoload_functions();
foreach ($autoloadFunctions as $autoloader) {
if (is_array($autoloader) && isset($autoloader[0]) && is_object($autoloader[0])) {
if ($autoloader[0] instanceof ClassLoader) {
$this->classLoader = $autoloader[0];
break;
}
if ($autoloader[0] instanceof DebugClassLoader
&& is_array($autoloader[0]->getClassLoader())
&& $autoloader[0]->getClassLoader()[0] instanceof ClassLoader) {
$this->classLoader = $autoloader[0]->getClassLoader()[0];
break;
}
}
}
if (null === $this->classLoader) {
throw new \Exception('Composer ClassLoader not found!');
}

self::$classLoader = require $autoloadPath;
}

return self::$classLoader;
return $this->classLoader;
}
}
54 changes: 32 additions & 22 deletions tests/Util/AutoloaderUtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Symfony\Bundle\MakerBundle\Tests\Util;

use Composer\Autoload\ClassLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\MakerBundle\Util\AutoloaderUtil;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;

class AutoloaderUtilTest extends TestCase
{
Expand All @@ -28,49 +28,59 @@ public static function setupPaths()

public function testGetPathForFutureClass()
{
$classLoader = new ClassLoader();
$composerJson = [
'autoload' => [
'psr-4' => [
'Also\\In\\Src\\' => 'src/SubDir',
'App\\' => 'src/',
'Other\\Namespace\\' => 'lib',
'' => 'fallback_dir',
'Also\\In\\Src\\' => '/src/SubDir',
'App\\' => '/src',
'Other\\Namespace\\' => '/lib',
'' => '/fallback_dir',
],
'psr-0' => [
'Psr0\\Package' => 'lib/other',
'Psr0\\Package' => '/lib/other',
],
],
];

$fs = new Filesystem();
if (!file_exists(self::$currentRootDir)) {
$fs->mkdir(self::$currentRootDir);
foreach ($composerJson['autoload'] as $psr => $dirs) {
foreach ($dirs as $prefix => $path) {
if ($psr == 'psr-4') {
$classLoader->addPsr4($prefix, self::$currentRootDir.$path);
} else {
$classLoader->add($prefix, self::$currentRootDir.$path);
}
}
}

$fs->remove(self::$currentRootDir.'/vendor');
file_put_contents(
self::$currentRootDir.'/composer.json',
json_encode($composerJson, JSON_PRETTY_PRINT)
);
$process = new Process('composer dump-autoload', self::$currentRootDir);
$process->run();
if (!$process->isSuccessful()) {
throw new \Exception('Error running composer dump-autoload: '.$process->getErrorOutput());
}
$reflection = new \ReflectionClass(AutoloaderUtil::class);
$property = $reflection->getProperty('classLoader');
$property->setAccessible(true);

$autoloaderUtil = new AutoloaderUtil();

$property->setValue($autoloaderUtil, $classLoader);

$autoloaderUtil = new AutoloaderUtil(self::$currentRootDir);
foreach ($this->getPathForFutureClassTests() as $className => $expectedPath) {
$this->assertSame(
// the paths will start in vendor/composer and be relative
str_replace('\\', '/', self::$currentRootDir.'/vendor/composer/../../'.$expectedPath),
str_replace('\\', '/', self::$currentRootDir.'/'.$expectedPath),
// normalize slashes for Windows comparison
str_replace('\\', '/', $autoloaderUtil->getPathForFutureClass($className)),
sprintf('class "%s" should have been in path "%s"', $className, $expectedPath)
);
}
}

public function testCanFindClassLoader()
{
$reflection = new \ReflectionClass(AutoloaderUtil::class);
$method = $reflection->getMethod('getClassLoader');
$method->setAccessible(true);
$autoloaderUtil = new AutoloaderUtil();
$autoloader = $method->invoke($autoloaderUtil);
$this->assertInstanceOf(ClassLoader::class, $autoloader, 'Wrong ClassLoader found');
}

public function getPathForFutureClassTests()
{
return [
Expand Down

0 comments on commit 0de5134

Please sign in to comment.