1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-10 08:44:46 +02:00

Improve error reporting ability of $modules->get(), $modules->getModule(), primarily for debugging purposes.

This commit is contained in:
Ryan Cramer
2017-10-20 09:45:53 -04:00
parent 97935d156a
commit b2381002be
2 changed files with 106 additions and 37 deletions

View File

@@ -567,14 +567,19 @@ class Modules extends WireArray {
* Initialize a single module
*
* @param Module $module
* @param bool $clearSettings If true, module settings will be cleared when appropriate to save space.
* @param array $options
* - `clearSettings` (bool): When true, module settings will be cleared when appropriate to save space. (default=true)
* - `throw` (bool): When true, exceptions will be allowed to pass through. (default=false)
* @return bool True on success, false on fail
* @throws \Exception Only if the `throw` option is true.
*
*/
protected function initModule(Module $module, $clearSettings = true) {
protected function initModule(Module $module, array $options = array()) {
$result = true;
$debugKey = null;
$clearSettings = isset($options['clearSettings']) ? (bool) $options['clearSettings'] : true;
$throw = isset($options['throw']) ? (bool) $options['throw'] : false;
if($this->debug) {
static $n = 0;
@@ -601,6 +606,7 @@ class Modules extends WireArray {
try {
$module->init();
} catch(\Exception $e) {
if($throw) throw($e);
$this->error(sprintf($this->_('Failed to init module: %s'), $moduleName) . " - " . $e->getMessage());
$result = false;
}
@@ -1167,11 +1173,13 @@ class Modules extends WireArray {
* This is the same as `$modules->get()` except that you can specify additional options to modify default behavior.
* These are the options you can specify in the `$options` array argument:
*
* - `noPermissionCheck` (bool): Specify true to disable module permission checks (and resulting exception).
* - `noInstall` (bool): Specify true to prevent a non-installed module from installing from this request.
* - `noInit` (bool): Specify true to prevent the module from being initialized.
* - `noSubstitute` (bool): Specify true to prevent inclusion of a substitute module.
* - `noCache` (bool): Specify true to prevent module instance from being cached for later getModule() calls.
* - `noPermissionCheck` (bool): Specify true to disable module permission checks (and resulting exception). (default=false)
* - `noInstall` (bool): Specify true to prevent a non-installed module from installing from this request. (default=false)
* - `noInit` (bool): Specify true to prevent the module from being initialized. (default=false)
* - `noSubstitute` (bool): Specify true to prevent inclusion of a substitute module. (default=false)
* - `noCache` (bool): Specify true to prevent module instance from being cached for later getModule() calls. (default=false)
* - `noThrow` (bool): Specify true to prevent exceptions from being thrown on permission or fatal error. (default=false)
* - `returnError` (bool): Return an error message (string) on error, rather than null. (default=false)
*
* If the module is not installed, but is installable, it will be installed, instantiated, and initialized.
* If you don't want that behavior, call `$modules->isInstalled('ModuleName')` as a condition first, OR specify
@@ -1179,69 +1187,111 @@ class Modules extends WireArray {
*
* @param string|int $key Module name or database ID.
* @param array $options Optional settings to change load behavior, see method description for details.
* @return Module|_Module|null Returns ready-to-use module or NULL if not found.
* @throws WirePermissionException If module requires a particular permission the user does not have
* @return Module|_Module|null|string Returns ready-to-use module or NULL|string if not found (string if `returnError` option used).
* @throws WirePermissionException|\Exception If module requires a particular permission the user does not have
* @see Modules::get()
*
*/
public function getModule($key, array $options = array()) {
if(empty($key)) return null;
$module = null;
$needsInit = false;
$error = '';
if(empty($key)) {
return empty($options['returnError']) ? null : "No module specified";
}
// check for optional module ID and convert to classname if found
if(ctype_digit("$key")) {
if(!$key = array_search($key, $this->moduleIDs)) return null;
$moduleID = (int) $key;
if(!$key = array_search($key, $this->moduleIDs)) {
return empty($options['returnError']) ? null : "Unable to find module ID $moduleID";
}
} else {
$key = wireClassName($key, false);
}
$module = parent::get($key);
if(!$module && empty($options['noSubstitute'])) {
if($this->isInstallable($key) && empty($options['noInstall'])) {
// module is on file system and may be installed, no need to substitute
if(!$module) {
if(empty($options['noSubstitute'])) {
if($this->isInstallable($key) && empty($options['noInstall'])) {
// module is on file system and may be installed, no need to substitute
} else {
$module = $this->getSubstituteModule($key, $options);
if($module) return $module; // returned module is ready to use
}
} else {
$module = $this->getSubstituteModule($key, $options);
if($module) return $module; // returned module is ready to use
$error = "Module '$key' not found and substitute not allowed (noSubstitute=true)";
}
}
if($module) {
// check if it's a placeholder, and if it is then include/instantiate/init the real module
// OR check if it's non-singular, so that a new instance is created
if($module instanceof ModulePlaceholder || !$this->isSingular($module)) {
$placeholder = $module;
$class = $this->getModuleClass($placeholder);
if($module instanceof ModulePlaceholder) $this->includeModule($module);
$module = $this->newModule($class);
try {
if($module instanceof ModulePlaceholder) $this->includeModule($module);
$module = $this->newModule($class);
} catch(\Exception $e) {
if(empty($options['noThrow'])) throw $e;
return empty($options['returnError']) ? null : "Module '$key' - " . $e->getMessage();
}
// if singular, save the instance so it can be used in later calls
if($module && $this->isSingular($module) && empty($options['noCache'])) $this->set($key, $module);
$needsInit = true;
}
} else if(empty($options['noInstall']) && array_key_exists($key, $this->getInstallable())) {
// check if the request is for an uninstalled module
// if so, install it and return it
$module = $this->install($key);
$needsInit = true;
} else if(empty($options['noInstall'])) {
// module was not available to get, see if we can install it
if(array_key_exists($key, $this->getInstallable())) {
// check if the request is for an uninstalled module
// if so, install it and return it
try {
$module = $this->install($key);
} catch(\Exception $e) {
if(empty($options['noThrow'])) throw $e;
if(!empty($options['returnError'])) return "Module '$key' install failed: " . $e->getMessage();
}
$needsInit = true;
if(!$module) $error = "Module '$key' not installed and install failed";
} else {
$error = "Module '$key' is not present or listed as installable";
}
} else {
$error = "Module '$key' is not present and not installable (noInstall=true)";
}
if($module && empty($options['noPermissionCheck'])) {
if(!$module) {
if(!$error) $error = "Unable to get module '$key'";
return empty($options['returnError']) ? null : $error;
}
if(empty($options['noPermissionCheck'])) {
// check that user has permission required to use module
if(!$this->hasPermission($module, $this->wire('user'), $this->wire('page'))) {
throw new WirePermissionException($this->_('You do not have permission to execute this module') . ' - ' . wireClassName($module));
$error = $this->_('You do not have permission to execute this module') . ' - ' . wireClassName($module);
if(empty($options['noThrow'])) throw new WirePermissionException($error);
return empty($options['returnError']) ? null : $error;
}
}
// skip autoload modules because they have already been initialized in the load() method
// unless they were just installed, in which case we need do init now
if($module && $needsInit) {
if($needsInit && empty($options['noInit'])) {
// if the module is configurable, then load it's config data
// and set values for each before initializing the module
if(empty($options['noInit'])) {
if(!$this->initModule($module, false)) $module = null;
try {
if(!$this->initModule($module, array('clearSettings' => false, 'throw' => true))) {
return empty($options['returnError']) ? null : "Module '$module' failed init";
$module = null;
}
} catch(\Exception $e) {
if(empty($options['noThrow'])) throw $e;
return empty($options['returnError']) ? null : "Module '$module' throw Exception on init - " . $e->getMessage();
}
}

View File

@@ -52,7 +52,15 @@ class ProcessController extends Wire {
* @var string
*
*/
protected $processName;
protected $processName;
/**
* Error message if unable to load Process module
*
* @var string
*
*/
protected $processError = '';
/**
* The name of the method to execute in this process
@@ -141,9 +149,13 @@ class ProcessController extends Wire {
*/
public function getProcess() {
if($this->process) $processName = $this->process->className();
else if($this->processName) $processName = $this->processName;
else return null;
if($this->process) {
$processName = $this->process->className();
} else if($this->processName) {
$processName = $this->processName;
} else {
return null;
}
// verify that there is adequate permission to execute the Process
$permissionName = '';
@@ -151,11 +163,18 @@ class ProcessController extends Wire {
if(!empty($info['permission'])) $permissionName = $info['permission'];
$this->hasPermission($permissionName, true); // throws exception if no permission
if(!$this->process) {
$this->process = $this->modules->getModule($processName);
$module = $this->modules->getModule($processName, array('returnError' => true));
if(is_string($module)) {
$this->processError = $module;
$this->process = null;
} else {
$this->process = $module;
}
}
// set a proces fuel, primarily so that certain Processes can determine if they are the root Process
// set a process fuel, primarily so that certain Processes can determine if they are the root Process
// example: PageList when in PageEdit
$this->wire('process', $this->process);
@@ -259,7 +278,7 @@ class ProcessController extends Wire {
}
} else {
throw new ProcessController404Exception("The requested process does not exist");
throw new ProcessController404Exception("The requested process does not exist - $this->processError");
}
if(empty($content) || is_bool($content)) {