Completed
Branch Gutenberg/master (b3a823)
by
unknown
73:52 queued 60:28
created

CollectionLoader::loadAllFromFilepaths()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 6
nop 0
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\services\collections;
4
5
use EE_Error;
6
use EE_Registry;
7
use EEH_File;
8
use EventEspresso\core\exceptions\InvalidClassException;
9
use EventEspresso\core\exceptions\InvalidDataTypeException;
10
use EventEspresso\core\exceptions\InvalidEntityException;
11
use EventEspresso\core\exceptions\InvalidFilePathException;
12
use EventEspresso\core\exceptions\InvalidInterfaceException;
13
use EventEspresso\core\services\locators\LocatorInterface;
14
use EventEspresso\core\services\locators\FileLocator;
15
use InvalidArgumentException;
16
use ReflectionException;
17
18
/**
19
 * Class CollectionLoader
20
 * given the details on where to find files, will populate a collection
21
 * plz see: \EventEspresso\core\services\collections\CollectionDetails
22
 *
23
 * @package       Event Espresso
24
 * @author        Brent Christensen
25
 * @since         4.9.0
26
 */
27
class CollectionLoader
28
{
29
30
    /**
31
     * possible return value when adding entities to a collection.
32
     * denotes that the entity was NOT ADDED to the collection
33
     */
34
    const ENTITY_NOT_ADDED = 'entity-not-added-to-collection';
35
36
    /**
37
     * possible return value when adding entities to a collection.
38
     * denotes that the entity was SUCCESSFULLY ADDED to the collection
39
     */
40
    const ENTITY_ADDED = 'entity-added-to-collection';
41
42
    /**
43
     * possible return value when adding entities to a collection.
44
     * denotes that the entity was ALREADY ADDED to the collection,
45
     * and therefore could not be added again.
46
     */
47
    const ENTITY_EXISTS = 'entity-already-in-collection';
48
49
50
    /**
51
     * @var CollectionDetailsInterface $collection_details
52
     */
53
    protected $collection_details;
54
55
    /**
56
     * @var CollectionInterface $collection
57
     */
58
    protected $collection;
59
60
    /**
61
     * @var FileLocator $file_locator
62
     */
63
    protected $file_locator;
64
65
66
    /**
67
     * CollectionLoader constructor.
68
     *
69
     * @param CollectionDetailsInterface $collection_details
70
     * @param CollectionInterface        $collection
71
     * @param LocatorInterface           $file_locator
72
     * @throws ReflectionException
73
     * @throws InvalidArgumentException
74
     * @throws EE_Error
75
     * @throws InvalidInterfaceException
76
     * @throws InvalidClassException
77
     * @throws InvalidDataTypeException
78
     * @throws InvalidFilePathException
79
     * @throws InvalidEntityException
80
     */
81
    public function __construct(
82
        CollectionDetailsInterface $collection_details,
83
        CollectionInterface $collection = null,
84
        LocatorInterface $file_locator = null
85
    ) {
86
        $this->collection_details = $collection_details;
87
        if (! $collection instanceof CollectionInterface) {
88
            $collection = new Collection($this->collection_details->getCollectionInterface());
89
        }
90
        $this->collection = $collection;
91
        $this->file_locator = $file_locator;
92
        $this->loadAllFromFilepaths();
93
        $this->loadFromFQCNs();
94
    }
95
96
97
    /**
98
     * @return CollectionInterface
99
     */
100
    public function getCollection()
101
    {
102
        return $this->collection;
103
    }
104
105
106
    /**
107
     * @throws InvalidClassException
108
     * @throws InvalidFilePathException
109
     * @throws InvalidDataTypeException
110
     * @throws InvalidEntityException
111
     */
112
    protected function loadAllFromFilepaths()
113
    {
114
        if (! $this->file_locator instanceof FileLocator) {
115
            $this->file_locator = new FileLocator();
116
        }
117
        $this->file_locator->setFileMask($this->collection_details->getFileMask());
118
        // find all of the files that match the file mask in the specified folder
119
        $this->file_locator->locate($this->collection_details->getCollectionPaths());
120
        // filter the results
121
        $filepaths = (array) apply_filters(
122
            'FHEE__CollectionLoader__loadAllFromFilepath__filepaths',
123
            $this->file_locator->getFilePaths(),
124
            $this->collection_details->collectionName(),
125
            $this->collection_details
126
        );
127
        if (empty($filepaths)) {
128
            return;
129
        }
130
        foreach ($filepaths as $filepath) {
131
            $this->loadClassFromFilepath($filepath);
132
        }
133
    }
134
135
136
    /**
137
     * @param  string $filepath
138
     * @return string
139
     * @throws InvalidEntityException
140
     * @throws InvalidDataTypeException
141
     * @throws InvalidFilePathException
142
     * @throws InvalidClassException
143
     */
144
    protected function loadClassFromFilepath($filepath)
145
    {
146
        if (! is_string($filepath)) {
147
            throw new InvalidDataTypeException('$filepath', $filepath, 'string');
148
        }
149
        if (! is_readable($filepath)) {
150
            throw new InvalidFilePathException($filepath);
151
        }
152
        require_once $filepath;
153
        // extract filename from path
154
        $file_name = basename($filepath);
155
        // now remove any file extensions
156
        $class_name = EEH_File::get_classname_from_filepath_with_standard_filename($file_name);
157
        if (! class_exists($class_name)) {
158
            throw new InvalidClassException($class_name);
159
        }
160
        return $this->addEntityToCollection(new $class_name(), $file_name);
161
    }
162
163
164
    /**
165
     * @param        $entity
166
     * @param  mixed $identifier
167
     * @return string
168
     * @throws InvalidEntityException
169
     */
170
    protected function addEntityToCollection($entity, $identifier)
171
    {
172
        do_action(
173
            'FHEE__CollectionLoader__addEntityToCollection__entity',
174
            $entity,
175
            $this->collection_details->collectionName(),
176
            $this->collection_details
177
        );
178
        $identifier = $this->setIdentifier($entity, $identifier);
179 View Code Duplication
        if ($this->collection->has($identifier)) {
180
            do_action(
181
                'FHEE__CollectionLoader__addEntityToCollection__entity_already_added',
182
                $this,
183
                $this->collection_details->collectionName(),
184
                $this->collection_details
185
            );
186
            return CollectionLoader::ENTITY_EXISTS;
187
        }
188 View Code Duplication
        if ($this->collection->add($entity, $identifier)) {
189
            do_action(
190
                'FHEE__CollectionLoader__addEntityToCollection__entity_added',
191
                $this,
192
                $this->collection_details->collectionName(),
193
                $this->collection_details
194
            );
195
            return CollectionLoader::ENTITY_ADDED;
196
        }
197
        do_action(
198
            'FHEE__CollectionLoader__addEntityToCollection__entity_not_added',
199
            $this,
200
            $this->collection_details->collectionName(),
201
            $this->collection_details
202
        );
203
        return CollectionLoader::ENTITY_NOT_ADDED;
204
    }
205
206
207
    /**
208
     * @param        $entity
209
     * @param  mixed $identifier
210
     * @return string
211
     * @throws InvalidEntityException
212
     */
213
    protected function setIdentifier($entity, $identifier)
214
    {
215
        switch ($this->collection_details->identifierType()) {
216
            // every unique object gets added to the collection, but not duplicates of the exact same object
217
            case CollectionDetails::ID_OBJECT_HASH:
218
                $identifier = spl_object_hash($entity);
219
                break;
220
            // only one entity per class can be added to collection, like a singleton
221
            case CollectionDetails::ID_CLASS_NAME:
222
                $identifier = get_class($entity);
223
                break;
224
            // objects added to the collection based on entity callback, so the entity itself decides
225
            case CollectionDetails::ID_CALLBACK_METHOD:
226
                $identifier_callback = $this->collection_details->identifierCallback();
227
                if (! method_exists($entity, $identifier_callback)) {
228
                    throw new InvalidEntityException(
229
                        $entity,
230
                        $this->collection_details->getCollectionInterface(),
231
                        sprintf(
232
                            __(
233
                                'The current collection is configured to use a method named "%1$s" when setting or retrieving objects. The supplied entity is an instance
234
                                of "%2$s", but does not contain this method.',
235
                                'event_espresso'
236
                            ),
237
                            $identifier_callback,
238
                            get_class($entity)
239
                        )
240
                    );
241
                }
242
                $identifier = $entity->{$identifier_callback}();
243
                break;
244
        }
245
        return apply_filters(
246
            'FHEE__CollectionLoader__addEntityToCollection__identifier',
247
            $identifier,
248
            $this->collection_details->collectionName(),
249
            $this->collection_details
250
        );
251
    }
252
253
254
    /**
255
     * @throws ReflectionException
256
     * @throws InvalidArgumentException
257
     * @throws InvalidInterfaceException
258
     * @throws EE_Error
259
     * @throws InvalidClassException
260
     * @throws InvalidDataTypeException
261
     * @throws InvalidEntityException
262
     */
263
    protected function loadFromFQCNs()
264
    {
265
        $FQCNs = $this->collection_details->getCollectionFQCNs();
266
        $FQCNs = (array) apply_filters(
267
            'FHEE__CollectionLoader__loadAllFromFQCNs__FQCNs',
268
            $FQCNs,
269
            $this->collection_details->collectionName(),
270
            $this->collection_details
271
        );
272
        foreach ($FQCNs as $FQCN) {
273
            $this->loadClassFromFQCN($FQCN);
274
        }
275
    }
276
277
278
    /**
279
     * @param  string $FQCN Fully Qualified Class Name
280
     * @return string
281
     * @throws InvalidArgumentException
282
     * @throws InvalidInterfaceException
283
     * @throws ReflectionException
284
     * @throws EE_Error
285
     * @throws InvalidEntityException
286
     * @throws InvalidDataTypeException
287
     * @throws InvalidClassException
288
     */
289
    protected function loadClassFromFQCN($FQCN)
290
    {
291
        if (! is_string($FQCN)) {
292
            throw new InvalidDataTypeException('$FQCN', $FQCN, 'string');
293
        }
294
        if (! class_exists($FQCN)) {
295
            throw new InvalidClassException($FQCN);
296
        }
297
        return $this->addEntityToCollection(
298
            EE_Registry::instance()->create($FQCN),
299
            $FQCN
300
        );
301
    }
302
}
303