Completed
Push — master ( 976c63...17f8b4 )
by Andreas
03:12
created

classgenerator::write_annotations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

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