ColumnUtil   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 45
eloc 113
c 3
b 1
f 1
dl 0
loc 192
rs 8.8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A createCacheFilename() 0 24 5
B populateCacheFile() 0 35 9
A cacheClassesFromFile() 0 6 2
F extractClassesFromFile() 0 94 29

How to fix   Complexity   

Complex Class

Complex classes like ColumnUtil often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ColumnUtil, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Dtc\GridBundle\Util;
4
5
use Dtc\GridBundle\Annotation\Action;
6
use Exception;
7
use Symfony\Component\Yaml\Yaml;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Yaml\Yaml was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
9
class ColumnUtil
10
{
11
    /**
12
     * @param $cacheDir
13
     * @param $fqn
14
     *
15
     * @return string
16
     *
17
     * @throws Exception
18
     */
19
    public static function createCacheFilename($cacheDir, $fqn)
20
    {
21
        $directory = $cacheDir.'/DtcGridBundle';
22
        $umask = decoct(umask());
23
        $umask = str_pad($umask, 4, '0', STR_PAD_LEFT);
24
25
        // Is there a better way to do this?
26
        $permissions = '0777';
27
        $permissions[1] = intval($permissions[1]) - intval($umask[1]);
28
        $permissions[2] = intval($permissions[2]) - intval($umask[2]);
29
        $permissions[3] = intval($permissions[3]) - intval($umask[3]);
30
31
        $name = str_replace('\\', DIRECTORY_SEPARATOR, $fqn);
32
        $name = ltrim($name, DIRECTORY_SEPARATOR);
33
        $filename = $directory.DIRECTORY_SEPARATOR.$name.'.php';
34
35
        if (($dir = dirname($filename)) && !is_dir($dir) && !mkdir($dir, octdec($permissions), true)) {
0 ignored issues
show
Bug introduced by
It seems like octdec($permissions) can also be of type double; however, parameter $permissions of mkdir() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

35
        if (($dir = dirname($filename)) && !is_dir($dir) && !mkdir($dir, /** @scrutinizer ignore-type */ octdec($permissions), true)) {
Loading history...
36
            throw new Exception("Can't create: ".$dir);
37
        }
38
        if (!is_writable($dir)) {
39
            throw new Exception("Can't write to: $dir");
40
        }
41
42
        return $filename;
43
    }
44
45
    /**
46
     * @param string $filename
47
     */
48
    public static function populateCacheFile($filename, array $classInfo)
49
    {
50
        $columns = isset($classInfo['columns']) ? $classInfo['columns'] : [];
51
        $sort = isset($classInfo['sort']) ? $classInfo['sort'] : [];
52
53
        if ($columns) {
54
            $output = "<?php\nreturn array('columns' => array(\n";
55
            foreach ($columns as $field => $info) {
56
                $class = $info['class'];
57
                $output .= "'$field' => new $class(";
58
                $first = true;
59
                foreach ($info['arguments'] as $argument) {
60
                    if ($first) {
61
                        $first = false;
62
                    } else {
63
                        $output .= ',';
64
                    }
65
                    $output .= var_export($argument, true);
66
                }
67
                $output .= '),';
68
            }
69
            $output .= "), 'sort' => array(";
70
            foreach ($sort as $key => $value) {
71
                $output .= "'$key'".' => ';
72
                if (null === $value) {
73
                    $output .= 'null,';
74
                } else {
75
                    $output .= "'$value',";
76
                }
77
            }
78
            $output .= "));\n";
79
        } else {
80
            $output = "<?php\nreturn false;\n";
81
        }
82
        file_put_contents($filename, $output);
83
    }
84
85
    /**
86
     * @param string $cacheDir
87
     * @param string $filename
88
     *
89
     * @throws Exception
90
     */
91
    public static function cacheClassesFromFile($cacheDir, $filename)
92
    {
93
        $classes = self::extractClassesFromFile($filename);
94
        foreach ($classes as $class => $columnInfo) {
95
            $filename = ColumnUtil::createCacheFilename($cacheDir, $class);
96
            self::populateCacheFile($filename, $columnInfo);
97
        }
98
    }
99
100
    /**
101
     * @param string $filename
102
     *
103
     * @return array
104
     *
105
     * @throws Exception
106
     */
107
    public static function extractClassesFromFile($filename)
108
    {
109
        // @TODO probably break this into multiple functions
110
        if (!is_readable($filename)) {
111
            throw new Exception("Can't read {$filename}");
112
        }
113
        $stat = stat($filename);
114
        $result = method_exists('Symfony\Component\Yaml\Yaml', 'parseFile') ? Yaml::parseFile($filename) : Yaml::parse(file_get_contents($filename));
115
        if (!$result && $stat['size'] > 0) {
116
            throw new Exception("Can't parse data from {$filename}");
117
        }
118
119
        $classes = [];
120
        foreach ($result as $class => $info) {
121
            if (!isset($info['columns'])) {
122
                // @TODO some kind of warning here - or try to read using reflection?
123
                continue;
124
            }
125
            $class = ltrim($class, '\\');
126
            if (!class_exists($class)) {
127
                throw new Exception("$class - class does not exist");
128
            }
129
            $classes[$class]['columns'] = [];
130
            foreach ($info['columns'] as $name => $columnDef) {
131
                $label = isset($columnDef['label']) ? $columnDef['label'] : CamelCase::fromCamelCase($name);
132
                $column = ['class' => '\Dtc\GridBundle\Grid\Column\GridColumn', 'arguments' => [$name, $label]];
133
                $column['arguments'][] = isset($columnDef['formatter']) ? $columnDef['formatter'] : null;
134
                if (isset($columnDef['sortable'])) {
135
                    $column['arguments'][] = ['sortable' => $columnDef['sortable'] ? true : false];
136
                } else {
137
                    $column['arguments'][] = [];
138
                }
139
                $column['arguments'][] = isset($columnDef['searchable']) ? ($columnDef['searchable'] ? true : false) : false;
140
                $column['arguments'][] = null;
141
                $classes[$class]['columns'][$name] = $column;
142
            }
143
144
            if (isset($info['actions'])) {
145
                $field = '\$-action';
146
                $actionArgs = [$field];
147
                $actionDefs = [];
148
                /* @var Action $action */
149
                foreach ($info['actions'] as $action) {
150
                    if (!isset($action['label'])) {
151
                        throw new Exception("$class - action definition missing 'label' ".print_r($action, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($action, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

151
                        throw new Exception("$class - action definition missing 'label' "./** @scrutinizer ignore-type */ print_r($action, true));
Loading history...
152
                    }
153
                    $actionDef = ['label' => $action['label']];
154
                    if (isset($action['route'])) {
155
                        $actionDef['route'] = $action['route'];
156
                    }
157
                    if (isset($action['onclick'])) {
158
                        $actionDef['onclick'] = $action['onclick'];
159
                    }
160
                    if (isset($action['button_class'])) {
161
                        $actionDef['button_class'] = $action['button_class'];
162
                    }
163
                    $type = '';
164
                    if (isset($action['type'])) {
165
                        $type = $action['type'];
166
                    }
167
                    switch ($type) {
168
                        case 'show':
169
                            $actionDef['action'] = 'show';
170
                            break;
171
                        case 'delete':
172
                            $actionDef['action'] = 'delete';
173
                            break;
174
                        default:
175
                            $actionDef['action'] = 'custom';
176
                    }
177
                    $actionDefs[] = $actionDef;
178
                }
179
                $actionArgs[] = $actionDefs;
180
                $classes[$class]['columns'][$field] = ['class' => '\Dtc\GridBundle\Grid\Column\ActionGridColumn', 'arguments' => $actionArgs];
181
            }
182
            if (isset($info['sort'])) {
183
                foreach ($info['sort'] as $key => $value) {
184
                    if (!isset($info['columns'][$key])) {
185
                        throw new Exception("$class - can't find sort column $key in list of columns.");
186
                    }
187
                    switch ($value) {
188
                        case 'ASC':
189
                            break;
190
                        case 'DESC':
191
                            break;
192
                        default:
193
                            throw new Exception("$class - sort type should be ASC or DESC instead of $value.");
194
                    }
195
                }
196
                $classes[$class]['sort'] = $info['sort'];
197
            }
198
        }
199
200
        return $classes;
201
    }
202
}
203