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

subscriber   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 251
Duplicated Lines 0 %

Test Coverage

Coverage 76.88%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 251
ccs 133
cts 173
cp 0.7688
rs 6.8539
wmc 54

9 Methods

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