Passed
Push — pulls/good-things-from-phpstan ( 7312f2...8f8b5f )
by Sam
10:36
created

DevConfigController::index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 17
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 22
rs 9.7
1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPResponse;
8
use SilverStripe\Core\ClassInfo;
9
use SilverStripe\Core\Config\Config;
10
use Symfony\Component\Yaml\Yaml;
11
use SilverStripe\Core\Injector\Injector;
12
13
/**
14
 * Outputs the full configuration.
15
 */
16
class DevConfigController extends Controller
17
{
18
19
    /**
20
     * @var array
21
     */
22
    private static $url_handlers = [
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
23
        'audit' => 'audit',
24
        '' => 'index'
25
    ];
26
27
    /**
28
     * @var array
29
     */
30
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
31
        'index',
32
        'audit',
33
    ];
34
35
    /**
36
     * Note: config() method is already defined, so let's just use index()
37
     *
38
     * @return string|HTTPResponse
39
     */
40
    public function index()
41
    {
42
        $body = '';
43
        $subtitle = "Config manifest";
44
45
        if (Director::is_cli()) {
46
            $body .= sprintf("\n%s\n\n", strtoupper($subtitle));
47
            $body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
48
        } else {
49
            $renderer = DebugView::create();
50
            $body .= $renderer->renderHeader();
51
            $body .= $renderer->renderInfo("Configuration", Director::absoluteBaseURL());
52
            $body .= "<div class=\"options\">";
53
            $body .= sprintf("<h2>%s</h2>", $subtitle);
54
            $body .= "<pre>";
55
            $body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
56
            $body .= "</pre>";
57
            $body .= "</div>";
58
            $body .= $renderer->renderFooter();
59
        }
60
61
        return $this->getResponse()->setBody($body);
62
    }
63
64
    /**
65
     * Output the extraneous config properties which are defined in .yaml but not in a corresponding class
66
     *
67
     * @return string|HTTPResponse
68
     */
69
    public function audit()
70
    {
71
        $body = '';
72
        $missing = [];
73
        $subtitle = "Missing Config property definitions";
74
75
        foreach ($this->array_keys_recursive(Config::inst()->getAll(), 2) as $className => $props) {
76
            $props = array_keys($props);
77
78
            if (!count($props)) {
79
                // We can skip this entry
80
                continue;
81
            }
82
83
            if ($className == strtolower(Injector::class)) {
84
                // We don't want to check the injector config
85
                continue;
86
            }
87
88
            foreach ($props as $prop) {
89
                $defined = false;
90
                // Check ancestry (private properties don't inherit natively)
91
                foreach (ClassInfo::ancestry($className) as $cn) {
92
                    if (property_exists($cn, $prop)) {
93
                        $defined = true;
94
                        break;
95
                    }
96
                }
97
98
                if ($defined) {
99
                    // No need to record this property
100
                    continue;
101
                }
102
103
                $missing[] = sprintf("%s::$%s\n", $className, $prop);
104
            }
105
        }
106
107
        $output = count($missing)
108
            ? implode("\n", $missing)
109
            : "All configured properties are defined\n";
110
111
        if (Director::is_cli()) {
112
            $body .= sprintf("\n%s\n\n", strtoupper($subtitle));
113
            $body .= $output;
114
        } else {
115
            $renderer = DebugView::create();
116
            $body .= $renderer->renderHeader();
117
            $body .= $renderer->renderInfo(
118
                "Configuration",
119
                Director::absoluteBaseURL(),
120
                "Config properties that are not defined (or inherited) by their respective classes"
121
            );
122
            $body .= "<div class=\"options\">";
123
            $body .= sprintf("<h2>%s</h2>", $subtitle);
124
            $body .= sprintf("<pre>%s</pre>", $output);
125
            $body .= "</div>";
126
            $body .= $renderer->renderFooter();
127
        }
128
129
        return $this->getResponse()->setBody($body);
130
    }
131
132
    /**
133
     * Returns all the keys of a multi-dimensional array while maintining any nested structure
134
     *
135
     * @param array $array
136
     * @param int $maxdepth
137
     * @param int $depth
138
     * @param array $arrayKeys
139
     * @return array
140
     */
141
    private function array_keys_recursive($array, $maxdepth = 20, $depth = 0, $arrayKeys = [])
142
    {
143
        if ($depth < $maxdepth) {
144
            $depth++;
145
            $keys = array_keys($array);
146
147
            foreach ($keys as $key) {
148
                if (!is_array($array[$key])) {
149
                    continue;
150
                }
151
152
                $arrayKeys[$key] = $this->array_keys_recursive($array[$key], $maxdepth, $depth);
153
            }
154
        }
155
156
        return $arrayKeys;
157
    }
158
}
159