1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-14 02:34:24 +02:00

Additional performance optimizations to Modules class, plus fix issue with some moduleInfo properties falling back to default value when they weren't supposed to

This commit is contained in:
Ryan Cramer
2023-05-30 12:02:16 -04:00
parent caed81876e
commit 8046f1989b
2 changed files with 104 additions and 63 deletions

View File

@@ -465,12 +465,12 @@ class Modules extends WireArray {
/**
* Get data from the verbose module info cache
*
* @param int|string $moduleID
* @param int|string|null $moduleID
* @param string $property
* @return array|mixed|null
*
*/
protected function moduleInfoCacheVerbose($moduleID = 0, $property = '') {
protected function moduleInfoCacheVerbose($moduleID = null, $property = '') {
return $this->moduleInfoCache($moduleID, $property, true);
}
@@ -672,7 +672,10 @@ class Modules extends WireArray {
$className = wireClassName($className, true);
}
$debugKey = $this->debug ? $this->debugTimerStart("newModule($moduleName)") : null;
if(!class_exists($className, false)) $this->includeModule($moduleName);
if(!class_exists($className, false)) {
$result = $this->includeModule($moduleName);
if(!$result) return null;
}
if(!class_exists($className, false)) {
// attempt 2.x module in dedicated namespace or root namespace
$className = $this->getModuleNamespace($moduleName) . $moduleName;
@@ -1122,16 +1125,19 @@ class Modules extends WireArray {
}
// if the filename doesn't end with .module or .module.php, then stop and move onto the next
if(!strpos($filename, '.module') || (substr($filename, -7) !== '.module' && substr($filename, -11) !== '.module.php')) return false;
if(strpos($filename, '.module') === false) return false;
list(, $ext) = explode('.module', $filename, 2);
if(!empty($ext) && $ext !== '.php') return false;
// if the filename doesn't start with the requested path, then continue
if(strpos($pathname, $basepath) !== 0) return '';
// if the file isn't there, it was probably uninstalled, so ignore it
if(!file_exists($pathname)) return '';
// if(!file_exists($pathname)) return '';
// if the module isn't installed, then stop and move on to next
if(!array_key_exists($basename, $installed)) {
if(!isset($installed[$basename]) && !array_key_exists($basename, $installed)) {
// array_key_exists is used as secondary to check the null case
$this->installable[$basename] = $pathname;
return '';
}
@@ -1146,11 +1152,11 @@ class Modules extends WireArray {
// this is an Autoload module.
// include the module and instantiate it but don't init() it,
// because it will be done by Modules::init()
$moduleInfo = $this->getModuleInfo($basename);
// determine if module has dependencies that are not yet met
if(count($moduleInfo['requires'])) {
foreach($moduleInfo['requires'] as $requiresClass) {
$requiresClasses = $this->getModuleInfoProperty($basename, 'requires');
if(!empty($requiresClasses)) {
foreach($requiresClasses as $requiresClass) {
$nsRequiresClass = $this->getModuleClass($requiresClass, true);
if(!wireClassExists($nsRequiresClass, false)) {
$requiresInfo = $this->getModuleInfo($requiresClass);
@@ -1167,22 +1173,22 @@ class Modules extends WireArray {
return $basename;
}
}
// if not defined in getModuleInfo, then we'll accept the database flag as enough proof
// since the module may have defined it via an isAutoload() function
if(!isset($moduleInfo['autoload'])) $moduleInfo['autoload'] = true;
/** @var bool|string|callable $autoload */
$autoload = $moduleInfo['autoload'];
$autoload = $this->moduleInfoCache($basename, 'autoload');
if(empty($autoload)) $autoload = true;
if($autoload === 'function') {
// function is stored by the moduleInfo cache to indicate we need to call a dynamic function specified with the module itself
$i = $this->getModuleInfoExternal($basename);
if(empty($i)) {
$this->includeModuleFile($pathname, $basename);
$className = $moduleInfo['namespace'] . $basename;
$namespace = $this->getModuleNamespace($basename);
$className = $namespace . $basename;
if(method_exists($className, 'getModuleInfo')) {
$i = $className::getModuleInfo();
} else {
$i = array();
$i = $this->getModuleInfo($className);
}
}
$autoload = isset($i['autoload']) ? $i['autoload'] : true;
@@ -1207,10 +1213,13 @@ class Modules extends WireArray {
}
}
if(is_null($module)) {
if($module === null) {
// placeholder for a module, which is not yet included and instantiated
if(!$moduleInfo) $moduleInfo = $this->getModuleInfo($basename);
$module = $this->newModulePlaceholder($basename, $moduleInfo['namespace'], $pathname, $info['flags'] & self::flagsSingular, $autoload);
// if(!$moduleInfo) $moduleInfo = $this->getModuleInfo($basename);
$ns = $moduleInfo ? $moduleInfo['namespace'] : $this->moduleInfoCache($basename, 'namespace');
if(empty($namespace)) $ns = __NAMESPACE__ . "\\";
$singular = $info['flags'] & self::flagsSingular;
$module = $this->newModulePlaceholder($basename, $ns, $pathname, $singular, $autoload);
}
$this->moduleIDs[$basename] = $info['id'];
@@ -1701,14 +1710,17 @@ class Modules extends WireArray {
// still can't figure out what file is? fail
if(!$file) return false;
}
if(!$this->includeModuleFile($file, $moduleName)) {
// module file failed to include(), try to identify and include file again
if($fast) {
$filePrev = $file;
$file = $this->getModuleFile($moduleName, array('fast' => false));
if($file && $file !== $filePrev) {
$this->includeModuleFile($file, $moduleName);
if($this->includeModuleFile($file, $moduleName)) {
// module is missing a module file
return false;
}
}
} else {
// we already tried this earlier, no point in doing it again
@@ -2935,7 +2947,7 @@ class Modules extends WireArray {
* - `namespace` (string): PHP namespace that module lives in.
*
* The following properties are also included when "verbose" mode is requested. When not in verbose mode, these
* properties are present but blank:
* properties may be present but with empty values:
*
* - `versionStr` (string): formatted module version string.
* - `file` (string): module filename from PW installation root, or false when it can't be found.
@@ -3001,7 +3013,7 @@ class Modules extends WireArray {
$moduleName = '';
$moduleID = 0;
$fromCache = false; // was the data loaded from cache?
if(!$getAll && !$getSystem) {
$moduleName = $this->getModuleClass($module);
$moduleID = (string) $this->getModuleID($module); // typecast to string for cache
@@ -3068,9 +3080,16 @@ class Modules extends WireArray {
if(empty($this->moduleInfoCache)) $this->loadModuleInfoCache();
$modulesInfo = $this->moduleInfoCache();
if($options['verbose']) {
if(empty($this->moduleInfoCacheVerbose)) $this->loadModuleInfoCacheVerbose();
foreach($this->moduleInfoCacheVerbose() as $moduleID => $moduleInfoVerbose) {
$modulesInfo[$moduleID] = array_merge($modulesInfo[$moduleID], $moduleInfoVerbose);
if($options['noCache']) {
$modulesInfo[$moduleID] = $this->getModuleInfo($moduleID, $options);
} else {
$modulesInfo[$moduleID] = array_merge($modulesInfo[$moduleID], $moduleInfoVerbose);
}
}
} else if($options['noCache']) {
foreach(array_keys($modulesInfo) as $moduleID) {
$modulesInfo[$moduleID] = $this->getModuleInfo($moduleID, $options);
}
}
if(!$options['minify']) {
@@ -3159,12 +3178,12 @@ class Modules extends WireArray {
}
// populate defaults for properties omitted from cache
if(is_null($info['autoload'])) $info['autoload'] = false;
if(is_null($info['singular'])) $info['singular'] = false;
if(is_null($info['configurable'])) $info['configurable'] = false;
if(is_null($info['core'])) $info['core'] = false;
if(is_null($info['installed'])) $info['installed'] = true;
if(is_null($info['namespace'])) $info['namespace'] = strlen(__NAMESPACE__) ? "\\" . __NAMESPACE__ . "\\" : "";
if($info['autoload'] === null) $info['autoload'] = false;
if($info['singular'] === null) $info['singular'] = false;
if($info['configurable'] === null) $info['configurable'] = false;
if($info['core'] === null) $info['core'] = false;
if($info['installed'] === null) $info['installed'] = true;
if($info['namespace'] === null) $info['namespace'] = strlen(__NAMESPACE__) ? "\\" . __NAMESPACE__ . "\\" : "";
if(!empty($info['requiresVersions'])) $info['requires'] = array_keys($info['requiresVersions']);
if($moduleName == 'SystemUpdater') $info['configurable'] = 1; // fallback, just in case
@@ -3272,11 +3291,13 @@ class Modules extends WireArray {
if($options['minify']) {
// when minify, any values that match defaults from infoTemplate are removed
if(!$options['verbose']) foreach($this->moduleInfoVerboseKeys as $key) unset($info[$key]);
foreach($info as $key => $value) {
if(!array_key_exists($key, $infoTemplate)) continue;
if($value !== $infoTemplate[$key]) continue;
unset($info[$key]);
if(!$options['verbose']) {
foreach($this->moduleInfoVerboseKeys as $key) unset($info[$key]);
foreach($info as $key => $value) {
if(!array_key_exists($key, $infoTemplate)) continue;
if($value !== $infoTemplate[$key]) continue;
unset($info[$key]);
}
}
}
@@ -3324,6 +3345,19 @@ class Modules extends WireArray {
*
*/
public function getModuleInfoProperty($class, $property, array $options = array()) {
if(empty($options['noCache'])) {
// shortcuts where possible
switch($property) {
case 'namespace':
return $this->getModuleNamespace($class);
case 'requires':
$v = $this->moduleInfoCache($class, 'requires');
if(empty($v)) return array(); // early exist when known not to exist
break; // fallback to calling getModuleInfo
}
}
if(in_array($property, $this->moduleInfoVerboseKeys)) {
$info = $this->getModuleInfoVerbose($class, $options);
$info['verbose'] = true;
@@ -3334,6 +3368,7 @@ class Modules extends WireArray {
// try again, just in case we can find it in verbose data
$info = $this->getModuleInfoVerbose($class, $options);
}
return isset($info[$property]) ? $info[$property] : null;
}
@@ -3658,7 +3693,7 @@ class Modules extends WireArray {
if(!$hasDuplicate) {
// see if we can determine it from already stored paths
$path = $config->paths->$moduleName;
$path = $config->paths($moduleName);
if($path) {
$file = $path . $moduleName . ($this->moduleFileExts[$moduleName] === 2 ? '.module.php' : '.module');
if(!$options['fast'] && !file_exists($file)) $file = false;
@@ -3748,7 +3783,7 @@ class Modules extends WireArray {
if(!$file && !empty($options['guess'])) {
// make a guess about where module would be if we had been able to find it
$file = $config->paths->siteModules . "$moduleName/$moduleName.module";
$file = $config->paths('siteModules') . "$moduleName/$moduleName.module";
}
if($file) {

View File

@@ -6,7 +6,7 @@
* Provides functions for managing sitautions where more than one
* copy of the same module is intalled. This is a helper for the Modules class.
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
* https://processwire.com
*
*/
@@ -73,7 +73,7 @@ class ModulesDuplicates extends Wire {
public function hasDuplicate($className, $pathname = '') {
if(!isset($this->duplicates[$className])) return false;
if($pathname) {
$rootPath = $this->wire('config')->paths->root;
$rootPath = $this->wire()->config->paths->root;
if(strpos($pathname, $rootPath) === 0) $pathname = str_replace($rootPath, '/', $pathname);
return in_array($pathname, $this->duplicates[$className]);
}
@@ -90,7 +90,7 @@ class ModulesDuplicates extends Wire {
*/
public function addDuplicate($className, $pathname, $current = false) {
if(!isset($this->duplicates[$className])) $this->duplicates[$className] = array();
$rootPath = $this->wire('config')->paths->root;
$rootPath = $this->wire()->config->paths->root;
if(strpos($pathname, $rootPath) === 0) $pathname = str_replace($rootPath, '/', $pathname);
if(!in_array($pathname, $this->duplicates[$className])) {
$this->duplicates[$className][] = $pathname;
@@ -153,11 +153,12 @@ class ModulesDuplicates extends Wire {
public function getDuplicates($className = '') {
if(!$className) return $this->duplicates;
$className = $this->wire('modules')->getModuleClass($className);
$modules = $this->wire()->modules;
$className = $modules->getModuleClass($className);
$files = isset($this->duplicates[$className]) ? $this->duplicates[$className] : array();
$using = isset($this->duplicatesUse[$className]) ? $this->duplicatesUse[$className] : '';
$rootPath = $this->wire('config')->paths->root;
$rootPath = $this->wire()->config->paths->root;
foreach($files as $key => $file) {
$file = rtrim($rootPath, '/') . $file;
@@ -167,7 +168,7 @@ class ModulesDuplicates extends Wire {
}
if(count($files) > 1 && !$using) {
$using = $this->wire('modules')->getModuleFile($className);
$using = $modules->getModuleFile($className);
$using = str_replace($rootPath, '/', $using);
}
@@ -191,8 +192,9 @@ class ModulesDuplicates extends Wire {
*
*/
public function setUseDuplicate($className, $pathname) {
$className = $this->wire('modules')->getModuleClass($className);
$rootPath = $this->wire('config')->paths->root;
$modules = $this->wire()->modules;
$className = $modules->getModuleClass($className);
$rootPath = $this->wire()->config->paths->root;
if(!isset($this->duplicates[$className])) {
throw new WireException("Module $className does not have duplicates");
}
@@ -204,9 +206,9 @@ class ModulesDuplicates extends Wire {
throw new WireException("Duplicate module file does not exist: $pathname");
}
$this->duplicatesUse[$className] = $pathname;
$configData = $this->wire('modules')->getModuleConfigData($className);
$configData = $modules->getModuleConfigData($className);
$configData['-dups-use'] = $pathname;
$this->wire('modules')->saveModuleConfigData($className, $configData);
$modules->saveModuleConfigData($className, $configData);
}
/**
@@ -214,8 +216,9 @@ class ModulesDuplicates extends Wire {
*
*/
public function updateDuplicates() {
$rootPath = $this->wire('config')->paths->root;
$modules = $this->wire()->modules;
$rootPath = $this->wire()->config->paths->root;
// store duplicate information in each module's data field
foreach($this->getDuplicates() as $moduleName => $files) {
@@ -228,7 +231,7 @@ class ModulesDuplicates extends Wire {
$files[$key] = $file;
}
$files = array_unique($files);
$configData = $this->wire('modules')->getModuleConfigData($moduleName);
$configData = $modules->getModuleConfigData($moduleName);
if((empty($configData['-dups']) && !empty($files))
|| (empty($configData['-dups-use']) || $configData['-dups-use'] != $using)
|| (isset($configData['-dups']) && implode(' ', $configData['-dups']) != implode(' ', $files))
@@ -237,13 +240,13 @@ class ModulesDuplicates extends Wire {
$this->duplicatesUse[$moduleName] = $using;
$configData['-dups'] = $files;
$configData['-dups-use'] = $using;
$this->wire('modules')->saveModuleConfigData($moduleName, $configData);
$modules->saveModuleConfigData($moduleName, $configData);
}
}
// update any modules that no longer have duplicates
$removals = array();
$query = $this->wire('database')->prepare("SELECT `class`, `flags` FROM modules WHERE `flags` & :flag");
$query = $this->wire()->database->prepare("SELECT `class`, `flags` FROM modules WHERE `flags` & :flag");
$query->bindValue(':flag', Modules::flagsDuplicate, \PDO::PARAM_INT);
$query->execute();
@@ -258,10 +261,10 @@ class ModulesDuplicates extends Wire {
}
foreach($removals as $class => $flags) {
$this->wire('modules')->setFlags($class, $flags);
$configData = $this->wire('modules')->getModuleConfigData($class);
$modules->setFlags($class, $flags);
$configData = $modules->getModuleConfigData($class);
unset($configData['-dups'], $configData['-dups-use']);
$this->wire('modules')->saveModuleConfigData($class, $configData);
$modules->saveModuleConfigData($class, $configData);
}
}
@@ -275,7 +278,9 @@ class ModulesDuplicates extends Wire {
*
*/
public function recordDuplicate($basename, $pathname, $pathname2, &$installed) {
$rootPath = $this->wire('config')->paths->root;
$config = $this->wire()->config;
$modules = $this->wire()->modules;
$rootPath = $config->paths->root;
// ensure paths start from root of PW install
if(strpos($pathname, $rootPath) === 0) $pathname = str_replace($rootPath, '/', $pathname);
if(strpos($pathname2, $rootPath) === 0) $pathname2 = str_replace($rootPath, '/', $pathname2);
@@ -295,7 +300,7 @@ class ModulesDuplicates extends Wire {
if(isset($installed[$basename]['flags'])) {
$flags = $installed[$basename]['flags'];
} else {
$flags = $this->wire('modules')->getFlags($basename);
$flags = $modules->getFlags($basename);
}
if($flags & Modules::flagsDuplicate) {
// flags already represent duplicate status
@@ -303,14 +308,14 @@ class ModulesDuplicates extends Wire {
// make database aware this module has multiple files by adding the duplicate flag
$this->numNewDuplicates++; // trigger update needed
$flags = $flags | Modules::flagsDuplicate;
$this->wire('modules')->setFlags($basename, $flags);
$modules->setFlags($basename, $flags);
}
$err = sprintf($this->_('There appear to be multiple copies of module "%s" on the file system.'), $basename) . ' ';
$this->wire('log')->save('modules', $err);
$user = $this->wire('user');
$this->wire()->log->save('modules', $err);
$user = $this->wire()->user;
if($user && $user->isSuperuser()) {
$err .= $this->_('Please edit the module settings to tell ProcessWire which one to use:') . ' ' .
"<a href='" . $this->wire('config')->urls->admin . 'module/edit?name=' . $basename . "'>$basename</a>";
"<a href='" . $config->urls->admin . 'module/edit?name=' . $basename . "'>$basename</a>";
$this->warning($err, Notice::allowMarkup);
}
//$this->message("recordDuplicate($basename, $pathname) $this->numNewDuplicates"); //DEBUG
@@ -327,10 +332,11 @@ class ModulesDuplicates extends Wire {
*
*/
public function getDuplicatesConfigData($className, array $configData = array()) {
$config = $this->wire()->config;
// ensure original duplicates info is retained and validate that it is still current
if(isset($this->duplicates[$className])) {
foreach($this->duplicates[$className] as $key => $file) {
$pathname = rtrim($this->wire('config')->paths->root, '/') . $file;
$pathname = rtrim($config->paths->root, '/') . $file;
if(!file_exists($pathname)) {
unset($this->duplicates[$className][$key]);
}
@@ -341,7 +347,7 @@ class ModulesDuplicates extends Wire {
} else {
$configData['-dups'] = $this->duplicates[$className];
if(isset($this->duplicatesUse[$className])) {
$pathname = rtrim($this->wire('config')->paths->root, '/') . $this->duplicatesUse[$className];
$pathname = rtrim($config->paths->root, '/') . $this->duplicatesUse[$className];
if(file_exists($pathname)) {
$configData['-dups-use'] = $this->duplicatesUse[$className];
} else {