Passed
Branch master (056094)
by Andreas
04:47
created

classgenerator::write()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 8.2685

Importance

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