Test Failed
Push — feature/paths-loader ( ca8af4 )
by Patrick
04:40
created

FluentDriver::__construct()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.1158

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 10
cts 12
cp 0.8333
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 10
nc 5
nop 2
crap 5.1158
1
<?php
2
3
namespace LaravelDoctrine\Fluent;
4
5
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
6
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
7
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
8
use Doctrine\ORM\Mapping\MappingException;
9
use FilesystemIterator;
10
use InvalidArgumentException;
11
use LaravelDoctrine\Fluent\Builders\Builder;
12
use LaravelDoctrine\Fluent\Mappers\MapperSet;
13
use RecursiveDirectoryIterator;
14
use RecursiveIteratorIterator;
15
use RecursiveRegexIterator;
16
use ReflectionClass;
17
use RegexIterator;
18
19
class FluentDriver implements MappingDriver
20
{
21
    /**
22
     * @var MapperSet
23
     */
24
    protected $mappers;
25
26
    /**
27
     * @var callable
28
     */
29
    protected $fluentFactory;
30
31
    /**
32
     * The file extension of mapping documents.
33
     *
34
     * @var string
35
     */
36
    protected $fileExtension = '.php';
37
38
    /**
39
     * Initializes a new FileDriver that looks in the given path(s) for mapping
40
     * documents and operates in the specified operating mode.
41
     *
42
     * @param string[] $mappings
43
     * @param array    $paths
44
     *
45
     * @throws MappingException
46
     */
47
    public function __construct(array $mappings = [], array $paths = [])
48
    {
49 4
        $this->fluentFactory = function (ClassMetadata $metadata) {
50 4
            return new Builder(new ClassMetadataBuilder($metadata));
51
        };
52
53 368
        $this->mappers = new MapperSet();
54
55 368
        if (empty($paths) && empty($mappings)) {
56 52
            throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
57
        }
58
59 316
        if (!empty($paths)) {
60
            $this->loadPaths($paths);
61
        }
62
63 316
        if (!empty($mappings)) {
64 316
            $this->addMappings($mappings);
65 158
        }
66 316
    }
67
68
    /**
69
     * Loads the metadata for the specified class into the provided container.
70
     *
71
     * @param string        $className
72
     * @param ClassMetadata $metadata
73
     */
74 4
    public function loadMetadataForClass($className, ClassMetadata $metadata)
75
    {
76 4
        $this->mappers->getMapperFor($className)->map(
77 4
            $this->getFluent($metadata)
78 2
        );
79 4
    }
80
81
    /**
82
     * Gets the names of all mapped classes known to this driver.
83
     *
84
     * @throws MappingException
85
     * @return string[] The names of all mapped classes known to this driver.
86
     */
87 40
    public function getAllClassNames()
88
    {
89 40
        return $this->mappers->getClassNames();
90
    }
91
92
    /**
93
     * Returns whether the class with the specified name should have its metadata loaded.
94
     * This is only the case if it is either mapped as an Entity or a MappedSuperclass.
95
     *
96
     * @param string $className
97
     *
98
     * @return bool
99
     */
100
    public function isTransient($className)
101
    {
102
        return
103
            !$this->mappers->hasMapperFor($className) ||
104
            $this->mappers->getMapperFor($className)->isTransient();
105
    }
106
107
    /**
108
     * @param array $paths
109
     *
110
     * @throws MappingException
111
     */
112
    public function loadPaths(array $paths)
113
    {
114
        $classes       = [];
0 ignored issues
show
Unused Code introduced by
$classes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
115
        $includedFiles = [];
116
117
        foreach ($paths as $path) {
118
            if (!is_dir($path)) {
119
                throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
120
            }
121
122
            $iterator = new RegexIterator(
123
                new RecursiveIteratorIterator(
124
                    new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
125
                    RecursiveIteratorIterator::LEAVES_ONLY
126
                ),
127
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
128
                RecursiveRegexIterator::GET_MATCH
129
            );
130
131
            foreach ($iterator as $file) {
132
                $sourceFile = $file[0];
133
134
                if (!preg_match('(^phar:)i', $sourceFile)) {
135
                    $sourceFile = realpath($sourceFile);
136
                }
137
138
                require_once $sourceFile;
139
140
                $includedFiles[] = $sourceFile;
141
            }
142
143
            $declared = get_declared_classes();
144
145
            foreach ($declared as $className) {
146
                $rc         = new ReflectionClass($className);
147
                $sourceFile = $rc->getFileName();
148
149
                if (!in_array($sourceFile, $includedFiles)) {
150
                    continue;
151
                }
152
153
                if ($rc->isAbstract() || $rc->isInterface()) {
154
                    continue;
155
                }
156
157
                if (!$rc->implementsInterface(Mapping::class)) {
158
                    continue;
159
                }
160
161
                if ($this->isTransient($className)) {
162
163
                    /** @var Mapping $mapping */
164
                    $mapping = $rc->newInstanceWithoutConstructor();
165
166
                    $this->addMapping($mapping);
167
                }
168
            }
169
        }
170
    }
171
172
    /**
173
     * @param string[] $mappings
174
     *
175
     * @throws InvalidArgumentException
176
     */
177 316
    public function addMappings(array $mappings = [])
178
    {
179 316
        foreach ($mappings as $class) {
180 316
            if (!class_exists($class)) {
181
                throw new InvalidArgumentException("Mapping class [{$class}] does not exist");
182
            }
183
184 316
            $mapping = new $class();
185
186 316
            if (!$mapping instanceof Mapping) {
187
                throw new InvalidArgumentException("Mapping class [{$class}] should implement " . Mapping::class);
188
            }
189
190 316
            $this->addMapping($mapping);
191 158
        }
192 316
    }
193
194
    /**
195
     * @param Mapping $mapping
196
     *
197
     * @throws MappingException
198
     * @return void
199
     */
200 316
    public function addMapping(Mapping $mapping)
201
    {
202 316
        $this->mappers->add($mapping);
203 316
    }
204
205
    /**
206
     * @return MapperSet
207
     */
208 4
    public function getMappers()
209
    {
210 4
        return $this->mappers;
211
    }
212
213
    /**
214
     * Override the default Fluent factory method with a custom one.
215
     * Use this to implement your own Fluent builder.
216
     * The method will receive a ClassMetadata object as its only argument.
217
     *
218
     * @param callable $factory
219
     */
220
    public function setFluentFactory(callable $factory)
221
    {
222
        $this->fluentFactory = $factory;
223
    }
224
225
    /**
226
     * @param ClassMetadata $metadata
227
     *
228
     * @return Fluent
229
     */
230 4
    protected function getFluent(ClassMetadata $metadata)
231
    {
232 4
        return call_user_func($this->fluentFactory, $metadata);
233
    }
234
}
235