diff --git a/.phpcs.xml b/.phpcs.xml index 0537b2a2a..b888b5317 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -2,18 +2,14 @@ maho - app/bootstrap.php - app/Mage.php - app/code/core/Mage/ - lib/Mage/ - lib/MahoCLI/ - lib/Varien/ + app + lib public/api.php public/index.php public/api.php public/index.php - app/Mage.php + app/bootstrap.php diff --git a/app/Mage.php b/app/Mage.php index f92eafc53..7a8562002 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -10,55 +10,6 @@ * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -define('DS', DIRECTORY_SEPARATOR); -define('PS', PATH_SEPARATOR); -define('BP', MAHO_ROOT_DIR); - -/* - * Require Composer autoloader and set include paths - * @var \Composer\Autoload\ClassLoader $composerClassLoader - */ -$composerClassLoader = require BP . '/vendor/autoload.php'; -set_include_path(implode(PS, \Maho\MahoAutoload::generatePaths(BP)) . PS . get_include_path()); - -if (!empty($_SERVER['MAGE_IS_DEVELOPER_MODE']) || !empty($_ENV['MAGE_IS_DEVELOPER_MODE'])) { - Mage::setIsDeveloperMode(true); - ini_set('display_errors', '1'); - ini_set('error_prepend_string', '
');
-    ini_set('error_append_string', '
'); - - // Fix for overriding zf1-future during development - ini_set('opcache.revalidate_path', 1); - - // Check if we used `composer dump --optimize-autoloader` in development - $classMap = $composerClassLoader->getClassMap(); - if (isset($classMap['Mage_Core_Model_App'])) { - Mage::addBootupWarning('Optimized autoloader detected in developer mode.'); - } - - // Reload PSR-0 namespaces and controller classmap during development in case new files are added - $prefixes = $composerClassLoader->getPrefixes(); - foreach (\Maho\MahoAutoload::generatePsr0(BP) as $prefix => $paths) { - $prefixes[$prefix] ??= []; - if (count($prefixes[$prefix])) { - $prefixes[$prefix] = array_diff($prefixes[$prefix], $paths); - } - array_push($prefixes[$prefix], ...$paths); - $composerClassLoader->set($prefix, $paths); - } - $composerClassLoader->addClassMap(\Maho\MahoAutoload::generateControllerClassMap(BP)); -} - -require_once __DIR__ . '/code/core/Mage/Core/functions.php'; - -/** - * Support additional includes, originally used for OpenMage composer support - * See: https://github.com/OpenMage/magento-lts/pull/559 - */ -foreach (glob(BP . '/app/etc/includes/*.php') as $path) { - include_once $path; -} - /** * Main Mage hub class */ @@ -588,9 +539,8 @@ public static function throwException($message, $messageStorage = null) public static function addBootupWarning(string $message) { - $messages = Mage::registry('bootup_warnings') ?? []; - $messages[] = $message; - Mage::register('bootup_warnings', $message); + self::$_registry['bootup_warnings'] ??= []; + self::$_registry['bootup_warnings'][] = $message; } /** @@ -642,7 +592,7 @@ public static function init($code = '', $type = 'store', $options = [], $modules header('Location: ' . self::getBaseUrl()); die; } catch (Mage_Core_Model_Store_Exception $e) { - mahoErrorReport([], 404); + Maho::errorReport([], 404); die; } catch (Exception $e) { self::printException($e); @@ -685,7 +635,7 @@ public static function run($code = '', $type = 'store', $options = []) header('Location: ' . self::getBaseUrl()); die(); } catch (Mage_Core_Model_Store_Exception $e) { - mahoErrorReport([], 404); + Maho::errorReport([], 404); die(); } catch (Exception $e) { if (self::isInstalled()) { @@ -926,7 +876,7 @@ public static function printException(Throwable $e, $extra = '') $reportData['script_name'] = $_SERVER['SCRIPT_NAME']; } - mahoErrorReport($reportData); + Maho::errorReport($reportData); } die(); diff --git a/app/bootstrap.php b/app/bootstrap.php index cde8e1812..8a67f1c2f 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -6,14 +6,43 @@ * @package Mage * @copyright Copyright (c) 2006-2020 Magento, Inc. (https://magento.com) * @copyright Copyright (c) 2020-2022 The OpenMage Contributors (https://openmage.org) + * @copyright Copyright (c) 2024 Maho (https://mahocommerce.com) * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -/** - * Apply workaround for the libxml PHP bugs: - * @link https://bugs.php.net/bug.php?id=62577 - * @link https://bugs.php.net/bug.php?id=64938 - */ -if ((LIBXML_VERSION < 20900) && function_exists('libxml_disable_entity_loader')) { - libxml_disable_entity_loader(false); +// Require the autoloader if not already loaded +if (!class_exists('Mage')) { + if (file_exists(__DIR__ . '/../vendor/autoload.php')) { + require __DIR__ . '/../vendor/autoload.php'; + } elseif (file_exists(__DIR__ . '/../../../autoload.php')) { + require __DIR__ . '/../../../autoload.php'; + } else { + throw new Exception('Autoloader not found. Please run \'composer install\'.'); + } +} + +defined('DS') || define('DS', DIRECTORY_SEPARATOR); +defined('PS') || define('PS', PATH_SEPARATOR); +defined('BP') || define('BP', Maho::getBasePath()); + +/** @deprecated */ +defined('MAGENTO_ROOT') || define('MAGENTO_ROOT', BP); + +if (!empty($_SERVER['MAGE_IS_DEVELOPER_MODE']) || !empty($_ENV['MAGE_IS_DEVELOPER_MODE'])) { + Mage::setIsDeveloperMode(true); + + ini_set('display_errors', '1'); + ini_set('error_prepend_string', '
');
+    ini_set('error_append_string', '
'); + + // Fix for overriding zf1-future during development + ini_set('opcache.revalidate_path', 1); + + // Update Composer's autoloader during development in case new files are added + Maho::updateComposerAutoloader(); + + // Check if we used `composer dump --optimize-autoloader` in development + if (Maho::isComposerAutoloaderOptimized()) { + Mage::addBootupWarning('Optimized autoloader detected in developer mode.'); + } } diff --git a/app/code/core/Mage/Api/Model/Wsdl/Config.php b/app/code/core/Mage/Api/Model/Wsdl/Config.php index 0719ab704..77cda61c1 100644 --- a/app/code/core/Mage/Api/Model/Wsdl/Config.php +++ b/app/code/core/Mage/Api/Model/Wsdl/Config.php @@ -120,10 +120,10 @@ public function init() * Exclude Mage_Api wsdl xml file because it used for previous version * of API wsdl declaration */ - $mergeWsdl->addLoadedFile(mahoFindFileInIncludePath("$moduleDir/wsi.xml")); + $mergeWsdl->addLoadedFile(Maho::findFile("$moduleDir/wsi.xml")); // Base wsi file - $this->loadFile(mahoFindFileInIncludePath("$moduleDir/wsi.xml")); + $this->loadFile(Maho::findFile("$moduleDir/wsi.xml")); Mage::getConfig()->loadModulesConfiguration('wsi.xml', $this, $mergeWsdl); } else { @@ -131,10 +131,10 @@ public function init() * Exclude Mage_Api wsdl xml file because it used for previous version * of API wsdl declaration */ - $mergeWsdl->addLoadedFile(mahoFindFileInIncludePath("$moduleDir/wsdl.xml")); + $mergeWsdl->addLoadedFile(Maho::findFile("$moduleDir/wsdl.xml")); // Base wsdl file - $this->loadFile(mahoFindFileInIncludePath("$moduleDir/wsdl2.xml")); + $this->loadFile(Maho::findFile("$moduleDir/wsdl2.xml")); Mage::getConfig()->loadModulesConfiguration('wsdl.xml', $this, $mergeWsdl); } diff --git a/app/code/core/Mage/Core/Block/Template.php b/app/code/core/Mage/Core/Block/Template.php index f5bc7470a..f5cfffaca 100644 --- a/app/code/core/Mage/Core/Block/Template.php +++ b/app/code/core/Mage/Core/Block/Template.php @@ -267,9 +267,9 @@ public function fetchView($fileName) && ($this->_viewDir == Mage::getBaseDir('design') || str_starts_with(realpath($this->_viewDir), realpath(Mage::getBaseDir('design')))) ) { - $fileToInclude = mahoFindFileInIncludePath($fileName); + $fileToInclude = Maho::findFile($fileName); if (!$fileToInclude) { - $fileToInclude = mahoFindFileInIncludePath($this->_viewDir . DS . $fileName); + $fileToInclude = Maho::findFile($this->_viewDir . DS . $fileName); } include $fileToInclude; } else { diff --git a/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php b/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php index 461addf07..48b93faa6 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php +++ b/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php @@ -417,7 +417,7 @@ public function getControllerFileName($realModule, $controller) $file .= DS . implode(DS, $parts); } $file .= DS . uc_words($controller, DS) . 'Controller.php'; - $file = mahoFindFileInIncludePath($file); + $file = Maho::findFile($file); return $file; } diff --git a/app/code/core/Mage/Core/Model/Config.php b/app/code/core/Mage/Core/Model/Config.php index 5b2eeaf2e..61fce420a 100644 --- a/app/code/core/Mage/Core/Model/Config.php +++ b/app/code/core/Mage/Core/Model/Config.php @@ -85,7 +85,7 @@ class Mage_Core_Model_Config extends Mage_Core_Model_Config_Base /** * Configuration xml * - * @var Mage_Core_Model_Config_Element + * @var Mage_Core_Model_Config_Element|null */ protected $_xml = null; @@ -206,6 +206,11 @@ class Mage_Core_Model_Config extends Mage_Core_Model_Config_Base */ protected $_prototype; + /** + * Reference to the Varien_Simplexml_Config object where local.xml was loaded in to + */ + protected Mage_Core_Model_Config_Base $_refLocalConfigObject; + /** * Flag which identify what local configuration is loaded * @@ -323,36 +328,35 @@ public function init($options = []) */ public function loadBase() { + // Prevent double loading of base config + if ($this->getNode() !== false) { + return $this; + } + $files = []; - // Include Maho core and 3rd party module files - $modules = \Maho\MahoAutoload::getInstalledModules(BP); - foreach ($modules as $module => $info) { + foreach (Maho::getInstalledPackages() as $package => $info) { foreach (glob($info['path'] . '/app/etc/*.xml') as $file) { - $files[basename($file)] = $file; + $basename = basename($file); + if ($basename === 'local.xml' && $package !== 'root') { + continue; + } + $files[$basename] = $file; } } - // Prevent any module from defining a local.xml - unset($files['local.xml']); - - // Include local files, overriding core and 3rd party module files - foreach (glob($this->getOptions()->getEtcDir() . '/*.xml') as $file) { - $files[basename($file)] = $file; - } - // Merge all config files - $this->loadFile(current($files)); - while ($file = next($files)) { + $this->loadString(''); + foreach ($files as $basename => $file) { $merge = clone $this->_prototype; $merge->loadFile($file); + if ($basename === 'local.xml') { + $this->_isLocalConfigLoaded = true; + $this->_refLocalConfigObject = $merge; + } $this->extend($merge); } - if (isset($files['local.xml'])) { - $this->_isLocalConfigLoaded = true; - } - return $this; } @@ -388,13 +392,9 @@ public function loadModules() $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core')); $this->loadModulesConfiguration(['config.xml', $resourceConfig], $this); - /** - * Prevent local.xml directives overwriting - */ - $mergeConfig = clone $this->_prototype; - $this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir() . DS . 'local.xml'); + // Prevent local.xml directives overwriting if ($this->_isLocalConfigLoaded) { - $this->extend($mergeConfig); + $this->extend($this->_refLocalConfigObject); } $this->applyExtends(); @@ -451,6 +451,8 @@ public function loadEnv(): Mage_Core_Model_Config */ public function reinit($options = []) { + $this->_xml = null; + $this->_isLocalConfigLoaded = false; $this->_allowCacheForInit = false; $this->_useCache = false; return $this->init($options); @@ -503,7 +505,7 @@ public function getCacheSaveLock($waitTime = null, $ignoreFailure = false) throw new Exception('Could not get lock on cache save operation.'); } else { Mage::log(sprintf('Failed to get cache save lock in %d seconds.', $waitTime), Zend_Log::NOTICE); - mahoErrorReport(); + Maho::errorReport(); die(); } } @@ -780,20 +782,11 @@ protected function _getDeclaredModuleFiles() { $moduleFiles = []; - // Include Maho core and 3rd party module files - $modules = \Maho\MahoAutoload::getInstalledModules(BP); - foreach ($modules as $module => $info) { - foreach (glob($info['path'] . '/app/etc/modules/*.xml') as $file) { - $moduleFiles[basename($file)] = $file; - } - } - - // Include local files, overriding core and 3rd party module files - foreach (glob($this->getOptions()->getEtcDir() . '/modules/*.xml') as $file) { + foreach (Maho::globPackages('/app/etc/modules/*.xml') as $file) { $moduleFiles[basename($file)] = $file; } - if (!$moduleFiles) { + if (empty($moduleFiles)) { return false; } @@ -803,9 +796,7 @@ protected function _getDeclaredModuleFiles() ]; foreach ($moduleFiles as $v) { - $name = explode(DIRECTORY_SEPARATOR, $v); - $name = substr($name[count($name) - 1], 0, -4); - + $name = pathinfo($v, PATHINFO_FILENAME); if (array_key_exists($name, self::MAGE_MODULES)) { $collectModuleFiles['mage'][self::MAGE_MODULES[$name]] = $v; } else { @@ -1037,6 +1028,7 @@ public function loadModulesConfiguration($fileName, $mergeToObject = null, $merg if ($mergeModel === null) { $mergeModel = clone $this->_prototype; } + $modules = $this->getNode('modules')->children(); foreach ($modules as $modName => $module) { if ($module->is('active')) { @@ -1046,7 +1038,7 @@ public function loadModulesConfiguration($fileName, $mergeToObject = null, $merg foreach ($fileName as $configFile) { $moduleDir = $this->getModuleDir('etc', $modName); - $configFile = mahoFindFileInIncludePath("$moduleDir/$configFile"); + $configFile = Maho::findFile("$moduleDir/$configFile"); if ($mergeModel->loadFile($configFile)) { $this->_makeEventsLowerCase(Mage_Core_Model_App_Area::AREA_GLOBAL, $mergeModel); diff --git a/app/code/core/Mage/Core/Model/Config/System.php b/app/code/core/Mage/Core/Model/Config/System.php index cf2e21f6e..ea9906a07 100644 --- a/app/code/core/Mage/Core/Model/Config/System.php +++ b/app/code/core/Mage/Core/Model/Config/System.php @@ -24,9 +24,7 @@ class Mage_Core_Model_Config_System extends Mage_Core_Model_Config_Base */ public function load($module) { - $file = Mage::getConfig()->getModuleDir('etc', $module) . DS . 'system.xml'; - $file = mahoFindFileInIncludePath($file); - $this->loadFile($file); + $this->loadFile(Maho::findFile(Mage::getConfig()->getModuleDir('etc', $module) . '/system.xml')); return $this; } } diff --git a/app/code/core/Mage/Core/Model/Design/Config.php b/app/code/core/Mage/Core/Model/Design/Config.php index 86f394729..52837d33d 100644 --- a/app/code/core/Mage/Core/Model/Design/Config.php +++ b/app/code/core/Mage/Core/Model/Design/Config.php @@ -42,19 +42,16 @@ public function __construct(array $params = []) $files = []; - // Include Maho core and 3rd party module files - $modules = \Maho\MahoAutoload::getInstalledModules(BP); - foreach ($modules as $module => $info) { - foreach (glob($info['path'] . '/app/design/*/*/*/etc/theme.xml') as $file) { - $normalizedFile = str_replace($info['path'] . '/app/design', '', $file); - $files[$normalizedFile] = $file; + foreach (Maho::getInstalledPackages() as $package => $info) { + if ($package === 'root') { + $designRoot = $this->_designRoot; + } else { + $designRoot = $info['path'] . '/app/design'; + } + foreach (glob("$designRoot/*/*/*/etc/theme.xml") as $file) { + $relativePath = str_replace($designRoot, '', $file); + $files[$relativePath] = $file; } - } - - // Include local files, overriding core and 3rd party module files - foreach (glob($this->_designRoot . '/*/*/*/etc/theme.xml') as $file) { - $normalizedFile = str_replace($this->_designRoot, '', $file); - $files[$normalizedFile] = $file; } foreach ($files as $file) { diff --git a/app/code/core/Mage/Core/Model/Design/Package.php b/app/code/core/Mage/Core/Model/Design/Package.php index b6e625c22..492f359c4 100644 --- a/app/code/core/Mage/Core/Model/Design/Package.php +++ b/app/code/core/Mage/Core/Model/Design/Package.php @@ -373,7 +373,7 @@ public function validateFile($file, array $params) { $fileName = $this->_renderFilename($file, $params); $fileName = (empty($params['_relative']) ? '' : Mage::getBaseDir('design') . DS) . $fileName; - $fileName = mahoFindFileInIncludePath($fileName); + $fileName = Maho::findFile($fileName); return $fileName; } @@ -538,7 +538,7 @@ public function getSkinUrl($file = null, array $params = []) public function getPackageList() { $directory = Mage::getBaseDir('design') . DS . 'frontend'; - return mahoListDirectories($directory); + return Maho::listDirectories($directory); } /** @@ -556,7 +556,7 @@ public function getThemeList($package = null) } } else { $directory = Mage::getBaseDir('design') . DS . 'frontend' . DS . $package; - $result = mahoListDirectories($directory); + $result = Maho::listDirectories($directory); } return $result; diff --git a/app/code/core/Mage/Core/Model/Resource/Setup.php b/app/code/core/Mage/Core/Model/Resource/Setup.php index b5ab22490..80302c8e9 100644 --- a/app/code/core/Mage/Core/Model/Resource/Setup.php +++ b/app/code/core/Mage/Core/Model/Resource/Setup.php @@ -512,26 +512,20 @@ protected function _getAvailableDbFiles($actionType, $fromVersion, $toVersion) $resModel = (string)$this->_connectionConfig->model; $modName = (string)$this->_moduleConfig[0]->getName(); - $filesDir = Mage::getModuleDir('sql', $modName) . DS . $this->_resourceName; - $filesDir = mahoFindFileInIncludePath($filesDir); - if (!is_dir($filesDir) || !is_readable($filesDir)) { - return []; - } - $dbFiles = []; $typeFiles = []; $regExpDb = sprintf('#^%s-(.*)\.(php|sql)$#i', $actionType); $regExpType = sprintf('#^%s-%s-(.*)\.(php|sql)$#i', $resModel, $actionType); - $handlerDir = dir($filesDir); - while (($file = $handlerDir->read()) !== false) { + + $filesDir = Mage::getModuleDir('sql', $modName) . DS . $this->_resourceName; + foreach (Maho::globPackages("$filesDir/*") as $file) { $matches = []; - if (preg_match($regExpDb, $file, $matches)) { - $dbFiles[$matches[1]] = $filesDir . DS . $file; - } elseif (preg_match($regExpType, $file, $matches)) { - $typeFiles[$matches[1]] = $filesDir . DS . $file; + if (preg_match($regExpDb, basename($file), $matches) === 1) { + $dbFiles[$matches[1]] = $file; + } elseif (preg_match($regExpType, basename($file), $matches) === 1) { + $typeFiles[$matches[1]] = $file; } } - $handlerDir->close(); if (empty($typeFiles) && empty($dbFiles)) { return []; @@ -557,33 +551,24 @@ protected function _getAvailableDataFiles($actionType, $fromVersion, $toVersion) $modName = (string)$this->_moduleConfig[0]->getName(); $files = []; + $regExp = sprintf('#^%s-(.*)\.php$#i', $actionType); + $regExpOld = sprintf('#^%s-%s-(.*)\.php$#i', $this->_connectionConfig->model, $actionType); + $filesDir = Mage::getModuleDir('data', $modName) . DS . $this->_resourceName; - $filesDir = mahoFindFileInIncludePath($filesDir); - if (is_dir($filesDir) && is_readable($filesDir)) { - $regExp = sprintf('#^%s-(.*)\.php$#i', $actionType); - $handlerDir = dir($filesDir); - while (($file = $handlerDir->read()) !== false) { - $matches = []; - if (preg_match($regExp, $file, $matches)) { - $files[$matches[1]] = $filesDir . DS . $file; - } + foreach (Maho::globPackages("$filesDir/*") as $file) { + $matches = []; + if (preg_match($regExp, basename($file), $matches) === 1) { + $files[$matches[1]] = $file; } - $handlerDir->close(); } // search data files in old location $filesDir = Mage::getModuleDir('sql', $modName) . DS . $this->_resourceName; - if (is_dir($filesDir) && is_readable($filesDir)) { - $regExp = sprintf('#^%s-%s-(.*)\.php$#i', $this->_connectionConfig->model, $actionType); - $handlerDir = dir($filesDir); - - while (($file = $handlerDir->read()) !== false) { - $matches = []; - if (preg_match($regExp, $file, $matches)) { - $files[$matches[1]] = $filesDir . DS . $file; - } + foreach (Maho::globPackages("$filesDir/*") as $file) { + $matches = []; + if (preg_match($regExpOld, basename($file), $matches) === 1) { + $files[$matches[1]] = $file; } - $handlerDir->close(); } if (empty($files)) { @@ -601,18 +586,14 @@ protected function _getAvailableMahoFiles(string $actionType, string $fromVersio $modName = (string)$this->_moduleConfig[0]->getName(); $files = []; + $regExp = sprintf('#^%s-(.*)\.php$#i', $actionType); + $filesDir = Mage::getModuleDir('sql', $modName) . DS . 'maho_setup'; - $filesDir = mahoFindFileInIncludePath($filesDir); - if (is_dir($filesDir) && is_readable($filesDir)) { - $regExp = sprintf('#^%s-(.*)\.php$#i', $actionType); - $handlerDir = dir($filesDir); - while (($file = $handlerDir->read()) !== false) { - $matches = []; - if (preg_match($regExp, $file, $matches)) { - $files[$matches[1]] = $filesDir . DS . $file; - } + foreach (Maho::globPackages("$filesDir/*") as $file) { + $matches = []; + if (preg_match($regExp, basename($file), $matches) === 1) { + $files[$matches[1]] = $file; } - $handlerDir->close(); } if (empty($files)) { diff --git a/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php b/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php index f0ee5b447..49594fc27 100644 --- a/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php +++ b/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php @@ -146,7 +146,7 @@ public function start($sessionName = null) } catch (Throwable $e) { session_abort(); if (Mage::registry(self::REGISTRY_CONCURRENCY_ERROR)) { - mahoErrorReport(); + Maho::errorReport(); die(); } else { Mage::printException($e); diff --git a/app/code/core/Mage/Core/Model/Translate.php b/app/code/core/Mage/Core/Model/Translate.php index d693ee669..f277c8f38 100644 --- a/app/code/core/Mage/Core/Model/Translate.php +++ b/app/code/core/Mage/Core/Model/Translate.php @@ -292,9 +292,7 @@ protected function _loadDbTranslation($forceReload = false) */ protected function _getModuleFilePath($module, $fileName) { - $file = "app/locale/{$this->getLocale()}/{$fileName}"; - $file = mahoFindFileInIncludePath($file); - return $file; + return Maho::findFile("app/locale/{$this->getLocale()}/{$fileName}"); } /** diff --git a/app/code/core/Mage/Core/functions.php b/app/code/core/Mage/Core/functions.php index 953873fc2..db23714df 100644 --- a/app/code/core/Mage/Core/functions.php +++ b/app/code/core/Mage/Core/functions.php @@ -71,9 +71,7 @@ function is_empty_date($date) */ function mageFindClassFile($class) { - /** @var \Composer\Autoload\ClassLoader $composerClassLoader */ - $composerClassLoader = require BP . '/vendor/autoload.php'; - return $composerClassLoader->findFile($class); + return Maho::findClassFile($class); } /** @@ -295,76 +293,3 @@ function is_dir_writeable($dir) { return isDirWriteable($dir); } - -function mahoFindFileInIncludePath(string $path): string|false -{ - $paths = []; - $paths[] = BP; - - $modules = \Maho\MahoAutoload::getInstalledModules(BP); - foreach ($modules as $module => $info) { - if ($module === 'mahocommerce/maho') { - continue; - } - $paths[] = $info['path']; - } - if ($modules['mahocommerce/maho']['isChildProject']) { - $paths[] = $modules['mahocommerce/maho']['path']; - } - - $relativePath = str_replace(array_reverse($paths), '', $path); - $relativePath = ltrim($relativePath, '/'); - - // Temporarily set include paths, then revert - $oldPaths = get_include_path(); - set_include_path(implode(PS, $paths)); - $file = stream_resolve_include_path($relativePath); - set_include_path($oldPaths); - - return $file; -} - -function mahoListDirectories(string $path): array -{ - $results = []; - $relativePath = str_replace(BP . '/', '', $path); - - foreach (glob("$path/*", GLOB_ONLYDIR) as $folder) { - $results[] = basename($folder); - } - - $modules = \Maho\MahoAutoload::getInstalledModules(BP); - foreach ($modules as $module => $info) { - foreach (glob($info['path'] . "/$relativePath/*", GLOB_ONLYDIR) as $folder) { - $results[] = basename($folder); - } - } - - return array_unique($results); -} - -function mahoErrorReport(array $reportData = [], int $httpResponseCode = 503): void -{ - $reportIdMessage = ''; - if ($reportData) { - $reportId = abs((int)(microtime(true) * random_int(100, 1000))); - $reportIdMessage = "

Error log record number: {$reportId}

"; - $reportDir = Mage::getBaseDir('var') . '/report'; - if (!file_exists($reportDir)) { - @mkdir($reportDir, 0750, true); - } - - $reportFile = "{$reportDir}/$reportId"; - $reportData = array_map('strip_tags', $reportData); - @file_put_contents($reportFile, serialize($reportData)); - @chmod($reportFile, 0640); - } - - $description = match ($httpResponseCode) { - 404 => 'Not Found', - 503 => 'Service Unavailable', - default => '', - }; - header("HTTP/1.1 {$httpResponseCode} {$description}", true, $httpResponseCode); - echo "

There has been an error processing your request

{$reportIdMessage}"; -} diff --git a/app/code/core/Mage/Install/Block/Begin.php b/app/code/core/Mage/Install/Block/Begin.php index bdebd1b1f..e4786a91e 100644 --- a/app/code/core/Mage/Install/Block/Begin.php +++ b/app/code/core/Mage/Install/Block/Begin.php @@ -52,6 +52,6 @@ public function getPostUrl() */ public function getLicenseHtml() { - return nl2br(file_get_contents(mahoFindFileInIncludePath((string)Mage::getConfig()->getNode('install/eula_file')))); + return nl2br(file_get_contents(Maho::findFile((string)Mage::getConfig()->getNode('install/eula_file')))); } } diff --git a/app/code/core/Mage/Install/Model/Installer/Config.php b/app/code/core/Mage/Install/Model/Installer/Config.php index ae3c42335..903cf400b 100644 --- a/app/code/core/Mage/Install/Model/Installer/Config.php +++ b/app/code/core/Mage/Install/Model/Installer/Config.php @@ -78,7 +78,7 @@ public function install() $this->_getInstaller()->getDataModel()->setConfigData($data); - $template = file_get_contents(mahoFindFileInIncludePath(Mage::getBaseDir('etc') . DS . 'local.xml.template')); + $template = file_get_contents(Maho::findFile(Mage::getBaseDir('etc') . DS . 'local.xml.template')); foreach ($data as $index => $value) { $template = str_replace('{{' . $index . '}}', '', $template); } diff --git a/app/code/core/Mage/Widget/Model/Widget/Instance.php b/app/code/core/Mage/Widget/Model/Widget/Instance.php index 1e21a73c6..e728eaf0c 100644 --- a/app/code/core/Mage/Widget/Model/Widget/Instance.php +++ b/app/code/core/Mage/Widget/Model/Widget/Instance.php @@ -381,7 +381,7 @@ public function getWidgetConfig() '_theme' => $this->getTheme(), '_type' => 'etc' ]) . DS . 'widget.xml'; - $configFile = mahoFindFileInIncludePath($configFile); + $configFile = Maho::findFile($configFile); if (is_readable($configFile)) { $themeWidgetsConfig = new Varien_Simplexml_Config(); $themeWidgetsConfig->loadFile($configFile); diff --git a/composer.json b/composer.json index 94c9daa79..2f5881a01 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,9 @@ "OSL-3.0", "AFL-3.0" ], + "repositories": [ + { "type": "git", "url": "https://github.com/justinbeaty/maho-composer-plugin.git" } + ], "type": "maho-source", "require": { "php": ">=8.2", @@ -33,7 +36,7 @@ "composer-runtime-api": "^2", "cweagans/composer-patches": "^1.7", "ezyang/htmlpurifier": "^4.17", - "mahocommerce/maho-composer-plugin": "^2", + "mahocommerce/maho-composer-plugin": "dev-topic-v3", "pelago/emogrifier": "^7.0", "phpseclib/mcrypt_compat": "^2.0.3", "phpseclib/phpseclib": "^3.0.14", @@ -86,6 +89,7 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "cweagans/composer-patches": true, + "magento-hackathon/magento-composer-installer": false, "mahocommerce/maho-composer-plugin": true }, "platform": { diff --git a/composer.lock b/composer.lock index 78d1962b3..17d0ea878 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c55c597d2e92396f3255ab3fd01d284b", + "content-hash": "4506ffa474bba19a96a5f333495e73bb", "packages": [ { "name": "cweagans/composer-patches", @@ -117,36 +117,40 @@ }, { "name": "mahocommerce/maho-composer-plugin", - "version": "v2.1.0", + "version": "dev-topic-v3", "source": { "type": "git", - "url": "https://github.com/MahoCommerce/maho-composer-plugin.git", - "reference": "f27036354a10aa699718c9e3828fb2b2720eb94e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/MahoCommerce/maho-composer-plugin/zipball/f27036354a10aa699718c9e3828fb2b2720eb94e", - "reference": "f27036354a10aa699718c9e3828fb2b2720eb94e", - "shasum": "" + "url": "https://github.com/justinbeaty/maho-composer-plugin.git", + "reference": "236c5653eab9c0d50aad29875a2fed8582205f5e" }, "require": { - "composer-plugin-api": "^2.0" + "composer-plugin-api": "^2.1", + "composer-runtime-api": "^2" + }, + "require-dev": { + "composer/composer": "^2.8", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0" }, "type": "composer-plugin", "extra": { - "class": "Maho\\MahoComposerPlugin" + "class": [ + "Maho\\ComposerPlugin\\AutoloadPlugin", + "Maho\\ComposerPlugin\\FileCopyPlugin", + "Maho\\ComposerPlugin\\ModmanPlugin" + ] }, "autoload": { "psr-4": { - "Maho\\": "src/" + "Maho\\ComposerPlugin\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", - "support": { - "issues": "https://github.com/MahoCommerce/maho-composer-plugin/issues", - "source": "https://github.com/MahoCommerce/maho-composer-plugin/tree/v2.1.0" - }, - "time": "2024-11-15T10:31:25+00:00" + "license": [ + "MIT" + ], + "description": "Extension for Composer to copy assets and enable autoloading for Maho projects.", + "time": "2024-12-06T14:11:51+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -3030,7 +3034,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "mahocommerce/maho-composer-plugin": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/lib/Maho.php b/lib/Maho.php new file mode 100644 index 000000000..2421ed8d0 --- /dev/null +++ b/lib/Maho.php @@ -0,0 +1,175 @@ + strlen($b) <=> strlen($a)); + return ltrim(str_replace($paths, '', $path), '/'); + } + + /** + * Return a list of all files matching the pattern from installed packages + */ + public static function globPackages(string $pattern, int $flags = 0): array + { + $pattern = self::toRelativePath($pattern); + return AutoloadRuntime::globPackages($pattern, $flags); + } + + /** + * Return the absolute path of a file from installed packages respecting overrides + */ + public static function findFile(string $path): string|false + { + $relativePath = self::toRelativePath($path); + $paths = array_reverse(array_column(self::getInstalledPackages(), 'path')); + + // Temporarily set include paths, then revert + $oldPaths = get_include_path(); + set_include_path(implode(PATH_SEPARATOR, $paths)); + $file = stream_resolve_include_path($relativePath); + set_include_path($oldPaths); + + return $file; + } + + /** + * Return a list of all subdirectories from installed packages + */ + public static function listDirectories(string $path): array + { + $results = []; + foreach (self::globPackages("$path/*", GLOB_ONLYDIR) as $dir) { + $results[] = basename($dir); + } + return array_unique($results); + } + + /** + * Return Composer's ClassLoader instance + */ + public static function getComposerAutoloader(): ClassLoader + { + if (self::$composerClassLoader === null) { + self::$composerClassLoader = require self::getBasePath() . '/vendor/autoload.php'; + } + return self::$composerClassLoader; + } + + /** + * Update Composer's autoloader during development in case new files are added + */ + public static function updateComposerAutoloader(): void + { + $composerClassLoader = self::getComposerAutoloader(); + + $includePaths = AutoloadRuntime::generateIncludePaths(); + $includePaths[] = get_include_path(); + set_include_path(implode(PATH_SEPARATOR, $includePaths)); + + $composerClassLoader->addClassMap(AutoloadRuntime::generateClassMap()); + + foreach (AutoloadRuntime::generatePsr0() as $prefix => $paths) { + $composerClassLoader->add($prefix, $paths, true); + } + + $requireFile = \Closure::bind(static function ($file) { + require_once $file; + }, null, null); + + foreach (AutoloadRuntime::generateIncludeFiles() as $file) { + $requireFile($file); + } + } + + /** + * Check if Composer was run with the `--optimize` flag + */ + public static function isComposerAutoloaderOptimized(): bool + { + return isset(self::getComposerAutoloader()->getClassMap()['Mage_Core_Model_App']); + } + + /** + * Return the absolute path to a class file + */ + public static function findClassFile($class): string|false + { + return realpath(self::getComposerAutoloader()->findFile($class)); + } + + /** + * Generate an error report and output HTML + */ + public static function errorReport(array $reportData = [], int $httpResponseCode = 503): void + { + $reportIdMessage = ''; + if ($reportData) { + $reportId = abs((int)(microtime(true) * random_int(100, 1000))); + $reportIdMessage = "

Error log record number: {$reportId}

"; + $reportDir = Mage::getBaseDir('var') . '/report'; + if (!file_exists($reportDir)) { + @mkdir($reportDir, 0750, true); + } + + $reportFile = "{$reportDir}/$reportId"; + $reportData = array_map('strip_tags', $reportData); + @file_put_contents($reportFile, serialize($reportData)); + @chmod($reportFile, 0640); + } + + $description = match ($httpResponseCode) { + 404 => 'Not Found', + 503 => 'Service Unavailable', + default => '', + }; + header("HTTP/1.1 {$httpResponseCode} {$description}", true, $httpResponseCode); + echo "

There has been an error processing your request

{$reportIdMessage}"; + } +} diff --git a/lib/MahoCLI/Commands/BaseMahoCommand.php b/lib/MahoCLI/Commands/BaseMahoCommand.php index 805ad074f..e2bc7cb7c 100644 --- a/lib/MahoCLI/Commands/BaseMahoCommand.php +++ b/lib/MahoCLI/Commands/BaseMahoCommand.php @@ -17,15 +17,6 @@ abstract class BaseMahoCommand extends Command { protected function initMaho(): void { - $cwd = getcwd(); - if (file_exists("$cwd/app/bootstrap.php")) { - require "$cwd/app/bootstrap.php"; - require "$cwd/app/Mage.php"; - } else { - require "$cwd/vendor/mahocommerce/maho/app/bootstrap.php"; - require "$cwd/vendor/mahocommerce/maho/app/Mage.php"; - } - Mage::register('isSecureArea', true); Mage::app(); } diff --git a/lib/Varien/Simplexml/Config.php b/lib/Varien/Simplexml/Config.php index c94673251..5a012d497 100644 --- a/lib/Varien/Simplexml/Config.php +++ b/lib/Varien/Simplexml/Config.php @@ -21,7 +21,7 @@ class Varien_Simplexml_Config /** * Configuration xml * - * @var Varien_Simplexml_Element|SimpleXMLElement + * @var Varien_Simplexml_Element|SimpleXMLElement|null */ protected $_xml = null; @@ -422,10 +422,10 @@ public function loadFile($filePath) /** * All calls from core will be an absolute path, but for compatibility with - * 3rd party modules we will alternatively use mahoFindFileInIncludePath() + * 3rd party modules we will alternatively use Maho::findFile() */ if (!is_readable($filePath)) { - $filePath = mahoFindFileInIncludePath($filePath); + $filePath = Maho::findFile($filePath); if (!is_readable($filePath)) { //throw new Exception('Can not read xml file '.$filePath); return false; diff --git a/public/api.php b/public/api.php index 72f8c88be..e602d80a9 100644 --- a/public/api.php +++ b/public/api.php @@ -10,18 +10,10 @@ * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -/** @deprecated Use MAHO_ROOT_DIR instead. */ -define('MAGENTO_ROOT', dirname(__DIR__)); define('MAHO_ROOT_DIR', dirname(__DIR__)); define('MAHO_PUBLIC_DIR', __DIR__); -if (file_exists(MAHO_ROOT_DIR . '/app/bootstrap.php')) { - require MAHO_ROOT_DIR . '/app/bootstrap.php'; - require MAHO_ROOT_DIR . '/app/Mage.php'; -} else { - require MAHO_ROOT_DIR . '/vendor/mahocommerce/maho/app/bootstrap.php'; - require MAHO_ROOT_DIR . '/vendor/mahocommerce/maho/app/Mage.php'; -} +require MAHO_ROOT_DIR . '/vendor/autoload.php'; if (!Mage::isInstalled()) { echo 'Application is not installed yet.'; diff --git a/public/index.php b/public/index.php index 5a4d727d0..3f39ef163 100644 --- a/public/index.php +++ b/public/index.php @@ -10,18 +10,10 @@ * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -/** @deprecated Use MAHO_ROOT_DIR instead. */ -define('MAGENTO_ROOT', dirname(__DIR__)); define('MAHO_ROOT_DIR', dirname(__DIR__)); define('MAHO_PUBLIC_DIR', __DIR__); -if (file_exists(MAHO_ROOT_DIR . '/app/bootstrap.php')) { - require MAHO_ROOT_DIR . '/app/bootstrap.php'; - require MAHO_ROOT_DIR . '/app/Mage.php'; -} else { - require MAHO_ROOT_DIR . '/vendor/mahocommerce/maho/app/bootstrap.php'; - require MAHO_ROOT_DIR . '/vendor/mahocommerce/maho/app/Mage.php'; -} +require MAHO_ROOT_DIR . '/vendor/autoload.php'; #Varien_Profiler::enable(); @@ -47,7 +39,7 @@ } } if (!$maintenanceBypass) { - mahoErrorReport(); + Maho::errorReport(); exit; }