Passed
Push — master ( f34cf9...9869ed )
by Andreas
03:19
created

subscriber   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Test Coverage

Coverage 77.72%

Importance

Changes 0
Metric Value
eloc 150
dl 0
loc 275
ccs 150
cts 193
cp 0.7772
rs 4.08
c 0
b 0
f 0
wmc 59

9 Methods

Rating   Name   Duplication   Size   Complexity  
A onFlush() 0 15 4
A on_create() 0 30 5
F onSchemaCreateTable() 0 81 24
A onSchemaColumnDefinition() 0 23 5
A getSubscribedEvents() 0 6 1
A postGenerateSchemaTable() 0 9 4
B on_update() 0 45 10
A on_remove() 0 10 3
A calculate_size() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like subscriber 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.

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 subscriber, 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\storage;
9
10
use midgard\portable\storage\interfaces\metadata;
11
use midgard\portable\api\dbobject;
12
use midgard\portable\api\repligard;
13
use midgard\portable\storage\type\datetime;
14
use Doctrine\Common\EventSubscriber;
15
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
16
use Doctrine\ORM\Events;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Doctrine\ORM\Event\OnFlushEventArgs;
19
use Doctrine\DBAL\Schema\Column;
20
use Doctrine\DBAL\Types\Type;
21
use Doctrine\DBAL\Events as dbal_events;
22
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
23
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
24
use Doctrine\ORM\Tools\ToolEvents;
25
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
26
27
class subscriber implements EventSubscriber
28
{
29
    const ACTION_NONE = 0;
30
    const ACTION_DELETE = 1;
31
    const ACTION_PURGE = 2;
32
    const ACTION_CREATE = 3;
33
    const ACTION_UPDATE = 4;
34
35 116
    public function onFlush(OnFlushEventArgs $args)
36
    {
37 116
        $em = $args->getEntityManager();
38 116
        $uow = $em->getUnitOfWork();
39
40 116
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
41 116
            $this->on_create($entity, $em);
42 116
        }
43
44 116
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
45 40
            $this->on_update($entity, $em);
46 116
        }
47
48 116
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
49 18
            $this->on_remove($entity, $em);
50 116
        }
51 116
    }
52
53 116
    private function on_create(dbobject $entity, EntityManagerInterface $em)
54
    {
55 116
        $cm = $em->getClassMetadata(get_class($entity));
56 116
        if (!($entity instanceof repligard)) {
57 116
            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...
58 103
                $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

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