Completed
Push — master ( c45663...914a98 )
by Andreas
05:24
created

subscriber   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Test Coverage

Coverage 76.88%

Importance

Changes 0
Metric Value
eloc 145
dl 0
loc 267
ccs 143
cts 186
cp 0.7688
rs 5.04
c 0
b 0
f 0
wmc 57

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