From 699913406be6ecc385a63039cc6d20048148f561 Mon Sep 17 00:00:00 2001 From: Jackson Darlow Date: Fri, 28 Aug 2020 07:19:03 +1200 Subject: [PATCH] NEW Added a dev view to output the current config from dev/config (#9542) * #3236 Added dev/config view * Refactored use of echo to * Added config/audit for matching missing property definitions in the current config * Minor enhancements to dev/config implementation --- _config/dev.yml | 4 + .../07_Debugging/02_URL_Variable_Tools.md | 8 +- src/Dev/DevConfigController.php | 158 ++++++++++++++++++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 src/Dev/DevConfigController.php diff --git a/_config/dev.yml b/_config/dev.yml index c884d4ec3..ca74c082f 100644 --- a/_config/dev.yml +++ b/_config/dev.yml @@ -13,3 +13,7 @@ SilverStripe\Dev\DevelopmentAdmin: tasks: 'See a list of build tasks to run' confirm: controller: SilverStripe\Dev\DevConfirmationController + config: + controller: Silverstripe\Dev\DevConfigController + links: + config: 'View the current config, useful for debugging' diff --git a/docs/en/02_Developer_Guides/07_Debugging/02_URL_Variable_Tools.md b/docs/en/02_Developer_Guides/07_Debugging/02_URL_Variable_Tools.md index 96f308587..6090ce842 100644 --- a/docs/en/02_Developer_Guides/07_Debugging/02_URL_Variable_Tools.md +++ b/docs/en/02_Developer_Guides/07_Debugging/02_URL_Variable_Tools.md @@ -58,7 +58,7 @@ Redirections](/developer_guides/controllers/redirection) for more information an | ------------ | ------ | ----------- | | BackURL | URL | Set to a relative URL string to use once Security Action is complete | -## Building and Publishing URLS +## Building and Publishing URLs | Site URL | Action | | --------------------------------------------- | ------ | @@ -72,3 +72,9 @@ Redirections](/developer_guides/controllers/redirection) for more information an | quiet | 1 | Don't show messages during build | | dont_populate | 1 | Don't run **requireDefaultRecords()** on the models when building. This will build the table but not insert any records | +## Config diagnostic URLs + + | Site URL | Action | + | --------------------------------------------- | ------ | + | http://localhost**/dev/config** | Output a simplified representation properties in the `Config` manifest | + | http://localhost**/dev/config/audit** | Audit `Config` properties and display any with missing PHP definitions | diff --git a/src/Dev/DevConfigController.php b/src/Dev/DevConfigController.php new file mode 100644 index 000000000..71e57e75f --- /dev/null +++ b/src/Dev/DevConfigController.php @@ -0,0 +1,158 @@ + 'audit', + '' => 'index' + ]; + + /** + * @var array + */ + private static $allowed_actions = [ + 'index', + 'audit', + ]; + + /** + * Note: config() method is already defined, so let's just use index() + * + * @return string|HTTPResponse + */ + public function index() + { + $body = ''; + $subtitle = "Config manifest"; + + if (Director::is_cli()) { + $body .= sprintf("\n%s\n\n", strtoupper($subtitle)); + $body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + } else { + $renderer = DebugView::create(); + $body .= $renderer->renderHeader(); + $body .= $renderer->renderInfo("Configuration", Director::absoluteBaseURL()); + $body .= "
"; + $body .= sprintf("

%s

", $subtitle); + $body .= "
";
+            $body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
+            $body .= "
"; + $body .= "
"; + $body .= $renderer->renderFooter(); + } + + return $this->getResponse()->setBody($body); + } + + /** + * Output the extraneous config properties which are defined in .yaml but not in a corresponding class + * + * @return string|HTTPResponse + */ + public function audit() + { + $body = ''; + $missing = []; + $subtitle = "Missing Config property definitions"; + + foreach ($this->array_keys_recursive(Config::inst()->getAll(), 2) as $className => $props) { + $props = array_keys($props); + + if (!count($props)) { + // We can skip this entry + continue; + } + + if ($className == strtolower(Injector::class)) { + // We don't want to check the injector config + continue; + } + + foreach ($props as $prop) { + $defined = false; + // Check ancestry (private properties don't inherit natively) + foreach (ClassInfo::ancestry($className) as $cn) { + if (property_exists($cn, $prop)) { + $defined = true; + break; + } + } + + if ($defined) { + // No need to record this property + continue; + } + + $missing[] = sprintf("%s::$%s\n", $className, $prop); + } + } + + $output = count($missing) + ? implode("\n", $missing) + : "All configured properties are defined\n"; + + if (Director::is_cli()) { + $body .= sprintf("\n%s\n\n", strtoupper($subtitle)); + $body .= $output; + } else { + $renderer = DebugView::create(); + $body .= $renderer->renderHeader(); + $body .= $renderer->renderInfo( + "Configuration", + Director::absoluteBaseURL(), + "Config properties that are not defined (or inherited) by their respective classes" + ); + $body .= "
"; + $body .= sprintf("

%s

", $subtitle); + $body .= sprintf("
%s
", $output); + $body .= "
"; + $body .= $renderer->renderFooter(); + } + + return $this->getResponse()->setBody($body); + } + + /** + * Returns all the keys of a multi-dimensional array while maintining any nested structure + * + * @param array $array + * @param int $maxdepth + * @param int $depth + * @param array $arrayKeys + * @return array + */ + private function array_keys_recursive($array, $maxdepth = 20, $depth = 0, $arrayKeys = []) + { + if ($depth < $maxdepth) { + $depth++; + $keys = array_keys($array); + + foreach ($keys as $key) { + if (!is_array($array[$key])) { + continue; + } + + $arrayKeys[$key] = $this->array_keys_recursive($array[$key], $maxdepth, $depth); + } + } + + return $arrayKeys; + } +}