Completed
Branch BUG-9871-email-validation (e62b1a)
by
unknown
350:41 queued 333:27
created

Recipe::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 13
c 1
b 0
f 0
nc 1
nop 6
dl 0
loc 16
rs 9.4285
1
<?php
2
namespace EventEspresso\core\services\container;
3
4
use EventEspresso\core\exceptions\InvalidClassException;
5
use EventEspresso\core\exceptions\InvalidDataTypeException;
6
use EventEspresso\core\exceptions\InvalidIdentifierException;
7
use RuntimeException;
8
9
if ( ! defined('EVENT_ESPRESSO_VERSION')) {
10
    exit('No direct script access allowed');
11
}
12
13
14
15
/**
16
 * Class Recipe
17
 * Fairly simple DTO (Data Transfer Object) for relaying information about how to construct an object
18
 *
19
 * @package       Event Espresso
20
 * @author        Brent Christensen
21
 * @since         4.9.1
22
 */
23
class Recipe implements RecipeInterface
24
{
25
26
    /**
27
     * A default Recipe to use if none is specified for a class
28
     */
29
    const DEFAULT_ID = '*';
30
31
    /**
32
     * Identifier for the entity class to be constructed.
33
     * Typically a Fully Qualified Class Name
34
     *
35
     * @var string $identifier
36
     */
37
    private $identifier;
38
39
    /**
40
     * Fully Qualified Class Name
41
     *
42
     * @var string $fqcn
43
     */
44
    private $fqcn;
45
46
    /**
47
     * a dependency class map array
48
     * If a Recipe is for a single class (or group of classes that shares the EXACT SAME constructor arguments),
49
     * and that class type hints for an interface, then this property allows you to configure what dependencies
50
     * get used when instantiating the class.
51
     * For example:
52
     *  There's a class called Coffee, and one of its constructor arguments is BeanInterface
53
     *  There are two implementations of BeanInterface: HonduranBean, and KenyanBean
54
     *  We want one Coffee object to use HonduranBean for its BeanInterface,
55
     *  and the 2nd Coffee object to use KenyanBean for its BeanInterface.
56
     *  To do this, we need to create two Recipes:
57
     *      one with an identifier of 'HonduranCoffee' using the following ingredients :
58
     *          array('BeanInterface' => 'HonduranBean')
59
     *      and the other with an identifier of 'KenyanCoffee' using the following ingredients :
60
     *          array('BeanInterface' => 'KenyanBean')
61
     *  Then, whenever the CoffeeShop brews an instance of HonduranCoffee,
62
     *  an instance of HonduranBean will get injected for the BeanInterface dependency,
63
     *  and whenever the CoffeeShop brews an instance of KenyanCoffee,
64
     *  an instance of KenyanBean will get injected for the BeanInterface dependency
65
     *
66
     * @var array $ingredients
67
     */
68
    private $ingredients = array();
69
70
    /**
71
     * one of the class constants from CoffeeShop:
72
     *  CoffeeMaker::BREW_NEW - creates a new instance
73
     *  CoffeeMaker::BREW_SHARED - creates a shared instance
74
     *  CoffeeMaker::BREW_LOAD_ONLY - loads but does not instantiate
75
     *
76
     * @var string $type
77
     */
78
    private $type;
79
80
    /**
81
     * class name aliases - typically a Fully Qualified Interface that the class implements
82
     * identifiers passed to the CoffeeShop will be run through the filters to find the correct class name
83
     *
84
     * @var array $filters
85
     */
86
    private $filters = array();
87
88
    /**
89
     * array of full server filepaths to files that may contain the class
90
     *
91
     * @var array $paths
92
     */
93
    private $paths = array();
94
95
96
97
    /**
98
     * Recipe constructor.
99
     *
100
     * @param string $identifier    class identifier, can be an alias, or FQCN, or whatever
101
     * @param string $fqcn          \Fully\Qualified\ClassName, optional if $identifier is FQCN
102
     * @param array  $ingredients   array of dependencies that can not be resolved automatically,
103
     *                              used for resolving concrete classes for type hinted interfaces
104
     *                              for the dependencies of THIS class
105
     * @param string $type          recipe type: one of the class constants on
106
     *                              \EventEspresso\core\services\container\CoffeeMaker
107
     * @param array  $filters       array of class aliases, or class interfaces
108
     *                              this works somewhat opposite to the $ingredients array above,
109
     *                              in that this array specifies interfaces or aliases
110
     *                              that this Recipe can be used for when resolving OTHER class's dependencies
111
     * @param array  $paths         if class can not be loaded via PSR-4 autoloading,
112
     *                              then supply a filepath, or array of filepaths, so that it can be included
113
     */
114
    public function __construct(
115
	    $identifier,
116
        $fqcn = '',
117
        $filters = array(),
118
        $ingredients = array(),
119
	    $type = CoffeeMaker::BREW_NEW,
120
	    $paths = array()
121
    )
122
    {
123
        $this->setIdentifier($identifier);
124
        $this->setFilters((array)$filters);
125
        $this->setIngredients((array)$ingredients);
126
        $this->setType($type);
127
        $this->setPaths($paths);
128
        $this->setFqcn($fqcn);
129
    }
130
131
132
133
    /**
134
     * @return string
135
     */
136
    public function identifier()
137
    {
138
        return $this->identifier;
139
    }
140
141
142
143
    /**
144
     * @return string
145
     */
146
    public function fqcn()
147
    {
148
        return $this->fqcn;
149
    }
150
151
152
153
    /**
154
     * @return array
155
     */
156
    public function filters()
157
    {
158
        return (array)$this->filters;
159
    }
160
161
162
163
    /**
164
     * @return array
165
     */
166
    public function ingredients()
167
    {
168
        return $this->ingredients;
169
    }
170
171
172
173
    /**
174
     * @return string
175
     */
176
    public function type()
177
    {
178
        return $this->type;
179
    }
180
181
182
183
    /**
184
     * @return array
185
     */
186
    public function paths()
187
    {
188
        return (array)$this->paths;
189
    }
190
191
192
193
    /**
194
     * @param  string $identifier Identifier for the entity class that the Recipe applies to
195
     *                            Typically a Fully Qualified Class Name
196
     */
197 View Code Duplication
    public function setIdentifier($identifier)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
198
    {
199
        if ( ! is_string($identifier) || empty($identifier)) {
200
            throw new InvalidIdentifierException(
201
                is_object($identifier) ? get_class($identifier) : gettype($identifier),
202
                __('class identifier (typically a \Fully\Qualified\ClassName)', 'event_espresso')
203
            );
204
        }
205
        $this->identifier = $identifier;
206
    }
207
208
209
210
    /**
211
     * Ensures incoming string is a valid Fully Qualified Class Name,
212
     * except if this is the default wildcard Recipe ( * ),
213
     * or it's NOT an actual FQCN because the Recipe is using filepaths
214
     * for classes that are not PSR-4 compatible
215
     * PLZ NOTE:
216
     *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
217
     *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
218
     *
219
     * @param string $fqcn
220
     */
221
    public function setFqcn($fqcn)
222
    {
223
	    $fqcn = ! empty($fqcn) ? $fqcn : $this->identifier;
224
        if ( ! is_string($fqcn)) {
225
            throw new InvalidDataTypeException(
226
                '$fqcn',
227
                is_object($fqcn) ? get_class($fqcn) : gettype($fqcn),
228
                __('string (Fully\Qualified\ClassName)', 'event_espresso')
229
            );
230
        }
231
        $fqcn = ltrim($fqcn, '\\');
232
        if (
233
            $fqcn !== Recipe::DEFAULT_ID
234
            && ! empty($fqcn)
235
            && empty($this->paths)
236
            && ! class_exists($fqcn)
237
        ) {
238
            throw new InvalidClassException($fqcn);
239
        }
240
        $this->fqcn = $fqcn;
241
    }
242
243
244
245
    /**
246
     * @param array $ingredients    an array of dependencies where keys are the aliases and values are the FQCNs
247
     *                              example:
248
     *                              array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
249
     */
250 View Code Duplication
    public function setIngredients(array $ingredients)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
251
    {
252
        if (empty($ingredients)) {
253
            return;
254
        }
255
        if ( ! is_array($ingredients)) {
256
            throw new InvalidDataTypeException(
257
                '$ingredients',
258
                is_object($ingredients) ? get_class($ingredients) : gettype($ingredients),
259
                __('array of class dependencies', 'event_espresso')
260
            );
261
        }
262
        $this->ingredients = array_merge($this->ingredients, $ingredients);
263
    }
264
265
266
    /**
267
     * @param string $type one of the class constants returned from CoffeeMaker::getTypes()
268
     */
269
    public function setType($type = CoffeeMaker::BREW_NEW)
270
    {
271
        $this->type = CoffeeMaker::validateType($type);
272
    }
273
274
275
276
    /**
277
     * @param array $filters an array of filters where keys are the aliases and values are the FQCNs
278
     *                          example:
279
     *                          array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
280
     */
281 View Code Duplication
    public function setFilters(array $filters)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
282
    {
283
        if (empty($filters)) {
284
            return;
285
        }
286
        if ( ! is_array($filters)) {
287
            throw new InvalidDataTypeException(
288
                '$filters',
289
                is_object($filters) ? get_class($filters) : gettype($filters),
290
                __('array of class aliases', 'event_espresso')
291
            );
292
        }
293
        $this->filters = array_merge($this->filters, $filters);
294
    }
295
296
297
298
    /**
299
     * Ensures incoming paths is a valid filepath, or array of valid filepaths,
300
     * and merges them in with any existing filepaths
301
     *
302
     * PLZ NOTE:
303
     *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
304
     *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
305
     *
306
     * @param string|array $paths
307
     */
308
    public function setPaths($paths = array())
309
    {
310
        if (empty($paths)) {
311
            return;
312
        }
313
        if ( ! (is_string($paths) || is_array($paths))) {
314
            throw new InvalidDataTypeException(
315
                '$path',
316
                is_object($paths) ? get_class($paths) : gettype($paths),
317
                __('string or array of strings (full server filepath(s))', 'event_espresso')
318
            );
319
        }
320
        $paths = (array)$paths;
321
        foreach ($paths as $path) {
322 View Code Duplication
            if (strpos($path, '*') === false && ! is_readable($path)) {
323
                throw new RuntimeException(
324
                    sprintf(
325
                        __('The following filepath is not readable: "%1$s"', 'event_espresso'),
326
                        $path
327
                    )
328
                );
329
            }
330
        }
331
        $this->paths = array_merge($this->paths, $paths);
332
    }
333
334
335
336
}
337
// End of file Recipe.php
338
// Location: /Recipe.php