Completed
Push — master ( 12c3f6...3ce342 )
by Andreas
05:20
created

subscriber   F

Complexity

Total Complexity 55

Size/Duplication

Total Lines 256
Duplicated Lines 9.77 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 77.84%

Importance

Changes 9
Bugs 2 Features 1
Metric Value
c 9
b 2
f 1
dl 25
loc 256
ccs 137
cts 176
cp 0.7784
rs 2.9203
wmc 55
lcom 1
cbo 18

9 Methods

Rating   Name   Duplication   Size   Complexity  
A onFlush() 0 17 4
B on_update() 0 26 6
A on_remove() 0 9 2
A calculate_size() 0 11 3
B on_create() 0 32 5
F onSchemaCreateTable() 24 86 25
B onSchemaColumnDefinition() 0 26 5
A postGenerateSchemaTable() 0 11 4
A getSubscribedEvents() 0 8 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 114
    public function onFlush(OnFlushEventArgs $args)
36
    {
37 114
        $em = $args->getEntityManager();
38 114
        $uow = $em->getUnitOfWork();
39
40 114
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
41 114
            $this->on_create($entity, $em);
42 114
        }
43
44 114
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
45 39
            $this->on_update($entity, $em);
46 114
        }
47
48 114
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
49 18
            $this->on_remove($entity, $em);
50 114
        }
51 114
    }
52
53 114
    private function on_create(dbobject $entity, EntityManagerInterface $em)
54
    {
55 114
        $cm = $em->getClassMetadata(get_class($entity));
56 114
        if (!($entity instanceof repligard)) {
57 114
            if (empty($entity->guid)) {
0 ignored issues
show
Documentation introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
58 101
                $entity->set_guid(connection::generate_guid());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class midgard\portable\api\dbobject as the method set_guid() does only exist in the following sub-classes of midgard\portable\api\dbobject: midgard\portable\api\attachment, midgard\portable\api\object, midgard\portable\api\parameter, midgard\portable\api\person. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
59 101
                $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
0 ignored issues
show
Compatibility introduced by
$cm of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
60 101
            }
61
62 114
            $om = new objectmanager($em);
0 ignored issues
show
Compatibility introduced by
$em of type object<Doctrine\ORM\EntityManagerInterface> is not a sub-type of object<Doctrine\ORM\EntityManager>. It seems like you assume a concrete implementation of the interface Doctrine\ORM\EntityManagerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
63 114
            $repligard_cm = $em->getClassMetadata('midgard:midgard_repligard');
64 114
            $repligard_entry = $om->new_instance($repligard_cm->getName());
65 114
            $repligard_entry->typename = $cm->getReflectionClass()->getShortName();
0 ignored issues
show
Documentation introduced by
The property typename does not exist on object<midgard\portable\api\dbobject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
66 114
            $repligard_entry->guid = $entity->guid;
0 ignored issues
show
Documentation introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implemented __set(), maybe consider adding a @property or @property-write annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
67 114
            $repligard_entry->object_action = self::ACTION_CREATE;
0 ignored issues
show
Documentation introduced by
The property object_action does not exist on object<midgard\portable\api\dbobject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
68 114
            $em->persist($repligard_entry);
69 114
            $em->getUnitOfWork()->computeChangeSet($repligard_cm, $repligard_entry);
0 ignored issues
show
Compatibility introduced by
$repligard_cm of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
70 114
        }
71
72 114
        if ($entity instanceof metadata) {
73 96
            $entity->metadata->created = new \midgard_datetime();
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 96
            $entity->metadata->revised = $entity->metadata->created;
77 96
            if ($user = connection::get_user()) {
78 7
                $entity->metadata_creator = $user->person;
0 ignored issues
show
Documentation introduced by
The property $person is declared protected in midgard\portable\api\user. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
79 7
                $entity->metadata_revisor = $user->person;
0 ignored issues
show
Documentation introduced by
The property $person is declared protected in midgard\portable\api\user. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
80 7
            }
81 96
            $entity->metadata->size = $this->calculate_size($cm, $entity);
82 96
            $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
0 ignored issues
show
Compatibility introduced by
$cm of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
83 96
        }
84 114
    }
85
86 39
    private function on_update(dbobject $entity, EntityManagerInterface $em)
87
    {
88 39
        if ($entity instanceof metadata) {
89 38
            $cm = $em->getClassMetadata(get_class($entity));
90 38
            $entity->metadata_revised = new \midgard_datetime();
91 38
            $entity->metadata_revision++;
92 38
            if ($user = connection::get_user()) {
93 7
                $entity->metadata_revisor = $user->person;
0 ignored issues
show
Documentation introduced by
The property $person is declared protected in midgard\portable\api\user. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
94 7
            }
95 38
            $entity->metadata->size = $this->calculate_size($cm, $entity);
96 38
            $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
0 ignored issues
show
Compatibility introduced by
$cm of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
97 38
        }
98
99 39
        if (!($entity instanceof repligard)) {
100 39
            $repligard_entry = $em->getRepository('midgard:midgard_repligard')->findOneBy(['guid' => $entity->guid]);
0 ignored issues
show
Documentation introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
101
102
            if (   $entity instanceof metadata
103 39
                && $entity->{metadata::DELETED_FIELD}) {
104 26
                $repligard_entry->object_action = self::ACTION_DELETE;
105 26
            } else {
106 21
                $repligard_entry->object_action = self::ACTION_UPDATE;
107
            }
108 39
            $em->persist($repligard_entry);
109 39
            $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata('midgard:midgard_repligard'), $repligard_entry);
0 ignored issues
show
Compatibility introduced by
$em->getClassMetadata('m...ard:midgard_repligard') of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
110 39
        }
111 39
    }
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
Documentation introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

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);
0 ignored issues
show
Compatibility introduced by
$em->getClassMetadata('m...ard:midgard_repligard') of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
120 18
        }
121 18
    }
122
123 96
    private function calculate_size(ClassMetadata $cm, metadata $entity)
124
    {
125 96
        $size = 0;
126 96
        foreach ($cm->getAssociationNames() as $name) {
127 92
            $size += strlen($entity->$name);
128 96
        }
129 96
        foreach ($cm->getFieldNames() as $name) {
130 96
            $size += strlen($entity->$name);
131 96
        }
132 96
        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 View Code Duplication
                if (   !empty($config['comment'])
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...
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 View Code Duplication
                    if ($config['comment'] == 'BINARY') {
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...
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
                }
169
            }
170 7
            if (   !empty($config['columnDefinition'])
171 7
                && !empty($config['comment'])
172 7
                && $platform->supportsInlineColumnComments()) {
173
                $config['columnDefinition'] .=  " COMMENT " . $platform->quoteStringLiteral($config['comment']);
174
            }
175 7
        }
176
177 7
        if (!$modified) {
178 4
            return;
179
        }
180
181 6
        $args->preventDefault();
182
183
        //The following is basically copied from the respective Doctrine function, since there seems to be no way
184
        //to just modify columns and pass them back to the SchemaManager
185 6
        $table = $args->getTable();
186 6
        $options = $args->getOptions();
187
188 6
        $queryFields = $platform->getColumnDeclarationListSQL($columns);
189
190 6
        if (!empty($options['uniqueConstraints'])) {
191
            foreach ($options['uniqueConstraints'] as $name => $definition) {
192
                $queryFields .= ', ' . $platform->getUniqueConstraintDeclarationSQL($name, $definition);
193
            }
194
        }
195
196 6
        if (!empty($options['foreignKeys'])) {
197
            foreach ($options['foreignKeys'] as $foreignKey) {
198
                $queryFields .= ', ' . $platform->getForeignKeyDeclarationSQL($foreignKey);
199
            }
200
        }
201
202 6
        $name = str_replace('.', '__', $table->getName());
203 6
        $args->addSql('CREATE TABLE ' . $name . ' (' . $queryFields . ')');
204
205 6
        if (isset($options['alter']) && true === $options['alter']) {
206 1
            return;
207
        }
208
209 6 View Code Duplication
        if (!empty($options['indexes'])) {
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...
210 4
            foreach ($options['indexes'] as $indexDef) {
211 4
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
212 4
            }
213 4
        }
214
215 6 View Code Duplication
        if (!empty($options['unique'])) {
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...
216
            foreach ($options['unique'] as $indexDef) {
217
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
218
            }
219
        }
220 6
    }
221
222
    /**
223
     * This function contains workarounds for reading existing Midgard databases
224
     *
225
     * ENUM fields are converted to string for now (Like in the XML reader)
226
     * DATETIME files use our custom datetime type
227
     */
228 2
    public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $args)
229
    {
230 2
        $column = array_change_key_case($args->getTableColumn(), CASE_LOWER);
231 2
        $type = strtok($column['type'], '()');
232
233 2
        if ($type == 'enum') {
234
            $args->preventDefault();
235
236
            $options = [
237
                'length' => 255,
238
                'default' => isset($column['default']) ? $column['default'] : null,
239
                'notnull' => (bool) ($column['null'] != 'YES'),
240
                'comment' => $column['type']
241
            ];
242
243
            $args->setColumn(new Column($column['field'], Type::getType(Type::STRING), $options));
244 2
        } elseif ($type == 'datetime') {
245
            $args->preventDefault();
246
            $options = [
247
                'default' => isset($column['default']) ? $column['default'] : null,
248
                'notnull' => (bool) ($column['null'] != 'YES'),
249
            ];
250
251
            $args->setColumn(new Column($column['field'], Type::getType(datetime::TYPE), $options));
252
        }
253 2
    }
254
255
    /**
256
     * This is mostly a workaround for the fact that SchemaTool wants to create FKs on
257
     * each run since it doesn't detect that MyISAM tables don't support them
258
     *
259
     * @see http://www.doctrine-project.org/jira/browse/DDC-3460
260
     * @param GenerateSchemaTableEventArgs $args
261
     */
262 4
    public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $args)
263
    {
264 4
        $table = $args->getClassTable();
265 4
        if (   !$table->hasOption('engine')
266 4
            || $table->getOption('engine') !== 'MyISAM') {
267
            return;
268
        }
269 4
        foreach ($table->getForeignKeys() as $key) {
270 3
            $table->removeForeignKey($key->getName());
271 4
        }
272 4
    }
273
274 10
    public function getSubscribedEvents()
275
    {
276
        return [
277 10
            Events::onFlush,
278 10
            dbal_events::onSchemaCreateTable, dbal_events::onSchemaColumnDefinition,
279
            ToolEvents::postGenerateSchemaTable
280 10
        ];
281
    }
282
}
283