Passed
Push — master ( c96224...59578f )
by Andreas
03:53
created

classgenerator::write_properties()   C

Complexity

Conditions 15
Paths 32

Size

Total Lines 43
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 15

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 33
c 1
b 0
f 0
nc 32
nop 1
dl 0
loc 43
ccs 26
cts 26
cp 1
crap 15
rs 5.9166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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