Passed
Push — master ( 0d88a1...fded30 )
by Andreas
14:17
created

subscriber::onSchemaCreateTable()   F

Complexity

Conditions 22
Paths 899

Size

Total Lines 72
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 60.1067

Importance

Changes 6
Bugs 1 Features 0
Metric Value
cc 22
eloc 41
c 6
b 1
f 0
nc 899
nop 1
dl 0
loc 72
ccs 24
cts 42
cp 0.5714
crap 60.1067
rs 0.1402

How to fix   Long Method    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\storage;
9
10
use midgard\portable\storage\interfaces\metadata;
11
use midgard\portable\api\dbobject;
12
use midgard\portable\api\repligard;
13
use Doctrine\Common\EventSubscriber;
14
use Doctrine\Persistence\Mapping\ClassMetadata;
15
use Doctrine\ORM\Events;
16
use Doctrine\ORM\EntityManagerInterface;
17
use Doctrine\ORM\Event\OnFlushEventArgs;
18
use Doctrine\DBAL\Schema\Column;
19
use Doctrine\DBAL\Types\Type;
20
use Doctrine\DBAL\Events as dbal_events;
21
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
22
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
23
use Doctrine\ORM\Tools\ToolEvents;
24
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
25
use Doctrine\DBAL\Types\Types;
26
use Doctrine\Persistence\Event\LifecycleEventArgs;
27
use Doctrine\DBAL\Platforms\SqlitePlatform;
0 ignored issues
show
Bug introduced by
The type Doctrine\DBAL\Platforms\SqlitePlatform was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
29
30
class subscriber implements EventSubscriber
31
{
32
    const ACTION_NONE = 0;
33
    const ACTION_DELETE = 1;
34
    const ACTION_PURGE = 2;
35
    const ACTION_CREATE = 3;
36
    const ACTION_UPDATE = 4;
37
38 115
    public function onFlush(OnFlushEventArgs $args)
39
    {
40 115
        $em = $args->getObjectManager();
41 115
        $uow = $em->getUnitOfWork();
0 ignored issues
show
Bug introduced by
The method getUnitOfWork() does not exist on Doctrine\Persistence\ObjectManager. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Persistence\ObjectManagerDecorator. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

41
        /** @scrutinizer ignore-call */ 
42
        $uow = $em->getUnitOfWork();
Loading history...
42
43 115
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
44 115
            $this->on_create($entity, $em);
45
        }
46
47 115
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
48 39
            $this->on_update($entity, $em);
49
        }
50
51 115
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
52 17
            $this->on_remove($entity, $em);
53
        }
54
    }
55
56 81
    public function postLoad(LifecycleEventArgs $args)
57
    {
58 81
        $entity = $args->getObject();
59 81
        if ($entity instanceof dbobject) {
60 81
            $om = $args->getObjectManager();
61 81
            $entity->injectObjectManager($om, $om->getClassMetadata(get_class($entity)));
62
        }
63
    }
64
65 115
    private function on_create(dbobject $entity, EntityManagerInterface $em)
66
    {
67 115
        $cm = $em->getClassMetadata(get_class($entity));
68 115
        if (!($entity instanceof repligard)) {
69 115
            if (empty($entity->guid)) {
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
70 102
                $entity->set_guid(connection::generate_guid());
0 ignored issues
show
Bug introduced by
The method set_guid() does not exist on midgard\portable\api\dbobject. It seems like you code against a sub-type of midgard\portable\api\dbobject such as midgard\portable\api\mgdobject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

70
                $entity->/** @scrutinizer ignore-call */ 
71
                         set_guid(connection::generate_guid());
Loading history...
71 102
                $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
72
            }
73
74 115
            $om = new objectmanager($em);
75 115
            $repligard_cm = $em->getClassMetadata(connection::get_fqcn('midgard_repligard'));
76 115
            $repligard_entry = $om->new_instance($repligard_cm->getName());
77 115
            $repligard_entry->typename = $cm->getReflectionClass()->getShortName();
0 ignored issues
show
Bug Best Practice introduced by
The property typename does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
78 115
            $repligard_entry->guid = $entity->guid;
79 115
            $repligard_entry->object_action = self::ACTION_CREATE;
0 ignored issues
show
Bug Best Practice introduced by
The property object_action does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
80 115
            $em->persist($repligard_entry);
81 115
            $em->getUnitOfWork()->computeChangeSet($repligard_cm, $repligard_entry);
82
        }
83
84 115
        if ($entity instanceof metadata) {
85 97
            $entity->metadata->created = new \midgard_datetime();
0 ignored issues
show
Bug Best Practice introduced by
The property metadata does not exist on midgard\portable\api\repligard. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property metadata does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
86
            // we copy here instead of creating a new, because otherwise we might have
87
            // a one second difference if the code runs at the right millisecond
88 97
            $entity->metadata->revised = $entity->metadata->created;
89 97
            if ($user = connection::get_user()) {
90 7
                $entity->metadata_creator = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_creator does not exist on midgard\portable\api\repligard. Since you implemented __set, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property metadata_creator does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
91 7
                $entity->metadata_revisor = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revisor does not exist on midgard\portable\api\repligard. Since you implemented __set, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property metadata_revisor does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
92
            }
93 97
            $entity->metadata->size = $this->calculate_size($cm, $entity);
94 97
            $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
95
        }
96
    }
97
98 39
    private function on_update(dbobject $entity, EntityManagerInterface $em)
99
    {
100 39
        if ($entity instanceof repligard) {
101
            return;
102
        }
103 39
        $check_repligard = true;
104 39
        $deleted = false;
105 39
        if ($entity instanceof metadata) {
106 38
            $deleted = $entity->{metadata::DELETED_FIELD};
107 38
            $cs = $em->getUnitOfWork()->getEntityChangeSet($entity);
108
            // We only need to update repligard if we're coming from create (revision 0)
109
            // or if we delete/undelete
110 38
            if (   !array_key_exists('metadata_deleted', $cs)
111 38
                && $entity->metadata_revision > 0)  {
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revision does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
112 4
                $check_repligard = false;
113
            }
114
115 38
            $create_revision = true;
116 38
            if (array_key_exists('metadata_islocked', $cs)) {
117 2
                $lock_fields = array_flip(['metadata_locked', 'metadata_islocked', 'metadata_locker']);
118 2
                $create_revision = !empty(array_diff_key($cs, $lock_fields));
119
            }
120
121 38
            if ($create_revision) {
122 36
                $cm = $em->getClassMetadata(get_class($entity));
123 36
                $entity->metadata_revised = new \midgard_datetime();
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revised does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
124 36
                $entity->metadata_revision++;
125 36
                if ($user = connection::get_user()) {
126 5
                    $entity->metadata_revisor = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revisor does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
127
                }
128 36
                $entity->metadata->size = $this->calculate_size($cm, $entity);
0 ignored issues
show
Bug Best Practice introduced by
The property metadata does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
129 36
                $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
130
            }
131
        }
132
133 39
        if ($check_repligard) {
134 39
            $repligard_entry = $em->getRepository(connection::get_fqcn('midgard_repligard'))->findOneBy(['guid' => $entity->guid]);
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
135
136 39
            if ($deleted) {
137 26
                $repligard_entry->object_action = self::ACTION_DELETE;
138
            } else {
139 21
                $repligard_entry->object_action = self::ACTION_UPDATE;
140
            }
141 39
            $em->persist($repligard_entry);
142 39
            $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata(connection::get_fqcn('midgard_repligard')), $repligard_entry);
143
        }
144
    }
145
146 17
    private function on_remove(dbobject $entity, EntityManagerInterface $em)
147
    {
148 17
        if (!($entity instanceof repligard)) {
149 17
            $repligard_entry = $em->getRepository(connection::get_fqcn('midgard_repligard'))->findOneBy(['guid' => $entity->guid]);
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
150 17
            if (empty($repligard_entry)) {
151
                connection::log()->error('No repligard entry found for GUID ' . $entity->guid);
152
            } else {
153 17
                $repligard_entry->object_action = self::ACTION_PURGE;
154 17
                $em->persist($repligard_entry);
155 17
                $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata(connection::get_fqcn('midgard_repligard')), $repligard_entry);
156
            }
157
        }
158
    }
159
160 97
    private function calculate_size(ClassMetadata $cm, metadata $entity) : int
161
    {
162 97
        $size = 0;
163 97
        foreach ($cm->getAssociationNames() as $name) {
164 93
            $size += strlen($entity->$name);
165
        }
166 97
        foreach ($cm->getFieldNames() as $name) {
167 97
            $size += strlen($entity->$name);
168
        }
169 97
        return $size;
170
    }
171
172 6
    public function onSchemaCreateTable(SchemaCreateTableEventArgs $args)
173
    {
174 6
        $platform = $args->getPlatform();
175 6
        $columns = $args->getColumns();
176 6
        $modified = false;
177
178 6
        foreach ($columns as $name => &$config) {
179 6
            if ($platform instanceof SqlitePlatform) {
180 6
                if (   !empty($config['comment'])
181 6
                    && $config['comment'] == 'BINARY') {
182 1
                    $modified = true;
183 1
                    $config['columnDefinition'] = $config['type']->getSQLDeclaration($config, $platform) . ' COLLATE BINARY' . $platform->getDefaultValueDeclarationSQL($config);
184
                }
185
            }
186 6
            if ($platform instanceof AbstractMySQLPlatform) {
187
                if (!empty($config['comment'])) {
188
                    if ($config['comment'] == 'BINARY') {
189
                        $modified = true;
190
                        $config['columnDefinition'] = $config['type']->getSQLDeclaration($config, $platform) . ' CHARACTER SET utf8 COLLATE utf8_bin' . $platform->getDefaultValueDeclarationSQL($config);
191
                    }
192
                    if (substr(strtolower(trim($config['comment'])), 0, 3) == 'set') {
193
                        $modified = true;
194
                        $config['columnDefinition'] = $config['comment'] . $platform->getDefaultValueDeclarationSQL($config);
195
                    }
196
                    if (!empty($config['columnDefinition']) && $platform->supportsInlineColumnComments()) {
197
                        $config['columnDefinition'] .=  " COMMENT " . $platform->quoteStringLiteral($config['comment']);
198
                    }
199
                }
200
            }
201
        }
202
203 6
        if (!$modified) {
204 5
            return;
205
        }
206
207 1
        $args->preventDefault();
208
209
        //The following is basically copied from the respective Doctrine function, since there seems to be no way
210
        //to just modify columns and pass them back to the SchemaManager
211 1
        $table = $args->getTable();
212 1
        $options = $args->getOptions();
213
214 1
        $queryFields = $platform->getColumnDeclarationListSQL($columns);
215
216 1
        if (!empty($options['uniqueConstraints'])) {
217
            foreach ($options['uniqueConstraints'] as $name => $definition) {
218
                $queryFields .= ', ' . $platform->getUniqueConstraintDeclarationSQL($name, $definition);
219
            }
220
        }
221
222 1
        if (!empty($options['foreignKeys'])) {
223
            foreach ($options['foreignKeys'] as $foreignKey) {
224
                $queryFields .= ', ' . $platform->getForeignKeyDeclarationSQL($foreignKey);
225
            }
226
        }
227
228 1
        $name = str_replace('.', '__', $table->getName());
229 1
        $args->addSql('CREATE TABLE ' . $name . ' (' . $queryFields . ')');
230
231 1
        if (isset($options['alter']) && true === $options['alter']) {
232
            return;
233
        }
234
235 1
        if (!empty($options['indexes'])) {
236
            foreach ($options['indexes'] as $indexDef) {
237
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
238
            }
239
        }
240
241 1
        if (!empty($options['unique'])) {
242
            foreach ($options['unique'] as $indexDef) {
243
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
244
            }
245
        }
246
    }
247
248
    /**
249
     * This function contains workarounds for reading existing Midgard databases
250
     *
251
     * ENUM fields are converted to string for now (Like in the XML reader)
252
     */
253 2
    public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $args)
254
    {
255 2
        $column = array_change_key_case($args->getTableColumn(), CASE_LOWER);
256 2
        $type = strtok($column['type'], '()');
257
258 2
        if ($type == 'enum') {
259
            $options = [
260
                'length' => 255,
261
                'default' => $column['default'] ?? null,
262
                'notnull' => $column['null'] != 'YES',
263
                'comment' => $column['type']
264
            ];
265
266
            $args->preventDefault();
267
            $args->setColumn(new Column($column['field'], Type::getType(Types::STRING), $options));
268
        }
269
    }
270
271
    /**
272
     * This is mostly a workaround for the fact that SchemaTool wants to create FKs on
273
     * each run since it doesn't detect that MyISAM tables don't support them
274
     *
275
     * @see https://github.com/doctrine/orm/issues/4270
276
     * @param GenerateSchemaTableEventArgs $args
277
     */
278 4
    public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $args)
279
    {
280 4
        $table = $args->getClassTable();
281 4
        if (   !$table->hasOption('engine')
282 4
            || $table->getOption('engine') !== 'MyISAM') {
283
            return;
284
        }
285 4
        foreach ($table->getForeignKeys() as $key) {
286 3
            $table->removeForeignKey($key->getName());
287
        }
288
    }
289
290 9
    public function getSubscribedEvents()
291
    {
292 9
        return [
293 9
            Events::postLoad,
294 9
            Events::onFlush,
295 9
            dbal_events::onSchemaCreateTable, dbal_events::onSchemaColumnDefinition,
0 ignored issues
show
introduced by
The constant Doctrine\DBAL\Events::onSchemaCreateTable has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

295
            /** @scrutinizer ignore-deprecated */ dbal_events::onSchemaCreateTable, dbal_events::onSchemaColumnDefinition,
Loading history...
introduced by
The constant Doctrine\DBAL\Events::onSchemaColumnDefinition has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

295
            dbal_events::onSchemaCreateTable, /** @scrutinizer ignore-deprecated */ dbal_events::onSchemaColumnDefinition,
Loading history...
296 9
            ToolEvents::postGenerateSchemaTable
297 9
        ];
298
    }
299
}
300