Completed
Push — master ( 12c3f6...3ce342 )
by Andreas
05:20
created

manager   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 227
Duplicated Lines 3.52 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 89.51%

Importance

Changes 0
Metric Value
dl 8
loc 227
ccs 128
cts 143
cp 0.8951
rs 8.4864
c 0
b 0
f 0
wmc 48
lcom 1
cbo 5

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A get_types() 0 5 1
A get_inherited_mapping() 0 5 1
A get_child_classes() 0 8 2
D initialize() 0 44 12
A register_child_class() 0 7 2
C create_merged_types() 8 47 12
C add_type() 0 24 7
B resolve_targetclass() 0 20 5
B get_type_by_shortname() 0 17 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like manager 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 manager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
6
 */
7
8
namespace midgard\portable\mgdschema;
9
10
use midgard\portable\xmlreader;
11
use midgard\portable\storage\connection;
12
13
class manager
14
{
15
    /**
16
     *
17
     * @var array
18
     */
19
    private $types;
20
21
    /**
22
     *
23
     * @var array
24
     */
25
    private $schemadirs;
26
27
    /**
28
     *
29
     * @var string
30
     */
31
    private $namespace;
32
33
    /**
34
     *
35
     * @var array
36
     */
37
    private $merged_types = [];
38
39
    /**
40
     *
41
     * @var array
42
     */
43
    private $child_classes = [];
44
45 14
    public function __construct(array $schemadirs, $namespace)
46
    {
47 14
        $this->schemadirs = $schemadirs;
48 14
        $this->namespace = $namespace;
49 14
    }
50
51 16
    public function get_types()
52
    {
53 16
        $this->initialize();
54 16
        return $this->types;
55
    }
56
57 11
    public function get_inherited_mapping()
58
    {
59 11
        $this->initialize();
60 11
        return $this->merged_types;
61
    }
62
63 14
    public function get_child_classes($typename)
64
    {
65 14
        $this->initialize();
66 14
        if (array_key_exists($typename, $this->child_classes)) {
67 9
            return $this->child_classes[$typename];
68
        }
69 11
        return [];
70
    }
71
72 15
    public function resolve_targetclass(property $property)
73
    {
74 15
        $this->initialize();
75
76 15
        $fqcn = $property->link['target'];
77 15
        if (!empty($this->namespace)) {
78 15
            $fqcn = $this->namespace . '\\' . $fqcn;
79 15
        }
80
81 15
        if (   array_key_exists($fqcn, $this->types)
82 15
            || $property->link['target'] === $property->get_parent()->name) {
83 15
            $target_class = $property->link['target'];
84 15
        } else {
85
            if (!array_key_exists($property->link['target'], $this->merged_types)) {
86
                throw new \Exception('Link to unknown class ' . $property->link['target']);
87
            }
88
            $target_class = $this->merged_types[$property->link['target']];
89
        }
90 15
        return $target_class;
91
    }
92
93 19
    private function initialize()
94
    {
95 19
        if ($this->types !== null) {
96 18
            return;
97
        }
98 14
        $reader = new xmlreader;
99 14
        $types = $reader->parse(dirname(dirname(__DIR__)) . '/xml/core.xml');
100
101 14
        foreach ($this->schemadirs as $schemadir) {
102 14
            foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename) {
103 14
                if (!file_exists($filename)) {
104
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
105
                    continue;
106
                }
107 14
                $types = array_merge($types, $reader->parse($filename));
108 14
            }
109 14
        }
110
111 14
        $tablemap = [];
112 14
        foreach ($types as $name => $type) {
113 14
            if ($type->parent) {
114 14
                $this->register_child_class($type);
115 14
            }
116
117 14
            if (!array_key_exists($type->table, $tablemap)) {
118 14
                $tablemap[$type->table] = [];
119 14
            }
120 14
            $tablemap[$type->table][] = $type;
121 14
        }
122
123 14
        foreach ($tablemap as $name => $types) {
124 14
            if (count($types) == 1) {
125 14
                $this->add_type($types[0]);
126 14
                unset($tablemap[$name]);
127 14
            }
128 14
        }
129
130
        // We need to process those separately, to be sure the targets for rewriting links are present
131 14
        while ($types = array_pop($tablemap)) {
132 5
            if (!$this->create_merged_types($types)) {
133
                array_push($types, $tablemap);
134
            }
135 5
        }
136 14
    }
137
138 14
    private function register_child_class(type $type)
139
    {
140 14
        if (!array_key_exists($type->parent, $this->child_classes)) {
141 14
            $this->child_classes[$type->parent] = [];
142 14
        }
143 14
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
144 14
    }
145
146
    /**
147
     * This sort of provides a workaround for situations where two tables use the same name
148
     */
149 5
    private function create_merged_types(array $types)
150
    {
151 5
        $root_type = null;
152 5
        foreach ($types as $i => $type) {
153
            // TODO: We should have a second pass here that prefers classnames starting with midgard_
154 5
            if ($type->extends === '\\midgard\\portable\\api\\object') {
155 5
                $root_type = $type;
156 5
                unset($types[$i]);
157 5
                break;
158
            }
159 5
        }
160 5
        if (empty($root_type)) {
161
            throw new \Exception('could not determine root type of merged group');
162
        }
163
164 5
        foreach ($types as $type) {
165 5
            foreach ($type->get_properties() as $property) {
166 5
                if ($root_type->has_property($property->name)) {
167 5
                    $root_property = $root_type->get_property($property->name);
168 5 View Code Duplication
                    if ($root_property->field !== $property->field) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
169
                        connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field);
170
                    }
171 5 View Code Duplication
                    if ($root_property->type !== $property->type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
                        connection::log()->warn('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use type ' . $property->type);
173
                    }
174 5
                    continue;
175
                }
176 5
                $root_type->add_property($property);
177 5
            }
178
179 5
            if (array_key_exists($type->name, $this->child_classes)) {
180 5
                foreach ($this->child_classes[$type->name] as $childname => $parentfield) {
181 5
                    $child_type = $this->get_type_by_shortname($childname);
182 5
                    if ($child_type === null) {
183
                        return false;
184
                    }
185
186 5
                    $child_type->parent = $root_type->name;
187 5
                    $this->register_child_class($child_type);
188 5
                }
189 5
                unset($this->child_classes[$type->name]);
190 5
            }
191 5
            $this->merged_types[$type->name] = $root_type->name;
192 5
        }
193 5
        $this->add_type($root_type);
194 5
        return true;
195
    }
196
197 5
    private function get_type_by_shortname($classname)
198
    {
199 5
        $fqcn = $classname;
200 5
        if (!empty($this->namespace)) {
201 5
            $fqcn = $this->namespace . '\\' . $classname;
202 5
        }
203 5
        if (array_key_exists($fqcn, $this->types)) {
204
            return $this->types[$fqcn];
205 5
        } elseif (array_key_exists($classname, $this->merged_types)) {
206 5
            $fqcn = $this->merged_types[$classname];
207 5
            if (!empty($this->namespace)) {
208 5
                $fqcn = $this->namespace . '\\' . $fqcn;
209 5
            }
210 5
            return $this->types[$fqcn];
211
        }
212
        return null;
213
    }
214
215 14
    private function add_type(type $type)
216
    {
217 14
        $classname = $type->name;
218
        // TODO: This should probably be in classgenerator
219 14
        if ($classname === 'midgard_user') {
220 14
            $type->extends = 'base_user';
221 14
        }
222 14
        if ($classname === 'midgard_person') {
223 14
            $type->extends = 'base_person';
224 14
        }
225 14
        if ($classname === 'midgard_parameter') {
226 14
            $type->extends = 'base_parameter';
227 14
        }
228 14
        if ($classname === 'midgard_repligard') {
229 14
            $type->extends = 'base_repligard';
230 14
        }
231 14
        if ($classname === 'midgard_attachment') {
232 14
            $type->extends = 'base_attachment';
233 14
        }
234 14
        if (!empty($this->namespace)) {
235 14
            $classname = $this->namespace . '\\' . $classname;
236 14
        }
237 14
        $this->types[$classname] = $type;
238 14
    }
239
}
240