Commit f64bf94d authored by Gábor Hojtsy's avatar Gábor Hojtsy
Browse files

Issue #3200701 by mglaman, BenjaminLeibinger, Gábor Hojtsy, EricVL, lzande,...

Issue #3200701 by mglaman, BenjaminLeibinger, Gábor Hojtsy, EricVL, lzande, Kristen Pol, gurunathan, golddragon007, jprj, acnimda, samalander, manojapare, andypost, Marceldeb, James Marks, DYdave, couloir007: Use the Symfony Process component with PhpExecutableFinder to ensure PHP is ran with the same executable as the webserver
parent 7c6a8cbf
......@@ -12,6 +12,8 @@ use Drupal\Core\Template\TwigEnvironment;
use DrupalFinder\DrupalFinder;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use Twig\Util\DeprecationCollector;
use Twig\Util\TemplateDirIterator;
......@@ -52,6 +54,13 @@ final class DeprecationAnalyzer {
*/
protected $binPath;
/**
* Path to the PHP binary.
*
* @var string
*/
protected $phpPath;
/**
* Temporary directory to use for running phpstan.
*
......@@ -167,6 +176,8 @@ final class DeprecationAnalyzer {
return;
}
$this->phpPath = $this->findPhpPath();
$this->finder = new DrupalFinder();
$this->finder->locateRoot(DRUPAL_ROOT);
......@@ -222,7 +233,7 @@ final class DeprecationAnalyzer {
// If a bin-dir is specified, that is most specific.
if (isset($json['config']['bin-dir'])) {
$binPath = $this->finder->getComposerRoot() . '/' . $json['config']['bin-dir'];
$binPath = $this->finder->getComposerRoot() . '/' . rtrim($json['config']['bin-dir'], '/');
if (file_exists($binPath . '/phpstan')) {
return $binPath;
}
......@@ -233,7 +244,7 @@ final class DeprecationAnalyzer {
// If a vendor-dir is specified, that is slightly less specific.
if (isset($json['config']['vendor-dir'])) {
$binPath = $this->finder->getComposerRoot() . '/' . $json['config']['vendor-dir'] . '/bin';
$binPath = $this->finder->getComposerRoot() . '/' . rtrim($json['config']['vendor-dir'], '/') . '/bin';
if (file_exists($binPath . '/phpstan')) {
return $binPath;
}
......@@ -251,6 +262,26 @@ final class DeprecationAnalyzer {
throw new \Exception('The PHPStan binary was not found in the default vendor directory based on the location of ' . $composer_json_path . '. You may need to configure a vendor-dir in composer.json. See https://getcomposer.org/doc/06-config.md#vendor-dir. Attempted: ' . $binPath . '/phpstan.');
}
/**
* Finds the PHP path.
*
* This ensures we execute PHPStan with the same PHP binary that is used by
* the web server.
*
* @return string
* PHP path if found.
*
* @throws \Exception
*/
protected function findPhpPath() {
$finder = new PhpExecutableFinder();
$binary = $finder->find();
if ($binary === FALSE) {
throw new \Exception('The PHP binary was not found.');
}
return $binary;
}
/**
* Analyze the codebase of an extension including all its sub-components.
*
......@@ -276,17 +307,25 @@ final class DeprecationAnalyzer {
$project_dir = DRUPAL_ROOT . '/' . $extension->getPath();
$this->logger->notice('Processing %path.', ['%path' => $project_dir]);
$output = [];
$error_filename = $this->temporaryDirectory . '/phpstan_error_output';
$command = $this->binPath . '/phpstan analyse --memory-limit=-1 --error-format=json -c ' . $this->phpstanNeonPath . ' ' . $project_dir . ' 2> ' . $error_filename;
exec($command, $output);
$command = [
$this->phpPath,
$this->binPath . '/phpstan',
'analyse',
'--memory-limit=-1',
'--error-format=json',
'--configuration=' . $this->phpstanNeonPath,
$project_dir,
];
$process = new Process($command, DRUPAL_ROOT, NULL, NULL, NULL);
$process->run();
$json = json_decode(implode('', $output), TRUE);
if (!isset($json['files']) || !is_array($json['files'])) {
$stdout = trim(implode('', $output)) ?: 'Empty.';
$stderr = trim(file_get_contents($error_filename)) ?: 'Empty.';
$json = json_decode($process->getOutput(), TRUE);
if (json_last_error() !== JSON_ERROR_NONE) {
$stdout = trim($process->getOutput()) ?: 'Empty.';
$stderr = trim($process->getErrorOutput()) ?: 'Empty.';
$formatted_error =
"<h6>PHPStan command failed:</h6> <p>" . $command .
"<h6>PHPStan command failed:</h6> <p>" . implode(" ", $command) .
"</p> <h6>Command output:</h6> <p>" . $stdout .
"</p> <h6>Command error:</h6> <p>" . $stderr . '</p>';
$this->logger->error('%phpstan_fail', ['%phpstan_fail' => strip_tags($formatted_error)]);
......@@ -458,7 +497,7 @@ final class DeprecationAnalyzer {
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
elseif ((projectCollector::getDrupalCoreMajorVersion() > 8) && !empty($composer_json->require->{'php'} && !projectCollector::isCompatibleWithPHP8($composer_json->require->{'php'}))) {
elseif ((projectCollector::getDrupalCoreMajorVersion() > 8) && !empty($composer_json->require->{'php'}) && !projectCollector::isCompatibleWithPHP8($composer_json->require->{'php'})) {
$result['data']['files'][$extension->getPath() . '/composer.json']['messages'][] = [
'message' => "The PHP requirement is not compatible with PHP 8. Once the codebase is actually compatible, either remove this limitation or update it to be compatible.",
'line' => 0,
......
......@@ -224,7 +224,9 @@ class UpgradeStatusForm extends FormBase {
}
catch (\Exception $e) {
$analyzer_ready = FALSE;
$this->messenger()->addError($e->getMessage());
// Message and impact description is not translated as the message
// is sourced from an exception thrown.
$this->messenger()->addError($e->getMessage() . ' Scanning is not possible until this is resolved.');
}
$environment = $this->buildEnvironmentChecks();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment