Failed Conditions
Pull Request — develop (#1577)
by Marco
66:01
created

PhpExporter   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 295
Duplicated Lines 4.07 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 83.65%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 12
dl 12
loc 295
ccs 87
cts 104
cp 0.8365
rs 8.3999
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
F exportClassMetadata() 12 109 22
B exportFieldMetadata() 0 36 6
D exportAssociationMetadata() 0 66 13
B exportJoinTable() 0 28 2
A exportJoinColumns() 0 22 2
A varExport() 0 13 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 PhpExporter 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 PhpExporter, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Tools\Export\Driver;
21
22
use Doctrine\ORM\Mapping\AssociationMetadata;
23
use Doctrine\ORM\Mapping\ClassMetadata;
24
use Doctrine\ORM\Mapping\FieldMetadata;
25
use Doctrine\ORM\Mapping\JoinColumnMetadata;
26
use Doctrine\ORM\Mapping\JoinTableMetadata;
27
use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata;
28
use Doctrine\ORM\Mapping\ManyToOneAssociationMetadata;
29
use Doctrine\ORM\Mapping\OneToManyAssociationMetadata;
30
use Doctrine\ORM\Mapping\OneToOneAssociationMetadata;
31
use Doctrine\ORM\Mapping\ToOneAssociationMetadata;
32
33
/**
34
 * ClassMetadata exporter for PHP code.
35
 *
36
 * @link    www.doctrine-project.org
37
 * @since   2.0
38
 * @author  Jonathan Wage <[email protected]>
39
 */
40
class PhpExporter extends AbstractExporter
41 1
{
42
    /**
43 1
     * @var string
44 1
     */
45 1
    protected $extension = '.php';
46 1
47 1
    /**
48 1
     * {@inheritdoc}
49 1
     */
50
    public function exportClassMetadata(ClassMetadata $metadata)
51 1
    {
52
        $lines = [];
53
        $lines[] = '<?php';
54
        $lines[] = null;
55 1
        $lines[] = 'use Doctrine\DBAL\Types\Type;';
56 1
        $lines[] = 'use Doctrine\ORM\Mapping\ClassMetadata;';
57
        $lines[] = 'use Doctrine\ORM\Mapping;';
58
        $lines[] = null;
59 1
60
        if ($metadata->isMappedSuperclass) {
61
            $lines[] = '$metadata->isMappedSuperclass = true;';
62
        }
63 1
64 1
        if ($metadata->inheritanceType) {
65
            $lines[] = '$metadata->setInheritanceType(Mapping\InheritanceType::' . $metadata->inheritanceType . ');';
66
        }
67 1
68
        if ($metadata->customRepositoryClassName) {
0 ignored issues
show
Bug introduced by
The property customRepositoryClassName cannot be accessed from this context as it is declared protected in class Doctrine\ORM\Mapping\ClassMetadata.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
69
            $lines[] = '$metadata->customRepositoryClassName = "' . $metadata->customRepositoryClassName . '";';
0 ignored issues
show
Bug introduced by
The property customRepositoryClassName cannot be accessed from this context as it is declared protected in class Doctrine\ORM\Mapping\ClassMetadata.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
70
        }
71
72
        if ($metadata->table) {
73
            $table = $metadata->table;
74
75
            $lines[] = '$table = new Mapping\TableMetadata();';
76
            $lines[] = null;
77
78
            if (! empty($table->getSchema())) {
79
                $lines[] = '$table->setSchema("' . $table->getSchema() . '");';
80
            }
81
82
            $lines[] = '$table->setName("' . $table->getName() . '");';
83
            $lines[] = '$table->setOptions(' . $this->varExport($table->getOptions()) . ');';
84 1
85
            foreach ($table->getIndexes() as $index) {
86
                $lines[] = '$table->addIndex(' . $this->varExport($index) . ');';
87
            }
88 1
89 1
            foreach ($table->getUniqueConstraints() as $constraint) {
90
                $lines[] = '$table->addUniqueConstraint(' . $this->varExport($constraint) . ');';
91
            }
92 1
93 1
            $lines[] = null;
94 1
            $lines[] = '$metadata->setTable($table);';
95 1
        }
96
97
        if ($metadata->discriminatorColumn) {
98
            $discrColumn = $metadata->discriminatorColumn;
99
100 1
            $lines[] = '$discrColumn = new Mapping\DiscriminatorColumnMetadata();';
101 1
            $lines[] = null;
102 1
            $lines[] = '$discrColumn->setColumnName("' . $discrColumn->getColumnName() . '");';
103 1
            $lines[] = '$discrColumn->setType(Type::getType("' . $discrColumn->getTypeName() . '"));';
104 1
            $lines[] = '$discrColumn->setTableName("' . $discrColumn->getTableName() . '");';
105 1
106
            if (! empty($discrColumn->getColumnDefinition())) {
107
                $lines[] = '$property->setColumnDefinition("' . $discrColumn->getColumnDefinition() . '");';
108
            }
109 1
110 1
            if (! empty($discrColumn->getLength())) {
111
                $lines[] = '$property->setLength(' . $discrColumn->getLength() . ');';
112
            }
113 1
114 1
            if (! empty($discrColumn->getScale())) {
115
                $lines[] = '$property->setScale(' . $discrColumn->getScale() . ');';
116 1
            }
117 1
118 1
            if (! empty($discrColumn->getPrecision())) {
119
                $lines[] = '$property->setPrecision(' . $discrColumn->getPrecision() . ');';
120
            }
121
122 1
            $lines[] = '$discrColumn->setOptions(' . $this->varExport($discrColumn->getOptions()) . ');';
123 1
            $lines[] = '$discrColumn->setNullable(' . $this->varExport($discrColumn->isNullable()) . ');';
124
            $lines[] = '$discrColumn->setUnique(' . $this->varExport($discrColumn->isUnique()) . ');';
125
            $lines[] = null;
126
            $lines[] = '$metadata->setDiscriminatorColumn($discrColumn);';
127 1
        }
128 1
129 1
        if ($metadata->discriminatorMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
130
            $lines[] = '$metadata->setDiscriminatorMap(' . $this->varExport($metadata->discriminatorMap) . ');';
131
        }
132 1
133 1
        if ($metadata->changeTrackingPolicy) {
134
            $lines[] = '$metadata->setChangeTrackingPolicy(Mapping\ChangeTrackingPolicy::' . $metadata->changeTrackingPolicy . ');';
135
        }
136 1
137 1
        if ($metadata->lifecycleCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->lifecycleCallbacks of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
138 1 View Code Duplication
            foreach ($metadata->lifecycleCallbacks as $event => $callbacks) {
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...
139
                foreach ($callbacks as $callback) {
140 1
                    $lines[] = '$metadata->addLifecycleCallback("' . $callback . '", "' . $event . '");';
141 1
                }
142 1
            }
143 1
        }
144
145 1
        if (! $metadata->isIdentifierComposite()) {
146
            $lines[] = '$metadata->setIdGeneratorType(Mapping\GeneratorType::' . $metadata->generatorType . ');';
0 ignored issues
show
Bug introduced by
The property generatorType does not seem to exist in Doctrine\ORM\Mapping\ClassMetadata.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
147 1
        }
148 1
149 View Code Duplication
        foreach ($metadata->getProperties() as $property) {
0 ignored issues
show
Bug introduced by
The method getProperties() does not exist on Doctrine\ORM\Mapping\ClassMetadata. Did you maybe mean getPropertiesIterator()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
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...
150 1
            if ($property instanceof FieldMetadata) {
151 1
                $this->exportFieldMetadata($metadata, $property, $lines);
152 1
            } else if ($property instanceof AssociationMetadata) {
153 1
                $this->exportAssociationMetadata($metadata, $property, $lines);
154
            }
155 1
        }
156
157 1
        return implode(PHP_EOL, $lines);
158 1
    }
159 1
160
    private function exportFieldMetadata(ClassMetadata $metadata, FieldMetadata $property, array &$lines)
161 1
    {
162
        $lines[] = sprintf(
163
            '$property = new Mapping\%sFieldMetadata("%s");',
164
            ($metadata->versionProperty === $property) ? 'Version' : '',
165
            $property->getName()
166 1
        );
167 1
168
        $lines[] = null;
169
        $lines[] = '$property->setColumnName("' . $property->getColumnName() . '");';
170
        $lines[] = '$property->setType(Type::getType("' . $property->getTypeName() . '"));';
171 1
        $lines[] = '$property->setTableName("' . $property->getTableName() . '");';
172
173 1
        if (! empty($property->getColumnDefinition())) {
174
            $lines[] = '$property->setColumnDefinition("' . $property->getColumnDefinition() . '");';
175 1
        }
176 1
177 1
        if (! empty($property->getLength())) {
178
            $lines[] = '$property->setLength(' . $property->getLength() . ');';
179 1
        }
180
181
        if (! empty($property->getScale())) {
182
            $lines[] = '$property->setScale(' . $property->getScale() . ');';
183
        }
184 1
185 1
        if (! empty($property->getPrecision())) {
186 1
            $lines[] = '$property->setPrecision(' . $property->getPrecision() . ');';
187
        }
188
189 1
        $lines[] = '$property->setOptions(' . $this->varExport($property->getOptions()) . ');';
190
        $lines[] = '$property->setPrimaryKey(' . $this->varExport($property->isPrimaryKey()) . ');';
191 1
        $lines[] = '$property->setNullable(' . $this->varExport($property->isNullable()) . ');';
192
        $lines[] = '$property->setUnique(' . $this->varExport($property->isUnique()) . ');';
193
        $lines[] = null;
194 1
        $lines[] = '$metadata->addProperty($property);';
195
    }
196 1
197
    private function exportAssociationMetadata(ClassMetadata $metadata, AssociationMetadata $association, array &$lines)
0 ignored issues
show
Unused Code introduced by
The parameter $metadata is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
198
    {
199 1
        $cascade = ['remove', 'persist', 'refresh'];
200
201
        foreach ($cascade as $key => $value) {
202
            if ( ! in_array($value, $association->getCascade())) {
203
                unset($cascade[$key]);
204
            }
205
        }
206
207 1
        if (count($cascade) === 5) {
208
            $cascade = ['all'];
209 1
        }
210 1
211 1
        if ($association instanceof OneToOneAssociationMetadata) {
212 1
            $this->exportJoinColumns($association->getJoinColumns(), $lines, 'joinColumns');
213 1
214 1
            $lines[] = '$association = new Mapping\OneToOneAssociationMetadata("' . $association->getName() . '");';
215 1
            $lines[] = null;
216 1
            $lines[] = '$association->setJoinColumns($joinColumns);';
217
        } else if ($association instanceof ManyToOneAssociationMetadata) {
218 1
            $this->exportJoinColumns($association->getJoinColumns(), $lines, 'joinColumns');
219
220
            $lines[] = '$association = new Mapping\ManyToOneAssociationMetadata("' . $association->getName() . '");';
221
            $lines[] = null;
222
            $lines[] = '$association->setJoinColumns($joinColumns);';
223
        } else if ($association instanceof OneToManyAssociationMetadata) {
224
            $lines[] = '$association = new Mapping\OneToManyAssociationMetadata("' . $association->getName() . '");';
225
            $lines[] = null;
226
            $lines[] = '$association->setOrderBy(' . $this->varExport($association->getOrderBy()) . ');';
227
        } else if ($association instanceof ManyToManyAssociationMetadata) {
228
            if ($association->getJoinTable()) {
229
                $this->exportJoinTable($association->getJoinTable(), $lines);
230
            }
231
232
            $lines[] = '$association = new Mapping\ManyToManyAssociationMetadata("' . $association->getName() . '");';
233
            $lines[] = null;
234
235
            if ($association->getJoinTable()) {
236
                $lines[] = '$association->setJoinTable($joinTable);';
237
            }
238
239
            if ($association->getIndexedBy()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $association->getIndexedBy() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
240
                $lines[] = '$association->setIndexedBy("' . $association->getIndexedBy() . '");';
241
            }
242
243
            $lines[] = '$association->setOrderBy(' . $this->varExport($association->getOrderBy()) . ');';
244
        }
245
246
        $lines[] = '$association->setTargetEntity("' . $association->getTargetEntity() . '");';
247
        $lines[] = '$association->setFetchMode("' . $association->getFetchMode() . '");';
248
249
        if ($association->getMappedBy()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $association->getMappedBy() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
250
            $lines[] = '$association->setMappedBy("' . $association->getMappedBy() . '");';
251
        }
252
253
        if ($association->getInversedBy()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $association->getInversedBy() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
254
            $lines[] = '$association->setInversedBy("' . $association->getInversedBy() . '");';
255
        }
256
257
        $lines[] = '$association->setCascade(' . $this->varExport($cascade) . ');';
258
        $lines[] = '$association->setOrphanRemoval(' . $this->varExport($association->isOrphanRemoval()) . ');';
259
        $lines[] = '$association->setPrimaryKey(' . $this->varExport($association->isPrimaryKey()) . ');';
260
        $lines[] = null;
261
        $lines[] = '$metadata->addProperty($association);';
262
    }
263
264
    private function exportJoinTable(JoinTableMetadata $joinTable, array &$lines)
265
    {
266
        $lines[] = null;
267
        $lines[] = '$joinTable = new Mapping\JoinTableMetadata();';
268
        $lines[] = null;
269
        $lines[] = '$joinTable->setName("' . $joinTable->getName() . '");';
270
271
        if (! empty($joinTable->getSchema())) {
272
            $lines[] = '$joinTable->setSchema("' . $joinTable->getSchema() . '");';
273
        }
274
275
        $lines[] = '$joinTable->setOptions(' . $this->varExport($joinTable->getOptions()) . ');';
276
277
        $this->exportJoinColumns($joinTable->getJoinColumns(), $lines, 'joinColumns');
278
279
        $lines[] = null;
280
        $lines[] = 'foreach ($joinColumns as $joinColumn) {';
281
        $lines[] = '    $joinTable->addJoinColumn($joinColumn);';
282
        $lines[] = '}';
283
        $lines[] = null;
284
285
        $this->exportJoinColumns($joinTable->getInverseJoinColumns(), $lines, 'inverseJoinColumns');
286
287
        $lines[] = null;
288
        $lines[] = 'foreach ($inverseJoinColumns as $inverseJoinColumn) {';
289
        $lines[] = '    $joinTable->addInverseJoinColumn($inverseJoinColumn);';
290
        $lines[] = '}';
291
    }
292
293
    private function exportJoinColumns(array $joinColumns, array &$lines, $variableName)
294
    {
295
        $lines[] = '$' . $variableName . ' = array();';
296
297
        foreach ($joinColumns as $joinColumn) {
298
            /** @var JoinColumnMetadata $joinColumn */
299
            $lines[] = '$joinColumn = new Mapping\JoinColumnMetadata();';
300
            $lines[] = null;
301
            $lines[] = '$joinColumn->setTableName("' . $joinColumn->getTableName() . '");';
302
            $lines[] = '$joinColumn->setColumnName("' . $joinColumn->getColumnName() . '");';
303
            $lines[] = '$joinColumn->setReferencedColumnName("' . $joinColumn->getReferencedColumnName() . '");';
304
            $lines[] = '$joinColumn->setAliasedName("' . $joinColumn->getAliasedName() . '");';
305
            $lines[] = '$joinColumn->setColumnDefinition("' . $joinColumn->getColumnDefinition() . '");';
306
            $lines[] = '$joinColumn->setOnDelete("' . $joinColumn->getOnDelete() . '");';
307
            $lines[] = '$joinColumn->setOptions(' . $this->varExport($joinColumn->getOptions()) . ');';
308
            $lines[] = '$joinColumn->setNullable("' . $joinColumn->isNullable() . '");';
309
            $lines[] = '$joinColumn->setUnique("' . $joinColumn->isUnique() . '");';
310
            $lines[] = '$joinColumn->setPrimaryKey("' . $joinColumn->isPrimaryKey() . '");';
311
            $lines[] = null;
312
            $lines[] = '$' . $variableName . '[] = $joinColumn;';
313
        }
314
    }
315
316
    /**
317
     * @param mixed $var
318
     *
319
     * @return string
320
     */
321
    protected function varExport($var)
322
    {
323
        $export = var_export($var, true);
324
        $export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export);
325
        $export = str_replace('  ', ' ', $export);
326
        $export = str_replace('array (', 'array(', $export);
327
        $export = str_replace('array( ', 'array(', $export);
328
        $export = str_replace(',)', ')', $export);
329
        $export = str_replace(', )', ')', $export);
330
        $export = str_replace('  ', ' ', $export);
331
332
        return $export;
333
    }
334
}
335