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:
@@ -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) {
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user