Completed
Push — master ( 06aafd...6743f9 )
by Andreas
07:17 queued 03:33
created

manager   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Test Coverage

Coverage 88.15%

Importance

Changes 0
Metric Value
eloc 104
dl 0
loc 221
ccs 119
cts 135
cp 0.8815
rs 8.72
c 0
b 0
f 0
wmc 46

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A get_types() 0 4 1
A get_inherited_mapping() 0 4 1
A register_child_class() 0 6 2
A add_type() 0 20 6
A get_child_classes() 0 7 2
A get_fcqn() 0 6 2
C create_merged_types() 0 46 12
C initialize() 0 41 12
A resolve_targetclass() 0 16 4
A get_type_by_shortname() 0 10 3

How to fix   Complexity   

Complex Class

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.

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 = $this->get_fcqn($property->link['target']);
77
78 15
        if (   array_key_exists($fqcn, $this->types)
79 15
            || $property->link['target'] === $property->get_parent()->name) {
80 15
            $target_class = $property->link['target'];
81 15
        } else {
82
            if (!array_key_exists($property->link['target'], $this->merged_types)) {
83
                throw new \Exception('Link to unknown class ' . $property->link['target']);
84
            }
85
            $target_class = $this->merged_types[$property->link['target']];
86
        }
87 15
        return $target_class;
88
    }
89
90 19
    private function initialize()
91
    {
92 19
        if ($this->types !== null) {
93 18
            return;
94
        }
95 14
        $reader = new xmlreader;
96 14
        $types = $reader->parse(dirname(dirname(__DIR__)) . '/xml/core.xml');
97
98 14
        foreach ($this->schemadirs as $schemadir) {
99 14
            foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename) {
100 14
                if (!file_exists($filename)) {
101
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
102
                    continue;
103
                }
104 14
                $types = array_merge($types, $reader->parse($filename));
105 14
            }
106 14
        }
107
108 14
        $tablemap = [];
109 14
        foreach ($types as $name => $type) {
110 14
            if ($type->parent) {
111 14
                $this->register_child_class($type);
112 14
            }
113
114 14
            if (!array_key_exists($type->table, $tablemap)) {
115 14
                $tablemap[$type->table] = [];
116 14
            }
117 14
            $tablemap[$type->table][] = $type;
118 14
        }
119
120 14
        foreach ($tablemap as $name => $types) {
121 14
            if (count($types) == 1) {
122 14
                $this->add_type($types[0]);
123 14
                unset($tablemap[$name]);
124 14
            }
125 14
        }
126
127
        // We need to process those separately, to be sure the targets for rewriting links are present
128 14
        while ($types = array_pop($tablemap)) {
129 5
            if (!$this->create_merged_types($types)) {
130
                array_push($types, $tablemap);
131
            }
132 5
        }
133 14
    }
134
135 14
    private function register_child_class(type $type)
136
    {
137 14
        if (!array_key_exists($type->parent, $this->child_classes)) {
138 14
            $this->child_classes[$type->parent] = [];
139 14
        }
140 14
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
141 14
    }
142
143
    /**
144
     * This sort of provides a workaround for situations where two tables use the same name
145
     */
146 5
    private function create_merged_types(array $types)
147
    {
148 5
        $root_type = null;
149 5
        foreach ($types as $i => $type) {
150
            // TODO: We should have a second pass here that prefers classnames starting with midgard_
151 5
            if ($type->extends === '\\midgard\\portable\\api\\mgdobject') {
152 5
                $root_type = $type;
153 5
                unset($types[$i]);
154 5
                break;
155
            }
156 5
        }
157 5
        if (empty($root_type)) {
158
            throw new \Exception('could not determine root type of merged group');
159
        }
160
161 5
        foreach ($types as $type) {
162 5
            foreach ($type->get_properties() as $property) {
163 5
                if ($root_type->has_property($property->name)) {
164 5
                    $root_property = $root_type->get_property($property->name);
165 5
                    if ($root_property->field !== $property->field) {
166
                        connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field);
167
                    }
168 5
                    if ($root_property->type !== $property->type) {
169
                        connection::log()->warn('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use type ' . $property->type);
170
                    }
171 5
                    continue;
172
                }
173 5
                $root_type->add_property($property);
174 5
            }
175
176 5
            if (array_key_exists($type->name, $this->child_classes)) {
177 5
                foreach ($this->child_classes[$type->name] as $childname => $parentfield) {
178 5
                    $child_type = $this->get_type_by_shortname($childname);
179 5
                    if ($child_type === null) {
180
                        return false;
181
                    }
182
183 5
                    $child_type->parent = $root_type->name;
184 5
                    $this->register_child_class($child_type);
185 5
                }
186 5
                unset($this->child_classes[$type->name]);
187 5
            }
188 5
            $this->merged_types[$type->name] = $root_type->name;
189 5
        }
190 5
        $this->add_type($root_type);
191 5
        return true;
192
    }
193
194 5
    private function get_type_by_shortname($classname)
195
    {
196 5
        $fqcn = $this->get_fcqn($classname);
197 5
        if (array_key_exists($fqcn, $this->types)) {
198
            return $this->types[$fqcn];
199 5
        } elseif (array_key_exists($classname, $this->merged_types)) {
200 5
            $fqcn = $this->get_fcqn($this->merged_types[$classname]);
201 5
            return $this->types[$fqcn];
202
        }
203
        return null;
204
    }
205
206 14
    private function add_type(type $type)
207
    {
208 14
        $classname = $type->name;
209
        // TODO: This should probably be in classgenerator
210 14
        if ($classname === 'midgard_user') {
211 14
            $type->extends = 'base_user';
212 14
        }
213 14
        if ($classname === 'midgard_person') {
214 14
            $type->extends = 'base_person';
215 14
        }
216 14
        if ($classname === 'midgard_parameter') {
217 14
            $type->extends = 'base_parameter';
218 14
        }
219 14
        if ($classname === 'midgard_repligard') {
220 14
            $type->extends = 'base_repligard';
221 14
        }
222 14
        if ($classname === 'midgard_attachment') {
223 14
            $type->extends = 'base_attachment';
224 14
        }
225 14
        $this->types[$this->get_fcqn($classname)] = $type;
226 14
    }
227
228 17
    private function get_fcqn($classname)
229
    {
230 17
        if (!empty($this->namespace)) {
231 17
            return $this->namespace . '\\' . $classname;
232
        }
233
        return $classname;
234
    }
235
}
236