Skip to content

Commit

Permalink
Leverage an autoloader file over bootstrap, remove compiler extension
Browse files Browse the repository at this point in the history
  • Loading branch information
mglaman committed Dec 10, 2019
1 parent a644e48 commit 20aa2a7
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 103 deletions.
16 changes: 16 additions & 0 deletions drupal-autoloader.php
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

use PHPStan\DependencyInjection\Container;
use PHPStan\Drupal\DrupalAutoloader;

assert($container instanceof Container);
if ($container === NULL && !($container instanceof Container)) {
throw new \PHPStan\ShouldNotHappenException('The autoloader did not receive the container.');
}

if (!defined('DRUPAL_TEST_IN_CHILD_SITE')) {
define('DRUPAL_TEST_IN_CHILD_SITE', false);
}

$drupalAutoloader = new DrupalAutoloader();
$drupalAutoloader->register($container);
8 changes: 4 additions & 4 deletions extension.neon
@@ -1,4 +1,6 @@
parameters:
autoload_files:
- drupal-autoloader.php
excludes_analyse:
- *.api.php
- */tests/fixtures/*.php
Expand All @@ -9,9 +11,7 @@ parameters:
- install
- profile
- engine
extensions:
drupal: PHPStan\DependencyInjection\DrupalExtension
drupal:
drupalServiceMap: {}
entityTypeStorageMapping:
node: Drupal\node\NodeStorage
taxonomy_term: Drupal\taxonomy\TermStorage
Expand All @@ -31,7 +31,7 @@ services:
-
class: PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension
arguments:
entityTypeStorageMapping: %drupal.entityTypeStorageMapping%
entityTypeStorageMapping: %entityTypeStorageMapping%
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
-
class: PHPStan\Type\ServiceDynamicReturnTypeExtension
Expand Down
3 changes: 0 additions & 3 deletions phpstan-bootstrap.php

This file was deleted.

73 changes: 14 additions & 59 deletions src/DependencyInjection/DrupalExtension.php
Expand Up @@ -11,6 +11,13 @@
use PHPStan\Rules\Classes\RequireParentConstructCallRule;
use Symfony\Component\Yaml\Yaml;

/**
* @deprecated
*
* This extension is currently unused due to changes in phpstan:^0.12.
*
* It was used to dynamically provide information about Drupal services for its rules. Workarounds need to be found.
*/
class DrupalExtension extends CompilerExtension
{
/**
Expand All @@ -22,40 +29,6 @@ class DrupalExtension extends CompilerExtension
'drupal_root' => '',
];

/**
* @var string
*/
private $drupalRoot;

/**
* @var string
*/
private $drupalVendorDir;

/**
* List of available modules.
*
* @var \PHPStan\Drupal\Extension[]
*/
protected $moduleData = [];

/**
* List of available themes.
*
* @var \PHPStan\Drupal\Extension[]
*/
protected $themeData = [];

/**
* @var array
*/
private $modules = [];

/**
* @var array
*/
private $themes = [];

public function loadConfiguration(): void
{
/** @var array */
Expand All @@ -70,18 +43,15 @@ public function loadConfiguration(): void
}

$finder->locateRoot($start_path);
$this->drupalRoot = $finder->getDrupalRoot();
$this->drupalVendorDir = $finder->getVendorDir();
if (! (bool) $this->drupalRoot || ! (bool) $this->drupalVendorDir) {
$drupalRoot = $finder->getDrupalRoot();
$drupalVendorDir = $finder->getVendorDir();
if (! (bool) $drupalRoot || ! (bool) $drupalVendorDir) {
throw new \RuntimeException("Unable to detect Drupal at $start_path");
}

$builder = $this->getContainerBuilder();
$builder->parameters['bootstrap'] = dirname(__DIR__, 2) . '/phpstan-bootstrap.php';
$builder->parameters['drupalRoot'] = $this->drupalRoot;

$this->modules = $config['modules'] ?? [];
$this->themes = $config['themes'] ?? [];
$builder->parameters['drupalRoot'] = $drupalRoot;

$builder->parameters['drupal']['entityTypeStorageMapping'] = $config['entityTypeStorageMapping'] ?? [];

Expand All @@ -97,7 +67,7 @@ public function loadConfiguration(): void
}

// Build the service definitions...
$extensionDiscovery = new ExtensionDiscovery($this->drupalRoot);
$extensionDiscovery = new ExtensionDiscovery($drupalRoot);
$extensionDiscovery->setProfileDirectories([]);
$profiles = $extensionDiscovery->scan('profile');
$profile_directories = array_map(static function (\PHPStan\Drupal\Extension $profile) : string {
Expand All @@ -107,14 +77,14 @@ public function loadConfiguration(): void


$serviceYamls = [
'core' => $this->drupalRoot . '/core/core.services.yml',
'core' => $drupalRoot . '/core/core.services.yml',
];
$serviceClassProviders = [
'core' => 'Drupal\Core\CoreServiceProvider',
];
$extensions = array_merge($extensionDiscovery->scan('module'), $profiles);
foreach ($extensions as $extension) {
$module_dir = $this->drupalRoot . '/' . $extension->getPath();
$module_dir = $drupalRoot . '/' . $extension->getPath();
$moduleName = $extension->getName();
$servicesFileName = $module_dir . '/' . $moduleName . '.services.yml';
if (file_exists($servicesFileName)) {
Expand Down Expand Up @@ -173,19 +143,4 @@ protected function camelize(string $id): string
{
return strtr(ucwords(strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']);
}

public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
// @todo find a non-hack way to pass the Drupal roots to the bootstrap file.
$class->getMethod('initialize')->addBody('$GLOBALS["drupalRoot"] = ?;', [$this->drupalRoot]);
$class->getMethod('initialize')->addBody('$GLOBALS["drupalVendorDir"] = ?;', [$this->drupalVendorDir]);

// DRUPAL_TEST_IN_CHILD_SITE is only defined in the \Drupal\Core\DrupalKernel::bootEnvironment method when
// Drupal is bootstrapped. Since we don't actually invoke the bootstrapping of Drupal, define the constant here
// as `false`. And we have to conditionally define it due to our own PHPUnit tests
$class->getMethod('initialize')->addBody('
if (!defined("DRUPAL_TEST_IN_CHILD_SITE")) {
define("DRUPAL_TEST_IN_CHILD_SITE", ?);
}', [false]);
}
}
27 changes: 21 additions & 6 deletions src/Drupal/Bootstrap.php → src/Drupal/DrupalAutoloader.php
Expand Up @@ -5,8 +5,9 @@
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
use DrupalFinder\DrupalFinder;
use Nette\Utils\Finder;
use PHPStan\DependencyInjection\Container;

class Bootstrap
class DrupalAutoloader
{
/**
* @var array
Expand Down Expand Up @@ -60,15 +61,29 @@ class Bootstrap
*/
private $namespaces = [];

public function register(): void
public function register(Container $container): void
{
$drupalRoot = realpath($GLOBALS['drupalRoot']);
if ($drupalRoot === false) {
throw new \RuntimeException('Cannot determine the Drupal root from ' . $drupalRoot);
$startPath = null;
if ($container->hasParameter('drupal_root')) {
$drupalRoot = $container->getParameter('drupal_root');
if (realpath($drupalRoot) !== false && is_dir($drupalRoot)) {
$startPath = $drupalRoot;
}
}
if ($startPath === null) {
$startPath = dirname($GLOBALS['autoloaderInWorkingDirectory']);
}
$finder = new DrupalFinder();
$finder->locateRoot($startPath);

$drupalRoot = $finder->getDrupalRoot();
$drupalVendorRoot = $finder->getVendorDir();
if (! (bool) $drupalRoot || ! (bool) $drupalVendorRoot) {
throw new \RuntimeException("Unable to detect Drupal at $startPath");
}

$this->drupalRoot = $drupalRoot;

$drupalVendorRoot = realpath($GLOBALS['drupalVendorDir']);
$this->autoloader = include $drupalVendorRoot . '/autoload.php';

$this->extensionDiscovery = new ExtensionDiscovery($this->drupalRoot);
Expand Down
1 change: 0 additions & 1 deletion tests/fixtures/config/phpunit-drupal-phpstan.neon
Expand Up @@ -4,7 +4,6 @@ parameters:
# Ignore functions for the PHPUnit bootstrap process.
ignoreErrors:
- '#Function drupal_phpunit_[a-zA-Z0-9\\_]+ not found#'
drupal:
drupal_root: %currentWorkingDirectory%
includes:
- ../../../extension.neon
Expand Down
17 changes: 17 additions & 0 deletions tests/fixtures/drupal/vendor/autoload.php
@@ -0,0 +1,17 @@
<?php

/**
* @file
* Includes the autoloader created by Composer.
*
* This file was generated by drupal-composer/drupal-scaffold.
* https://github.com/drupal-composer/drupal-scaffold
*
* @see composer.json
* @see index.php
* @see core/install.php
* @see core/rebuild.php
* @see core/modules/statistics/statistics.php
*/

return require __DIR__ . '/../../../../vendor/autoload.php';
29 changes: 10 additions & 19 deletions tests/src/AnalyzerTestBase.php
Expand Up @@ -22,26 +22,17 @@ protected function runAnalyze(string $path) {
$fileHelper = $container->getByType(FileHelper::class);
assert($fileHelper !== null);

$bootstrapFile = $container->parameters['bootstrap'];
$this->assertEquals(dirname(__DIR__, 2) . '/phpstan-bootstrap.php', $bootstrapFile);
// Mock the autoloader.
$GLOBALS['drupalVendorDir'] = dirname(__DIR__, 2) . '/vendor';
if ($bootstrapFile !== null) {
$bootstrapFile = $fileHelper->normalizePath($bootstrapFile);
if (!is_file($bootstrapFile)) {
$this->fail('Bootstrap file not found');
}
try {
(static function (string $file): void {
$autoloadFiles = $container->getParameter('autoload_files');
$this->assertEquals([dirname(__DIR__, 2) . '/drupal-autoloader.php'], $autoloadFiles);
if ($autoloadFiles !== null) {
foreach ($autoloadFiles as $autoloadFile) {
$autoloadFile = $fileHelper->normalizePath($autoloadFile);
if (!is_file($autoloadFile)) {
$this->fail('Autoload file not found');
}
(static function (string $file) use ($container): void {
require_once $file;
})($bootstrapFile);
} catch (ContainerNotInitializedException $e) {
$trace = $e->getTrace();
$offending_file = $trace[1];
$this->fail(sprintf('%s called the Drupal container from unscoped code.', $offending_file['file']));
}
catch (\Throwable $e) {
$this->fail('Could not load the bootstrap file: ' . $e->getMessage());
})($autoloadFile);
}
}

Expand Down
22 changes: 11 additions & 11 deletions tests/src/BootstrapTest.php
Expand Up @@ -55,18 +55,18 @@ private function doDrupalBootstrap()
$fileHelper = $container->getByType(FileHelper::class);
assert($fileHelper !== null);

$bootstrapFile = $container->parameters['bootstrap'];
$this->assertEquals(dirname(__DIR__, 2) . '/phpstan-bootstrap.php', $bootstrapFile);
// Mock the autoloader.
$GLOBALS['drupalVendorDir'] = dirname(__DIR__, 2) . '/vendor';
if ($bootstrapFile !== null) {
$bootstrapFile = $fileHelper->normalizePath($bootstrapFile);
if (!is_file($bootstrapFile)) {
$this->fail('Bootstrap file not found');
$autoloadFiles = $container->getParameter('autoload_files');
$this->assertEquals([dirname(__DIR__, 2) . '/drupal-autoloader.php'], $autoloadFiles);
if ($autoloadFiles !== null) {
foreach ($autoloadFiles as $autoloadFile) {
$autoloadFile = $fileHelper->normalizePath($autoloadFile);
if (!is_file($autoloadFile)) {
$this->fail('Autoload file not found');
}
(static function (string $file) use ($container): void {
require_once $file;
})($autoloadFile);
}
(static function (string $file): void {
require_once $file;
})($bootstrapFile);
}
}

Expand Down

0 comments on commit 20aa2a7

Please sign in to comment.