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
This commit is contained in:
Jackson Darlow 2020-08-28 07:19:03 +12:00 committed by GitHub
parent 9aa2642d03
commit 699913406b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 169 additions and 1 deletions

View File

@ -13,3 +13,7 @@ SilverStripe\Dev\DevelopmentAdmin:
tasks: 'See a list of build tasks to run' tasks: 'See a list of build tasks to run'
confirm: confirm:
controller: SilverStripe\Dev\DevConfirmationController controller: SilverStripe\Dev\DevConfirmationController
config:
controller: Silverstripe\Dev\DevConfigController
links:
config: 'View the current config, useful for debugging'

View File

@ -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 | | 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 | | Site URL | Action |
| --------------------------------------------- | ------ | | --------------------------------------------- | ------ |
@ -72,3 +72,9 @@ Redirections](/developer_guides/controllers/redirection) for more information an
| quiet | 1 | Don't show messages during build | | 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 | | 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 |

View File

@ -0,0 +1,158 @@
<?php
namespace SilverStripe\Dev;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use Symfony\Component\Yaml\Yaml;
use SilverStripe\Core\Injector\Injector;
/**
* Outputs the full configuration.
*/
class DevConfigController extends Controller
{
/**
* @var array
*/
private static $url_handlers = [
'audit' => '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 .= "<div class=\"options\">";
$body .= sprintf("<h2>%s</h2>", $subtitle);
$body .= "<pre>";
$body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
$body .= "</pre>";
$body .= "</div>";
$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 .= "<div class=\"options\">";
$body .= sprintf("<h2>%s</h2>", $subtitle);
$body .= sprintf("<pre>%s</pre>", $output);
$body .= "</div>";
$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;
}
}