MDL-87717 core: Update for composer in parent directory

Co-authored-by: Andrew Nicols <[email protected]>
This commit is contained in:
meirzamoodle
2026-02-11 15:11:36 +08:00
committed by Andrew Nicols
co-authored by Andrew Nicols
parent 746815af49
commit 1cf6bd8b87
4 changed files with 75 additions and 78 deletions
+10
View File
@@ -204,6 +204,16 @@ class component {
spl_autoload_register([self::class, 'classloader']);
}
// Attempt to load the Composer autoloader from the Moodle root.
// In composer scaffolded installations this is a shim which delegates to the parent project.
global $CFG;
if (!empty($CFG->root)) {
$composerautoload = "{$CFG->root}/vendor/autoload.php";
if (is_file($composerautoload)) {
require_once($composerautoload);
}
}
// Load any composer-driven autoload files.
// This is intended to mimic the behaviour of the standard Composer Autoloader.
foreach (static::$composerautoloadfiles as $file) {
+64 -33
View File
@@ -24,6 +24,26 @@ namespace core;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class environment {
/**
* Get the Composer root install path for the active Composer runtime.
*
* @return string|null
*/
protected static function get_composer_root_install_path(): ?string {
if (!class_exists(\Composer\InstalledVersions::class)) {
return null;
}
$rootpackage = \Composer\InstalledVersions::getRootPackage();
if (!is_array($rootpackage) || empty($rootpackage['install_path'])) {
return null;
}
$realpath = realpath($rootpackage['install_path']);
return $realpath ?: null;
}
/**
* Ensure that Composer dependencies are installed and the necessary files are present.
*
@@ -32,24 +52,7 @@ class environment {
*/
public static function check_composer_dependencies_installed(\environment_results $result): ?\environment_results {
// Check if the composer vendor directory exists.
$vendorpath = static::get_vendor_path();
if (!is_dir($vendorpath)) {
$result->setInfo('Composer vendor directory not found');
$result->setFeedbackStr('composernotfound');
return $result;
}
// Check if the composer autoload file exists.
$autoloadpath = "{$vendorpath}/autoload.php";
if (!is_file($autoloadpath)) {
$result->setInfo('Composer autoload file not found');
$result->setFeedbackStr('composernotfound');
return $result;
}
// Check if the installed.php file exists in the composer directory.
$installedpath = "{$vendorpath}/composer/installed.php";
if (!is_file($installedpath)) {
if (!class_exists(\Composer\InstalledVersions::class)) {
$result->setInfo('Composer installed data not found');
$result->setFeedbackStr('composernotfound');
return $result;
@@ -72,21 +75,34 @@ class environment {
return null; // Skip this check in developer mode.
}
$vendorpath = static::get_vendor_path();
if (!is_dir($vendorpath)) {
return null; // No vendor directory, so no developer dependencies to check.
if (!class_exists(\Composer\InstalledVersions::class)) {
return null; // Composer not installed, so no developer dependencies to check.
}
// Check if the installed.php file exists in the composer directory.
$installedpath = "{$vendorpath}/composer/installed.php";
if (!is_file($installedpath)) {
return null; // No installed file, so no developer dependencies to check.
$installed = \Composer\InstalledVersions::getAllRawData();
if (!is_array($installed)) {
return null;
}
// Check if developer dependencies have been installed too.
$installed = include($installedpath);
if (is_array($installed) && array_key_exists('root', $installed)) {
if ($installed['root']['dev']) {
// Only consider the installed data set which matches the active Composer root.
// This reduces the risk of reporting false positives if multiple Composer autoloaders
// have been included in the same process.
$rootinstallpath = static::get_composer_root_install_path();
if ($rootinstallpath === null) {
return null;
}
foreach ($installed as $data) {
if (!is_array($data) || !array_key_exists('root', $data) || !is_array($data['root'])) {
continue;
}
$installpath = $data['root']['install_path'] ?? null;
if (empty($installpath) || realpath($installpath) !== $rootinstallpath) {
continue;
}
if (!empty($data['root']['dev'])) {
$result->setInfo('Composer Developer dependencies are installed');
$result->setFeedbackStr('composerdeveloperdependenciesinstalled');
return $result;
@@ -106,12 +122,27 @@ class environment {
public static function check_composer_dependencies_optimised(
\environment_results $result
): ?\environment_results {
$vendorpath = static::get_vendor_path();
if (!is_dir($vendorpath)) {
return null; // No vendor directory, so no developer dependencies to check.
if (!class_exists(\Composer\InstalledVersions::class)) {
return null; // Composer not installed, so no developer dependencies to check.
}
$autoloader = require("{$vendorpath}/autoload.php");
$rootpackage = \Composer\InstalledVersions::getRootPackage();
if (!is_array($rootpackage) || empty($rootpackage['install_path'])) {
return null;
}
$rootpath = $rootpackage['install_path'];
$rootvendor = realpath("{$rootpath}/vendor");
if (!$rootvendor) {
return null;
}
$loaders = \Composer\Autoload\ClassLoader::getRegisteredLoaders();
if (!array_key_exists($rootvendor, $loaders)) {
return null; // No autoloader for our vendor dir, so nothing to check.
}
$autoloader = $loaders[$rootvendor];
if (static::is_developer_mode_enabled()) {
if ($autoloader->isClassMapAuthoritative()) {
+1 -1
View File
@@ -25,7 +25,7 @@ namespace core\tests;
*/
class environment extends \core\environment {
/** @var string|null The path to the vendor dir if modified */
protected static ?string $vendorpath;
protected static ?string $vendorpath = null;
/** @var bool|null Whether developer mode is enabled, or defer to $CFG */
protected static ?bool $devmode = null;
-44
View File
@@ -28,50 +28,6 @@ use core\tests\environment as environment_tester;
*/
#[\PHPUnit\Framework\Attributes\CoversClass(environment::class)]
final class environment_test extends \advanced_testcase {
#[\PHPUnit\Framework\Attributes\DataProvider('composer_error_states_provider')]
public function test_composer_not_installed_cases(
array $fs = [],
string $expectedfeedback = 'composernotfound'
): void {
\org\bovigo\vfs\vfsStream::setup('root', null, $fs);
environment_tester::set_vendor_path(\org\bovigo\vfs\vfsStream::url('root/vendor'));
$result = new \environment_results('custom_check');
$result = environment_tester::check_composer_dependencies_installed($result);
$this->assertEquals($expectedfeedback, $result->getFeedbackStr());
// Check that the developer dependencies tests do not error in these conditions.
$result = new \environment_results('custom_check');
$result = environment_tester::check_composer_developer_dependencies_not_installed($result);
$this->assertNull($result);
}
/**
* Data provider for test_composer_not_installed_cases.
*
* @return \Generator
*/
public static function composer_error_states_provider(): \Generator {
yield 'composer vendor directory not found' => [
'fs' => [],
'expectedfeedback' => 'composernotfound',
];
yield 'composer autoload file not found' => [
'fs' => [
'vendor' => [],
],
'expectedfeedback' => 'composernotfound',
];
yield 'composer installed data not found' => [
'fs' => [
'vendor' => [
'autoload.php' => '',
],
],
'expectedfeedback' => 'composernotfound',
];
}
public function test_composer_installed(): void {
\org\bovigo\vfs\vfsStream::setup('root', null, [
'vendor' => [