Completed
Push — master ( 2a53f3...5ddc8c )
by Andreas
03:19
created

classgenerator   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 225
Duplicated Lines 4.44 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 95.17%

Importance

Changes 0
Metric Value
dl 10
loc 225
ccs 138
cts 145
cp 0.9517
rs 8.439
c 0
b 0
f 0
wmc 47
lcom 1
cbo 3

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A add_line() 0 9 2
C write() 0 43 8
B register_aliases() 10 19 7
A get_class_prefix() 0 7 2
A convert_type() 0 11 1
C write_properties() 0 44 15
A write_constructor() 0 11 3
A write_parent_getter() 0 18 4
A begin_class() 0 16 3
A end_class() 0 4 1

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 classgenerator 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 classgenerator, 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;
9
10
use midgard\portable\mgdschema\manager;
11
use midgard\portable\mgdschema\type;
12
use midgard\portable\mgdschema\mixin;
13
use midgard\portable\mgdschema\property;
14
use midgard\portable\mgdschema\translator;
15
16
class classgenerator
17
{
18
    /**
19
     *
20
     * @var string
21
     */
22
    private $output;
23
24
    /**
25
     *
26
     * @var string
27
     */
28
    private $filename;
29
30
    /**
31
     *
32
     * @var manager
33
     */
34
    private $manager;
35
36
    /**
37
     *
38
     * @var boolean
39
     */
40
    private $dev_mode = false;
41
42 11
    public function __construct(manager $manager, $filename, $dev_mode = false)
43
    {
44 11
        $this->manager = $manager;
45 11
        $this->filename = $filename;
46 11
        $this->dev_mode = $dev_mode;
47 11
    }
48
49 11
    private function add_line($line)
50
    {
51 11
        $this->output .= $line;
52 11
        if ($this->dev_mode) {
53 10
            $this->output .= "\n";
54 10
        } else {
55 1
            $this->output .= ' ';
56
        }
57 11
    }
58
59 11
    public function write($namespace = '')
60
    {
61 11
        if (file_exists($this->filename)) {
62 9
            unlink($this->filename);
63 9
        }
64
65 11
        $types = $this->manager->get_types();
66
        uasort($types, function ($a, $b) {
67 11
            if (   !empty($a->extends)
68 11
                && !empty($b->extends)) {
69 11
                return strnatcmp($a->extends, $b->extends);
70
            } elseif (!empty($a->extends)) {
71
                return -1;
72
            } elseif (!empty($b->extends)) {
73
                return 1;
74
            }
75
            return 0;
76 11
        });
77
78 11
        $this->add_line('<?php');
79
80 11
        if (!empty($namespace)) {
81 11
            $this->add_line('namespace ' . $namespace . ';');
82 11
            $this->add_line('use midgard\\portable\\api\\object as midgard_object;');
83 11
            $this->add_line('use midgard_metadata;');
84 11
            $this->add_line('use midgard_datetime;');
85 11
        }
86 11
        $this->add_line('use midgard\\portable\\api\\user as base_user;');
87 11
        $this->add_line('use midgard\\portable\\api\\person as base_person;');
88 11
        $this->add_line('use midgard\\portable\\api\\parameter as base_parameter;');
89 11
        $this->add_line('use midgard\\portable\\api\\repligard as base_repligard;');
90 11
        $this->add_line('use midgard\\portable\\api\\attachment as base_attachment; ');
91
92 11
        foreach ($types as $type) {
93 11
            $this->convert_type($type);
94 11
        }
95
96 11
        $this->register_aliases($namespace);
97
98
        //todo: midgard_blob special handling
99
100 11
        file_put_contents($this->filename, $this->output);
101 11
    }
102
103 11
    private function register_aliases($namespace)
104
    {
105 11
        $prefix = $this->get_class_prefix($namespace);
106
107 11
        foreach ($this->manager->get_types() as $type) {
108 View Code Duplication
            if (   $prefix !== ''
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...
109 11
                && !class_exists($type->name)) {
110 1
                $this->add_line('class_alias( "' . $prefix . $type->name . '", "' . $type->name . '");');
111 1
            }
112 11
        }
113
114 11
        foreach ($this->manager->get_inherited_mapping() as $child => $parent) {
115 5
            $this->add_line('class_alias( "' . $prefix . $parent . '", "' . $prefix . $child . '");');
116 View Code Duplication
            if (   $prefix !== ''
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...
117 5
                && !class_exists($child)) {
118 1
                $this->add_line('class_alias( "' . $prefix . $parent . '", "' . $child . '");');
119 1
            }
120 11
        }
121 11
    }
122
123 11
    private function get_class_prefix($namespace)
124
    {
125 11
        if ($namespace === '') {
126
            return '';
127
        }
128 11
        return str_replace('\\', '\\\\', $namespace) . '\\\\';
129
    }
130
131 11
    private function convert_type(type $type)
132
    {
133 11
        $this->begin_class($type);
134 11
        $objects = $this->write_properties($type);
135
136 11
        $this->write_constructor($objects);
137
138 11
        $this->write_parent_getter($type);
139
140 11
        $this->end_class();
141 11
    }
142
143 11
    private function write_properties(type $type)
144
    {
145 11
        $objects = array();
146
147 11
        foreach ($type->get_properties() as $name => $property) {
148 11
            if ($name == 'guid') {
149 11
                continue;
150
            }
151 11
            $line = ' protected $' . $name;
152 11
            $default = null;
153 11
            switch (translator::to_constant($property->type)) {
154 11
                case translator::TYPE_BOOLEAN:
155 11
                    $default = 'false';
156 11
                    break;
157 11
                case translator::TYPE_FLOAT:
158 9
                    $default = '0.0';
159 9
                    break;
160 11
                case translator::TYPE_UINT:
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
161 11
                    if ($name == $type->primaryfield) {
162
                        // no default value for identifier, because otherwise, Doctrine will think it's a detached entity
163 11
                        break;
164
                    }
165 11
                case translator::TYPE_INT:
166 11
                    $default = '0';
167 11
                    break;
168 11
                case translator::TYPE_GUID:
169 11
                case translator::TYPE_STRING:
170 11
                case translator::TYPE_LONGTEXT:
171 11
                    $default = "''";
172 11
                    break;
173 11
                case translator::TYPE_TIMESTAMP:
174 11
                    $objects[$name] = 'new midgard_datetime("0001-01-01 00:00:00")';
175 11
                    break;
176 11
            }
177
            if (   $default !== null
178
                   // we need to skip working links because in this case, Doctrine expects objects as values
179 11
                && (   !$property->link
180 11
                    || $this->manager->resolve_targetclass($property) === false)) {
181 11
                $line .= ' = ' . $default;
182 11
            }
183 11
            $this->add_line($line . ';');
184 11
        }
185 11
        return $objects;
186
    }
187
188 11
    private function write_constructor(array $objects)
189
    {
190 11
        if (!empty($objects)) {
191 11
            $this->add_line('public function __construct($id = null) {');
192 11
            foreach ($objects as $name => $code) {
193 11
                $this->add_line('$this->' . $name . ' = ' . $code . ';');
194 11
            }
195 11
            $this->add_line('parent::__construct($id);');
196 11
            $this->add_line('}');
197 11
        }
198 11
    }
199
200 11
    private function write_parent_getter($type)
201
    {
202 11
        $candidates = array();
203
204 11
        if (!empty($type->upfield)) {
205 11
            $candidates[] = $type->upfield;
206 11
        }
207 11
        if (!empty($type->parentfield)) {
208 11
            $candidates[] = $type->parentfield;
209 11
        }
210 11
        if (empty($candidates)) {
211 11
            return;
212
        }
213
214 11
        $this->add_line('public function get_parent() {');
215 11
        $this->add_line(' return $this->load_parent(' . var_export($candidates, true) . ');');
216 11
        $this->add_line('}');
217 11
    }
218
219 11
    private function begin_class(type $type)
220
    {
221 11
        $this->add_line('class ' . $type->name . ' extends ' . $type->extends);
222 11
        $mixins = $type->get_mixins();
223 11
        $interfaces = array_filter(array_map(function ($name) {
224 11
            if (interface_exists('\\midgard\\portable\\storage\\interfaces\\' . $name)) {
225 11
                return '\\midgard\\portable\\storage\\interfaces\\' . $name;
226
            }
227
            return false;
228 11
        }, array_keys($mixins)));
229
230 11
        if (count($interfaces) > 0) {
231 11
            $this->add_line(' implements ' . implode(', ', $interfaces));
232 11
        }
233 11
        $this->add_line('{');
234 11
    }
235
236 11
    private function end_class()
237
    {
238 11
        $this->add_line('}');
239 11
    }
240
}
241