Completed
Branch FET/11450/caching-unit-tests (fd4ae9)
by
unknown
51:38 queued 38:35
created

EE_Registry   F

Complexity

Total Complexity 193

Size/Duplication

Total Lines 1706
Duplicated Lines 2.23 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
dl 38
loc 1706
rs 0.6314
c 0
b 0
f 0
wmc 193
lcom 1
cbo 16

45 Methods

Rating   Name   Duplication   Size   Complexity  
A localize_i18n_js_strings() 0 10 3
A add_module() 0 12 3
A get_module() 0 6 2
A instance() 0 8 2
A __construct() 0 11 1
B initialize() 0 34 1
A init() 0 8 3
B load_core() 0 26 1
A load_service() 0 20 1
A load_dms() 0 13 1
A load_class() 0 21 1
A load_helper() 0 16 1
A load_lib() 0 21 1
A load_model() 0 20 1
A load_model_class() 0 20 1
A is_model_name() 0 4 1
A load_file() 0 14 1
A load_addon() 0 14 1
C create() 7 61 17
D loadOrVerifyClassExists() 0 25 9
C _load() 7 64 16
A get_class_abbreviation() 0 6 2
C _get_cached_class() 0 52 13
B clear_cached_class() 0 27 6
C _set_cached_class() 0 30 7
A getClassIdentifier() 0 18 4
C getIdentifierForArguments() 14 23 7
B _resolve_path() 0 25 6
B _require_file() 0 48 5
A resolve_legacy_class_parent() 0 12 3
C _create_object() 0 54 13
A _array_is_numerically_and_sequentially_indexed() 0 6 2
A get_ReflectionClass() 10 10 3
C _resolve_dependencies() 0 72 15
C _resolve_dependency() 0 45 8
A factory() 0 11 3
A getAddon() 0 5 1
A removeAddon() 0 5 1
A get_addon_by_name() 0 9 3
A get_addons_by_name() 0 8 2
B reset_model() 0 19 6
B reset() 0 23 4
C _reset_and_unset_object() 0 27 7
A cpt_models() 0 10 3
A CFG() 0 4 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 EE_Registry 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 EE_Registry, and based on these observations, apply Extract Interface, too.

1
<?php
2
use EventEspresso\core\exceptions\InvalidDataTypeException;
3
use EventEspresso\core\exceptions\InvalidInterfaceException;
4
use EventEspresso\core\interfaces\InterminableInterface;
5
use EventEspresso\core\interfaces\ResettableInterface;
6
use EventEspresso\core\services\assets\Registry;
7
use EventEspresso\core\services\commands\CommandBusInterface;
8
use EventEspresso\core\services\container\RegistryContainer;
9
use EventEspresso\core\services\loaders\LoaderFactory;
10
11
defined('EVENT_ESPRESSO_VERSION') || exit;
12
13
14
15
/**
16
 * EE_Registry Class
17
 * Centralized Application Data Storage and Management
18
 *
19
 * @package                   Event Espresso
20
 * @subpackage                core
21
 * @author                    Brent Christensen
22
 */
23
class EE_Registry implements ResettableInterface
24
{
25
26
    /**
27
     * @var EE_Registry $_instance
28
     */
29
    private static $_instance;
30
31
    /**
32
     * @var EE_Dependency_Map $_dependency_map
33
     */
34
    protected $_dependency_map;
35
36
    /**
37
     * @var array $_class_abbreviations
38
     */
39
    protected $_class_abbreviations = array();
40
41
    /**
42
     * @var CommandBusInterface $BUS
43
     */
44
    public $BUS;
45
46
    /**
47
     * @var EE_Cart $CART
48
     */
49
    public $CART;
50
51
    /**
52
     * @var EE_Config $CFG
53
     */
54
    public $CFG;
55
56
    /**
57
     * @var EE_Network_Config $NET_CFG
58
     */
59
    public $NET_CFG;
60
61
    /**
62
     * StdClass object for storing library classes in
63
     *
64
     * @var StdClass $LIB
65
     */
66
    public $LIB;
67
68
    /**
69
     * @var EE_Request_Handler $REQ
70
     */
71
    public $REQ;
72
73
    /**
74
     * @var EE_Session $SSN
75
     */
76
    public $SSN;
77
78
    /**
79
     * @since 4.5.0
80
     * @var EE_Capabilities $CAP
81
     */
82
    public $CAP;
83
84
    /**
85
     * @since 4.9.0
86
     * @var EE_Message_Resource_Manager $MRM
87
     */
88
    public $MRM;
89
90
91
    /**
92
     * @var Registry $AssetsRegistry
93
     */
94
    public $AssetsRegistry;
95
96
    /**
97
     * StdClass object for holding addons which have registered themselves to work with EE core
98
     *
99
     * @var EE_Addon[] $addons
100
     */
101
    public $addons;
102
103
    /**
104
     * keys are 'short names' (eg Event), values are class names (eg 'EEM_Event')
105
     *
106
     * @var EEM_Base[] $models
107
     */
108
    public $models = array();
109
110
    /**
111
     * @var EED_Module[] $modules
112
     */
113
    public $modules;
114
115
    /**
116
     * @var EES_Shortcode[] $shortcodes
117
     */
118
    public $shortcodes;
119
120
    /**
121
     * @var WP_Widget[] $widgets
122
     */
123
    public $widgets;
124
125
    /**
126
     * this is an array of all implemented model names (i.e. not the parent abstract models, or models
127
     * which don't actually fetch items from the DB in the normal way (ie, are not children of EEM_Base)).
128
     * Keys are model "short names" (eg "Event") as used in model relations, and values are
129
     * classnames (eg "EEM_Event")
130
     *
131
     * @var array $non_abstract_db_models
132
     */
133
    public $non_abstract_db_models = array();
134
135
136
    /**
137
     * internationalization for JS strings
138
     *    usage:   EE_Registry::i18n_js_strings['string_key'] = esc_html__( 'string to translate.', 'event_espresso' );
139
     *    in js file:  var translatedString = eei18n.string_key;
140
     *
141
     * @var array $i18n_js_strings
142
     */
143
    public static $i18n_js_strings = array();
144
145
146
    /**
147
     * $main_file - path to espresso.php
148
     *
149
     * @var array $main_file
150
     */
151
    public $main_file;
152
153
    /**
154
     * array of ReflectionClass objects where the key is the class name
155
     *
156
     * @var ReflectionClass[] $_reflectors
157
     */
158
    public $_reflectors;
159
160
    /**
161
     * boolean flag to indicate whether or not to load/save dependencies from/to the cache
162
     *
163
     * @var boolean $_cache_on
164
     */
165
    protected $_cache_on = true;
166
167
168
169
    /**
170
     * @singleton method used to instantiate class object
171
     * @param  EE_Dependency_Map $dependency_map
172
     * @return EE_Registry instance
173
     * @throws InvalidArgumentException
174
     * @throws InvalidInterfaceException
175
     * @throws InvalidDataTypeException
176
     */
177
    public static function instance(EE_Dependency_Map $dependency_map = null)
178
    {
179
        // check if class object is instantiated
180
        if (! self::$_instance instanceof EE_Registry) {
181
            self::$_instance = new self($dependency_map);
0 ignored issues
show
Bug introduced by
It seems like $dependency_map defined by parameter $dependency_map on line 177 can be null; however, EE_Registry::__construct() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
182
        }
183
        return self::$_instance;
184
    }
185
186
187
188
    /**
189
     * protected constructor to prevent direct creation
190
     *
191
     * @Constructor
192
     * @param  EE_Dependency_Map $dependency_map
193
     * @throws InvalidDataTypeException
194
     * @throws InvalidInterfaceException
195
     * @throws InvalidArgumentException
196
     */
197
    protected function __construct(EE_Dependency_Map $dependency_map)
198
    {
199
        $this->_dependency_map = $dependency_map;
200
        // $registry_container = new RegistryContainer();
201
        $this->LIB = new RegistryContainer();
202
        $this->addons = new RegistryContainer();
203
        $this->modules = new RegistryContainer();
204
        $this->shortcodes = new RegistryContainer();
205
        $this->widgets = new RegistryContainer();
206
        add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', array($this, 'initialize'));
207
    }
208
209
210
211
    /**
212
     * initialize
213
     *
214
     * @throws EE_Error
215
     * @throws ReflectionException
216
     */
217
    public function initialize()
218
    {
219
        $this->_class_abbreviations = apply_filters(
220
            'FHEE__EE_Registry____construct___class_abbreviations',
221
            array(
222
                'EE_Config'                                       => 'CFG',
223
                'EE_Session'                                      => 'SSN',
224
                'EE_Capabilities'                                 => 'CAP',
225
                'EE_Cart'                                         => 'CART',
226
                'EE_Network_Config'                               => 'NET_CFG',
227
                'EE_Request_Handler'                              => 'REQ',
228
                'EE_Message_Resource_Manager'                     => 'MRM',
229
                'EventEspresso\core\services\commands\CommandBus' => 'BUS',
230
                'EventEspresso\core\services\assets\Registry'     => 'AssetsRegistry',
231
            )
232
        );
233
        $this->load_core('Base', array(), true);
234
        // add our request and response objects to the cache
235
        $request_loader = $this->_dependency_map->class_loader(
236
            'EventEspresso\core\services\request\Request'
237
        );
238
        $this->_set_cached_class(
239
            $request_loader(),
240
            'EventEspresso\core\services\request\Request'
241
        );
242
        $response_loader = $this->_dependency_map->class_loader(
243
            'EventEspresso\core\services\request\Response'
244
        );
245
        $this->_set_cached_class(
246
            $response_loader(),
247
            'EventEspresso\core\services\request\Response'
248
        );
249
        add_action('AHEE__EE_System__set_hooks_for_core', array($this, 'init'));
250
    }
251
252
253
254
    /**
255
     * @return void
256
     */
257
    public function init()
258
    {
259
        // Get current page protocol
260
        $protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
261
        // Output admin-ajax.php URL with same protocol as current page
262
        self::$i18n_js_strings['ajax_url'] = admin_url('admin-ajax.php', $protocol);
263
        self::$i18n_js_strings['wp_debug'] = defined('WP_DEBUG') ? WP_DEBUG : false;
264
    }
265
266
267
268
    /**
269
     * localize_i18n_js_strings
270
     *
271
     * @return string
272
     */
273
    public static function localize_i18n_js_strings()
274
    {
275
        $i18n_js_strings = (array)self::$i18n_js_strings;
276
        foreach ($i18n_js_strings as $key => $value) {
277
            if (is_scalar($value)) {
278
                $i18n_js_strings[$key] = html_entity_decode((string)$value, ENT_QUOTES, 'UTF-8');
279
            }
280
        }
281
        return '/* <![CDATA[ */ var eei18n = ' . wp_json_encode($i18n_js_strings) . '; /* ]]> */';
282
    }
283
284
285
286
    /**
287
     * @param mixed string | EED_Module $module
288
     * @throws EE_Error
289
     * @throws ReflectionException
290
     */
291
    public function add_module($module)
292
    {
293
        if ($module instanceof EED_Module) {
294
            $module_class = get_class($module);
295
            $this->modules->{$module_class} = $module;
296
        } else {
297
            if ( ! class_exists('EE_Module_Request_Router', false)) {
298
                $this->load_core('Module_Request_Router');
299
            }
300
            EE_Module_Request_Router::module_factory($module);
301
        }
302
    }
303
304
305
306
    /**
307
     * @param string $module_name
308
     * @return mixed EED_Module | NULL
309
     */
310
    public function get_module($module_name = '')
311
    {
312
        return isset($this->modules->{$module_name})
313
            ? $this->modules->{$module_name}
314
            : null;
315
    }
316
317
318
319
    /**
320
     * loads core classes - must be singletons
321
     *
322
     * @param string $class_name - simple class name ie: session
323
     * @param mixed  $arguments
324
     * @param bool   $load_only
325
     * @return mixed
326
     * @throws EE_Error
327
     * @throws ReflectionException
328
     */
329
    public function load_core($class_name, $arguments = array(), $load_only = false)
330
    {
331
        $core_paths = apply_filters(
332
            'FHEE__EE_Registry__load_core__core_paths',
333
            array(
334
                EE_CORE,
335
                EE_ADMIN,
336
                EE_CPTS,
337
                EE_CORE . 'data_migration_scripts' . DS,
338
                EE_CORE . 'capabilities' . DS,
339
                EE_CORE . 'request_stack' . DS,
340
                EE_CORE . 'middleware' . DS,
341
            )
342
        );
343
        // retrieve instantiated class
344
        return $this->_load(
345
            $core_paths,
346
            'EE_',
347
            $class_name,
348
            'core',
349
            $arguments,
350
            false,
351
            true,
352
            $load_only
353
        );
354
    }
355
356
357
358
    /**
359
     * loads service classes
360
     *
361
     * @param string $class_name - simple class name ie: session
362
     * @param mixed  $arguments
363
     * @param bool   $load_only
364
     * @return mixed
365
     * @throws EE_Error
366
     * @throws ReflectionException
367
     */
368
    public function load_service($class_name, $arguments = array(), $load_only = false)
369
    {
370
        $service_paths = apply_filters(
371
            'FHEE__EE_Registry__load_service__service_paths',
372
            array(
373
                EE_CORE . 'services' . DS,
374
            )
375
        );
376
        // retrieve instantiated class
377
        return $this->_load(
378
            $service_paths,
379
            'EE_',
380
            $class_name,
381
            'class',
382
            $arguments,
383
            false,
384
            true,
385
            $load_only
386
        );
387
    }
388
389
390
391
    /**
392
     * loads data_migration_scripts
393
     *
394
     * @param string $class_name - class name for the DMS ie: EE_DMS_Core_4_2_0
395
     * @param mixed  $arguments
396
     * @return EE_Data_Migration_Script_Base|mixed
397
     * @throws EE_Error
398
     * @throws ReflectionException
399
     */
400
    public function load_dms($class_name, $arguments = array())
401
    {
402
        // retrieve instantiated class
403
        return $this->_load(
404
            EE_Data_Migration_Manager::instance()->get_data_migration_script_folders(),
405
            'EE_DMS_',
406
            $class_name,
407
            'dms',
408
            $arguments,
409
            false,
410
            false
411
        );
412
    }
413
414
415
416
    /**
417
     * loads object creating classes - must be singletons
418
     *
419
     * @param string $class_name - simple class name ie: attendee
420
     * @param mixed  $arguments  - an array of arguments to pass to the class
421
     * @param bool   $from_db    - some classes are instantiated from the db and thus call a different method to
422
     *                           instantiate
423
     * @param bool   $cache      if you don't want the class to be stored in the internal cache (non-persistent) then
424
     *                           set this to FALSE (ie. when instantiating model objects from client in a loop)
425
     * @param bool   $load_only  whether or not to just load the file and NOT instantiate, or load AND instantiate
426
     *                           (default)
427
     * @return EE_Base_Class | bool
428
     * @throws EE_Error
429
     * @throws ReflectionException
430
     */
431
    public function load_class($class_name, $arguments = array(), $from_db = false, $cache = true, $load_only = false)
432
    {
433
        $paths = apply_filters(
434
            'FHEE__EE_Registry__load_class__paths', array(
435
            EE_CORE,
436
            EE_CLASSES,
437
            EE_BUSINESS,
438
        )
439
        );
440
        // retrieve instantiated class
441
        return $this->_load(
442
            $paths,
443
            'EE_',
444
            $class_name,
445
            'class',
446
            $arguments,
447
            $from_db,
448
            $cache,
449
            $load_only
450
        );
451
    }
452
453
454
455
    /**
456
     * loads helper classes - must be singletons
457
     *
458
     * @param string $class_name - simple class name ie: price
459
     * @param mixed  $arguments
460
     * @param bool   $load_only
461
     * @return EEH_Base | bool
462
     * @throws EE_Error
463
     * @throws ReflectionException
464
     */
465
    public function load_helper($class_name, $arguments = array(), $load_only = true)
466
    {
467
        // todo: add doing_it_wrong() in a few versions after all addons have had calls to this method removed
468
        $helper_paths = apply_filters('FHEE__EE_Registry__load_helper__helper_paths', array(EE_HELPERS));
469
        // retrieve instantiated class
470
        return $this->_load(
471
            $helper_paths,
472
            'EEH_',
473
            $class_name,
474
            'helper',
475
            $arguments,
476
            false,
477
            true,
478
            $load_only
479
        );
480
    }
481
482
483
484
    /**
485
     * loads core classes - must be singletons
486
     *
487
     * @param string $class_name - simple class name ie: session
488
     * @param mixed  $arguments
489
     * @param bool   $load_only
490
     * @param bool   $cache      whether to cache the object or not.
491
     * @return mixed
492
     * @throws EE_Error
493
     * @throws ReflectionException
494
     */
495
    public function load_lib($class_name, $arguments = array(), $load_only = false, $cache = true)
496
    {
497
        $paths = array(
498
            EE_LIBRARIES,
499
            EE_LIBRARIES . 'messages' . DS,
500
            EE_LIBRARIES . 'shortcodes' . DS,
501
            EE_LIBRARIES . 'qtips' . DS,
502
            EE_LIBRARIES . 'payment_methods' . DS,
503
        );
504
        // retrieve instantiated class
505
        return $this->_load(
506
            $paths,
507
            'EE_',
508
            $class_name,
509
            'lib',
510
            $arguments,
511
            false,
512
            $cache,
513
            $load_only
514
        );
515
    }
516
517
518
519
    /**
520
     * loads model classes - must be singletons
521
     *
522
     * @param string $class_name - simple class name ie: price
523
     * @param mixed  $arguments
524
     * @param bool   $load_only
525
     * @return EEM_Base | bool
526
     * @throws EE_Error
527
     * @throws ReflectionException
528
     */
529
    public function load_model($class_name, $arguments = array(), $load_only = false)
530
    {
531
        $paths = apply_filters(
532
            'FHEE__EE_Registry__load_model__paths', array(
533
            EE_MODELS,
534
            EE_CORE,
535
        )
536
        );
537
        // retrieve instantiated class
538
        return $this->_load(
539
            $paths,
540
            'EEM_',
541
            $class_name,
542
            'model',
543
            $arguments,
544
            false,
545
            true,
546
            $load_only
547
        );
548
    }
549
550
551
552
    /**
553
     * loads model classes - must be singletons
554
     *
555
     * @param string $class_name - simple class name ie: price
556
     * @param mixed  $arguments
557
     * @param bool   $load_only
558
     * @return mixed | bool
559
     * @throws EE_Error
560
     * @throws ReflectionException
561
     */
562
    public function load_model_class($class_name, $arguments = array(), $load_only = true)
563
    {
564
        $paths = array(
565
            EE_MODELS . 'fields' . DS,
566
            EE_MODELS . 'helpers' . DS,
567
            EE_MODELS . 'relations' . DS,
568
            EE_MODELS . 'strategies' . DS,
569
        );
570
        // retrieve instantiated class
571
        return $this->_load(
572
            $paths,
573
            'EE_',
574
            $class_name,
575
            '',
576
            $arguments,
577
            false,
578
            true,
579
            $load_only
580
        );
581
    }
582
583
584
585
    /**
586
     * Determines if $model_name is the name of an actual EE model.
587
     *
588
     * @param string $model_name like Event, Attendee, Question_Group_Question, etc.
589
     * @return boolean
590
     */
591
    public function is_model_name($model_name)
592
    {
593
        return isset($this->models[$model_name]);
594
    }
595
596
597
598
    /**
599
     * generic class loader
600
     *
601
     * @param string $path_to_file - directory path to file location, not including filename
602
     * @param string $file_name    - file name  ie:  my_file.php, including extension
603
     * @param string $type         - file type - core? class? helper? model?
604
     * @param mixed  $arguments
605
     * @param bool   $load_only
606
     * @return mixed
607
     * @throws EE_Error
608
     * @throws ReflectionException
609
     */
610
    public function load_file($path_to_file, $file_name, $type = '', $arguments = array(), $load_only = true)
611
    {
612
        // retrieve instantiated class
613
        return $this->_load(
614
            $path_to_file,
615
            '',
616
            $file_name,
617
            $type,
618
            $arguments,
619
            false,
620
            true,
621
            $load_only
622
        );
623
    }
624
625
626
627
    /**
628
     * @param string $path_to_file - directory path to file location, not including filename
629
     * @param string $class_name   - full class name  ie:  My_Class
630
     * @param string $type         - file type - core? class? helper? model?
631
     * @param mixed  $arguments
632
     * @param bool   $load_only
633
     * @return bool|EE_Addon|object
634
     * @throws EE_Error
635
     * @throws ReflectionException
636
     */
637
    public function load_addon($path_to_file, $class_name, $type = 'class', $arguments = array(), $load_only = false)
638
    {
639
        // retrieve instantiated class
640
        return $this->_load(
641
            $path_to_file,
642
            'addon',
643
            $class_name,
644
            $type,
645
            $arguments,
646
            false,
647
            true,
648
            $load_only
649
        );
650
    }
651
652
653
654
    /**
655
     * instantiates, caches, and automatically resolves dependencies
656
     * for classes that use a Fully Qualified Class Name.
657
     * if the class is not capable of being loaded using PSR-4 autoloading,
658
     * then you need to use one of the existing load_*() methods
659
     * which can resolve the classname and filepath from the passed arguments
660
     *
661
     * @param bool|string $class_name   Fully Qualified Class Name
662
     * @param array       $arguments    an argument, or array of arguments to pass to the class upon instantiation
663
     * @param bool        $cache        whether to cache the instantiated object for reuse
664
     * @param bool        $from_db      some classes are instantiated from the db
665
     *                                  and thus call a different method to instantiate
666
     * @param bool        $load_only    if true, will only load the file, but will NOT instantiate an object
667
     * @param bool|string $addon        if true, will cache the object in the EE_Registry->$addons array
668
     * @return bool|null|mixed          null = failure to load or instantiate class object.
669
     *                                  object = class loaded and instantiated successfully.
670
     *                                  bool = fail or success when $load_only is true
671
     * @throws EE_Error
672
     * @throws ReflectionException
673
     */
674
    public function create(
675
        $class_name = false,
676
        $arguments = array(),
677
        $cache = false,
678
        $from_db = false,
679
        $load_only = false,
680
        $addon = false
681
    ) {
682
        try {
683
            $class_name = ltrim($class_name, '\\');
684
            $class_name = $this->_dependency_map->get_alias($class_name);
685
            $class_exists =
686
                $this->loadOrVerifyClassExists($class_name,
687
                    $arguments);// if a non-FQCN was passed, then verifyClassExists() might return an object
688
            // or it could return null if the class just could not be found anywhere
689
            if ($class_exists instanceof $class_name || $class_exists === null) {
690
                // either way, return the results
691
                return $class_exists;
692
            }
693
            $class_name =
694
                $class_exists;// if we're only loading the class and it already exists, then let's just return true immediately
695
            if ($load_only) {
696
                return true;
697
            }
698
            $addon = $addon
699
                ? 'addon'
700
                : '';// $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
701
            // $cache is controlled by individual calls to separate Registry loader methods like load_class()
702
            // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
703 View Code Duplication
            if ($this->_cache_on && $cache && ! $load_only) {
704
                // return object if it's already cached
705
                $cached_class = $this->_get_cached_class($class_name, $addon, $arguments);
706
                if ($cached_class !== null) {
707
                    return $cached_class;
708
                }
709
            }// obtain the loader method from the dependency map
710
            $loader = $this->_dependency_map->class_loader($class_name);// instantiate the requested object
711
            if ($loader instanceof Closure) {
712
                $class_obj = $loader($arguments);
713
            } else {
714
                if ($loader && method_exists($this, $loader)) {
715
                    $class_obj = $this->{$loader}($class_name, $arguments);
716
                } else {
717
                    $class_obj = $this->_create_object($class_name, $arguments, $addon, $from_db);
718
                }
719
            }
720
            if (($this->_cache_on && $cache) || $this->get_class_abbreviation($class_name, '')) {
721
                // save it for later... kinda like gum  { : $
722
                $this->_set_cached_class($class_obj, $class_name, $addon, $from_db, $arguments);
723
            }
724
            $this->_cache_on = true;
725
            return $class_obj;
726
        } catch (Exception $exception) {
727
            new \EventEspresso\core\exceptions\ExceptionStackTraceDisplay($exception);
728
        } catch (Error $e) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
729
            new \EventEspresso\core\exceptions\ExceptionStackTraceDisplay(
730
                new Exception($e->getMessage())
731
            );
732
            exit();
733
        }
734
    }
735
736
737
738
    /**
739
     * Recursively checks that a class exists and potentially attempts to load classes with non-FQCNs
740
     *
741
     * @param string $class_name
742
     * @param array  $arguments
743
     * @param int    $attempt
744
     * @return mixed
745
     */
746
    private function loadOrVerifyClassExists($class_name, array $arguments, $attempt = 1) {
747
        if (is_object($class_name) || class_exists($class_name)) {
748
            return $class_name;
749
        }
750
        switch ($attempt) {
751
            case 1:
752
                // if it's a FQCN then maybe the class is registered with a preceding \
753
                $class_name = strpos($class_name, '\\') !== false
754
                    ? '\\' . ltrim($class_name, '\\')
755
                    : $class_name;
756
                break;
757
            case 2:
758
                //
759
                $loader = $this->_dependency_map->class_loader($class_name);
760
                if ($loader && method_exists($this, $loader)) {
761
                    return $this->{$loader}($class_name, $arguments);
762
                }
763
                break;
764
            case 3:
765
            default;
766
                return null;
767
        }
768
        $attempt++;
769
        return $this->loadOrVerifyClassExists($class_name, $arguments, $attempt);
770
    }
771
772
773
774
    /**
775
     * instantiates, caches, and injects dependencies for classes
776
     *
777
     * @param array       $file_paths   an array of paths to folders to look in
778
     * @param string      $class_prefix EE  or EEM or... ???
779
     * @param bool|string $class_name   $class name
780
     * @param string      $type         file type - core? class? helper? model?
781
     * @param mixed       $arguments    an argument or array of arguments to pass to the class upon instantiation
782
     * @param bool        $from_db      some classes are instantiated from the db
783
     *                                  and thus call a different method to instantiate
784
     * @param bool        $cache        whether to cache the instantiated object for reuse
785
     * @param bool        $load_only    if true, will only load the file, but will NOT instantiate an object
786
     * @return bool|null|object null = failure to load or instantiate class object.
787
     *                                  object = class loaded and instantiated successfully.
788
     *                                  bool = fail or success when $load_only is true
789
     * @throws EE_Error
790
     * @throws ReflectionException
791
     */
792
    protected function _load(
793
        $file_paths = array(),
794
        $class_prefix = 'EE_',
795
        $class_name = false,
796
        $type = 'class',
797
        $arguments = array(),
798
        $from_db = false,
799
        $cache = true,
800
        $load_only = false
801
    ) {
802
        try {
803
            $class_name = ltrim($class_name, '\\');
804
            // strip php file extension
805
            $class_name = str_replace('.php', '', trim($class_name));
806
            // does the class have a prefix ?
807
            if (! empty($class_prefix) && $class_prefix !== 'addon') {
808
                // make sure $class_prefix is uppercase
809
                $class_prefix = strtoupper(trim($class_prefix));
810
                // add class prefix ONCE!!!
811
                $class_name = $class_prefix . str_replace($class_prefix, '', $class_name);
812
            }
813
            $class_name = $this->_dependency_map->get_alias($class_name);
814
            $class_exists = class_exists($class_name, false);
815
            // if we're only loading the class and it already exists, then let's just return true immediately
816
            if ($load_only && $class_exists) {
817
                return true;
818
            }
819
            // $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
820
            // $cache is controlled by individual calls to separate Registry loader methods like load_class()
821
            // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
822 View Code Duplication
            if ($this->_cache_on && $cache && ! $load_only) {
823
                // return object if it's already cached
824
                $cached_class = $this->_get_cached_class($class_name, $class_prefix, $arguments);
825
                if ($cached_class !== null) {
826
                    return $cached_class;
827
                }
828
            }
829
            // if the class doesn't already exist.. then we need to try and find the file and load it
830
            if (! $class_exists) {
831
                // get full path to file
832
                $path = $this->_resolve_path($class_name, $type, $file_paths);
833
                // load the file
834
                $loaded = $this->_require_file($path, $class_name, $type, $file_paths);
835
                // if loading failed, or we are only loading a file but NOT instantiating an object
836
                if (! $loaded || $load_only) {
837
                    // return boolean if only loading, or null if an object was expected
838
                    return $load_only
839
                        ? $loaded
840
                        : null;
841
                }
842
            }
843
            // instantiate the requested object
844
            $class_obj = $this->_create_object($class_name, $arguments, $type, $from_db);
845
            if ($this->_cache_on && $cache) {
846
                // save it for later... kinda like gum  { : $
847
                $this->_set_cached_class($class_obj, $class_name, $class_prefix, $from_db, $arguments);
848
            }
849
            $this->_cache_on = true;
850
            return $class_obj;
851
        } catch (Exception $exception) {
852
            new \EventEspresso\core\exceptions\ExceptionStackTraceDisplay($exception);
853
        }
854
855
    }
856
857
858
859
    /**
860
     * @param string $class_name
861
     * @param string $default have to specify something, but not anything that will conflict
862
     * @return mixed|string
863
     */
864
    protected function get_class_abbreviation($class_name, $default = 'FANCY_BATMAN_PANTS')
865
    {
866
        return isset($this->_class_abbreviations[$class_name])
867
            ? $this->_class_abbreviations[$class_name]
868
            : $default;
869
    }
870
871
872
    /**
873
     * attempts to find a cached version of the requested class
874
     * by looking in the following places:
875
     *        $this->{$class_abbreviation}            ie:    $this->CART
876
     *        $this->{$class_name}                        ie:    $this->Some_Class
877
     *        $this->LIB->{$class_name}                ie:    $this->LIB->Some_Class
878
     *        $this->addon->{$class_name}    ie:    $this->addon->Some_Addon_Class
879
     *
880
     * @param string $class_name
881
     * @param string $class_prefix
882
     * @param array  $arguments
883
     * @return mixed
884
     */
885
    protected function _get_cached_class(
886
        $class_name,
887
        $class_prefix = '',
888
        $arguments = array()
889
    ) {
890
        if ($class_name === 'EE_Registry') {
891
            return $this;
892
        }
893
        $class_abbreviation = $this->get_class_abbreviation($class_name);
894
        // check if class has already been loaded, and return it if it has been
895
        if (isset($this->{$class_abbreviation})) {
896
            return $this->{$class_abbreviation};
897
        }
898
        $class_name = str_replace('\\', '_', $class_name);
899
        if (isset ($this->{$class_name})) {
900
            return $this->{$class_name};
901
        }
902
        if ($class_prefix === 'addon' && isset ($this->addons->{$class_name})) {
903
            return $this->addons->{$class_name};
904
        }
905
        $class_identifier = $this->getClassIdentifier($class_name, $arguments);
906
        if(
907
            strpos($class_identifier, 'SharedClassToLoad') !== false
908
            || strpos($class_identifier, 'Domain') !== false
909
        ) {
910
            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
911
            \EEH_Debug_Tools::printr($class_name, '$fqcn', __FILE__, __LINE__);
912
            \EEH_Debug_Tools::printr($class_identifier, '$class_identifier', __FILE__, __LINE__);
913
            \EEH_Debug_Tools::printr($arguments, '$arguments', __FILE__, __LINE__);
914
            \EEH_Debug_Tools::printr(isset($this->LIB->{$class_identifier}), 'isset($this->LIB->{$class_identifier})', __FILE__, __LINE__);
915
        }
916
917
        if (isset($this->LIB->{$class_identifier})) {
918
            return $this->LIB->{$class_identifier};
919
        }
920
        foreach ($this->LIB as $key => $object) {
921
            if (
922
                // request does not contain new arguments and therfore no args identifier
923
                strpos($class_identifier, '||') === 0
924
                // but previously cached class with args was found
925
                && strpos($key, $class_name . '||') === 0
926
            ) {
927
                if(strpos($class_identifier, 'SharedClassToLoad') !== false) {
928
                    \EEH_Debug_Tools::printr($class_identifier, 'FOUND', __FILE__, __LINE__);
929
                }
930
931
                return $object;
932
            }
933
934
        }
935
        return null;
936
    }
937
938
939
    /**
940
     * removes a cached version of the requested class
941
     *
942
     * @param string  $class_name
943
     * @param boolean $addon
944
     * @param array   $arguments
945
     * @return boolean
946
     */
947
    public function clear_cached_class(
948
        $class_name,
949
        $addon = false,
950
        $arguments = array()
951
    ) {
952
        $class_abbreviation = $this->get_class_abbreviation($class_name);
953
        // check if class has already been loaded, and return it if it has been
954
        if (isset($this->{$class_abbreviation})) {
955
            $this->{$class_abbreviation} = null;
956
            return true;
957
        }
958
        $class_name = str_replace('\\', '_', $class_name);
959
        if (isset($this->{$class_name})) {
960
            $this->{$class_name} = null;
961
            return true;
962
        }
963
        if ($addon && isset($this->addons->{$class_name})) {
964
            unset($this->addons->{$class_name});
965
            return true;
966
        }
967
        $class_name = $this->getClassIdentifier($class_name, $arguments);
968
        if (isset($this->LIB->{$class_name})) {
969
            unset($this->LIB->{$class_name});
970
            return true;
971
        }
972
        return false;
973
    }
974
975
976
    /**
977
     * _set_cached_class
978
     * attempts to cache the instantiated class locally
979
     * in one of the following places, in the following order:
980
     *        $this->{class_abbreviation}   ie:    $this->CART
981
     *        $this->{$class_name}          ie:    $this->Some_Class
982
     *        $this->addon->{$$class_name}    ie:    $this->addon->Some_Addon_Class
983
     *        $this->LIB->{$class_name}     ie:    $this->LIB->Some_Class
984
     *
985
     * @param object $class_obj
986
     * @param string $class_name
987
     * @param string $class_prefix
988
     * @param bool   $from_db
989
     * @param array  $arguments
990
     * @return void
991
     */
992
    protected function _set_cached_class(
993
        $class_obj,
994
        $class_name,
995
        $class_prefix = '',
996
        $from_db = false,
997
        $arguments = array()
998
    ) {
999
        if ($class_name === 'EE_Registry' || empty($class_obj)) {
1000
            return;
1001
        }
1002
        // return newly instantiated class
1003
        $class_abbreviation = $this->get_class_abbreviation($class_name, '');
1004
        if ($class_abbreviation) {
1005
            $this->{$class_abbreviation} = $class_obj;
1006
            return;
1007
        }
1008
        $class_name = str_replace('\\', '_', $class_name);
1009
        if (property_exists($this, $class_name)) {
1010
            $this->{$class_name} = $class_obj;
1011
            return;
1012
        }
1013
        if ($class_prefix === 'addon') {
1014
            $this->addons->{$class_name} = $class_obj;
1015
            return;
1016
        }
1017
        if (! $from_db) {
1018
            $class_name = $this->getClassIdentifier($class_name, $arguments);
1019
            $this->LIB->{$class_name} = $class_obj;
1020
        }
1021
    }
1022
1023
1024
1025
    /**
1026
     * build a string representation of a class' name and arguments
1027
     *
1028
     * @param string $class_name
1029
     * @param array $arguments
1030
     * @return string
1031
     */
1032
    private function getClassIdentifier($class_name, $arguments = array())
1033
    {
1034
        $identifier = $this->getIdentifierForArguments($arguments);
1035
        $class_name2 = $class_name;
1036
        if(!empty($identifier)) {
1037
            $class_name2 =  $class_name . '||' . md5($identifier);
1038
        }
1039
        if(
1040
            $class_name === 'EventEspresso_tests_mocks_core_services_loaders_SharedClassToLoad'
1041
            || $class_name === 'EventEspresso_core_domain_Domain'
1042
        ) {
1043
            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
1044
            \EEH_Debug_Tools::printr($class_name, '$class_name', __FILE__, __LINE__);
1045
            \EEH_Debug_Tools::printr($class_name2, '$class_name + id', __FILE__, __LINE__);
1046
            \EEH_Debug_Tools::printr($identifier, '$identifier', __FILE__, __LINE__);
1047
        }
1048
        return $class_name2;
1049
    }
1050
1051
1052
1053
    /**
1054
     * build a string representation of a class' arguments
1055
     * (mostly because Closures can't be serialized)
1056
     *
1057
     * @param array $arguments
1058
     * @return string
1059
     */
1060
    private function getIdentifierForArguments($arguments = array())
1061
    {
1062
        if(empty($arguments)){
1063
            return '';
1064
        }
1065
        $identifier = '';
1066
        $arguments = is_array($arguments) ? $arguments : array();
1067 View Code Duplication
        foreach ($arguments as $argument) {
1068
            switch (true) {
1069
                case is_object($argument) :
1070
                case $argument instanceof Closure :
1071
                    $identifier .= spl_object_hash($argument);
1072
                    break;
1073
                case is_array($argument) :
1074
                    $identifier .= $this->getIdentifierForArguments($argument);
1075
                    break;
1076
                default :
1077
                    $identifier .= $argument;
1078
                    break;
1079
            }
1080
        }
1081
        return $identifier;
1082
    }
1083
1084
1085
1086
    /**
1087
     * attempts to find a full valid filepath for the requested class.
1088
     * loops thru each of the base paths in the $file_paths array and appends : "{classname} . {file type} . php"
1089
     * then returns that path if the target file has been found and is readable
1090
     *
1091
     * @param string $class_name
1092
     * @param string $type
1093
     * @param array  $file_paths
1094
     * @return string | bool
1095
     */
1096
    protected function _resolve_path($class_name, $type = '', $file_paths = array())
1097
    {
1098
        // make sure $file_paths is an array
1099
        $file_paths = is_array($file_paths)
1100
            ? $file_paths
1101
            : array($file_paths);
1102
        // cycle thru paths
1103
        foreach ($file_paths as $key => $file_path) {
1104
            // convert all separators to proper DS, if no filepath, then use EE_CLASSES
1105
            $file_path = $file_path
1106
                ? str_replace(array('/', '\\'), DS, $file_path)
1107
                : EE_CLASSES;
1108
            // prep file type
1109
            $type = ! empty($type)
1110
                ? trim($type, '.') . '.'
1111
                : '';
1112
            // build full file path
1113
            $file_paths[$key] = rtrim($file_path, DS) . DS . $class_name . '.' . $type . 'php';
1114
            //does the file exist and can be read ?
1115
            if (is_readable($file_paths[$key])) {
1116
                return $file_paths[$key];
1117
            }
1118
        }
1119
        return false;
1120
    }
1121
1122
1123
1124
    /**
1125
     * basically just performs a require_once()
1126
     * but with some error handling
1127
     *
1128
     * @param  string $path
1129
     * @param  string $class_name
1130
     * @param  string $type
1131
     * @param  array  $file_paths
1132
     * @return bool
1133
     * @throws EE_Error
1134
     * @throws ReflectionException
1135
     */
1136
    protected function _require_file($path, $class_name, $type = '', $file_paths = array())
1137
    {
1138
        $this->resolve_legacy_class_parent($class_name);
1139
        // don't give up! you gotta...
1140
        try {
1141
            //does the file exist and can it be read ?
1142
            if (! $path) {
1143
                // just in case the file has already been autoloaded,
1144
                // but discrepancies in the naming schema are preventing it from
1145
                // being loaded via one of the EE_Registry::load_*() methods,
1146
                // then let's try one last hail mary before throwing an exception
1147
                // and call class_exists() again, but with autoloading turned ON
1148
                if(class_exists($class_name)) {
1149
                    return true;
1150
                }
1151
                // so sorry, can't find the file
1152
                throw new EE_Error (
1153
                    sprintf(
1154
                        esc_html__(
1155
                            'The %1$s file %2$s could not be located or is not readable due to file permissions. Please ensure that the following filepath(s) are correct: %3$s',
1156
                            'event_espresso'
1157
                        ),
1158
                        trim($type, '.'),
1159
                        $class_name,
1160
                        '<br />' . implode(',<br />', $file_paths)
1161
                    )
1162
                );
1163
            }
1164
            // get the file
1165
            require_once($path);
1166
            // if the class isn't already declared somewhere
1167
            if (class_exists($class_name, false) === false) {
1168
                // so sorry, not a class
1169
                throw new EE_Error(
1170
                    sprintf(
1171
                        esc_html__('The %s file %s does not appear to contain the %s Class.', 'event_espresso'),
1172
                        $type,
1173
                        $path,
1174
                        $class_name
1175
                    )
1176
                );
1177
            }
1178
        } catch (EE_Error $e) {
1179
            $e->get_error();
1180
            return false;
1181
        }
1182
        return true;
1183
    }
1184
1185
1186
1187
    /**
1188
     * Some of our legacy classes that extended a parent class would simply use a require() statement
1189
     * before their class declaration in order to ensure that the parent class was loaded.
1190
     * This is not ideal, but it's nearly impossible to determine the parent class of a non-namespaced class,
1191
     * without triggering a fatal error because the parent class has yet to be loaded and therefore doesn't exist.
1192
     *
1193
     * @param string $class_name
1194
     */
1195
    protected function resolve_legacy_class_parent($class_name = '')
1196
    {
1197
        try {
1198
            $legacy_parent_class_map = array(
1199
                'EE_Payment_Processor' => 'core/business/EE_Processor_Base.class.php'
1200
            );
1201
            if(isset($legacy_parent_class_map[$class_name])) {
1202
                require_once EE_PLUGIN_DIR_PATH . $legacy_parent_class_map[$class_name];
1203
            }
1204
        } catch (Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1205
        }
1206
    }
1207
1208
1209
1210
    /**
1211
     * _create_object
1212
     * Attempts to instantiate the requested class via any of the
1213
     * commonly used instantiation methods employed throughout EE.
1214
     * The priority for instantiation is as follows:
1215
     *        - abstract classes or any class flagged as "load only" (no instantiation occurs)
1216
     *        - model objects via their 'new_instance_from_db' method
1217
     *        - model objects via their 'new_instance' method
1218
     *        - "singleton" classes" via their 'instance' method
1219
     *    - standard instantiable classes via their __constructor
1220
     * Prior to instantiation, if the classname exists in the dependency_map,
1221
     * then the constructor for the requested class will be examined to determine
1222
     * if any dependencies exist, and if they can be injected.
1223
     * If so, then those classes will be added to the array of arguments passed to the constructor
1224
     *
1225
     * @param string $class_name
1226
     * @param array  $arguments
1227
     * @param string $type
1228
     * @param bool   $from_db
1229
     * @return null|object
1230
     * @throws EE_Error
1231
     * @throws ReflectionException
1232
     */
1233
    protected function _create_object($class_name, $arguments = array(), $type = '', $from_db = false)
1234
    {
1235
        // create reflection
1236
        $reflector = $this->get_ReflectionClass($class_name);
1237
        // make sure arguments are an array
1238
        $arguments = is_array($arguments)
1239
            ? $arguments
1240
            : array($arguments);
1241
        // and if arguments array is numerically and sequentially indexed, then we want it to remain as is,
1242
        // else wrap it in an additional array so that it doesn't get split into multiple parameters
1243
        $arguments = $this->_array_is_numerically_and_sequentially_indexed($arguments)
1244
            ? $arguments
1245
            : array($arguments);
1246
        // attempt to inject dependencies ?
1247
        if ($this->_dependency_map->has($class_name)) {
1248
            $arguments = $this->_resolve_dependencies($reflector, $class_name, $arguments);
1249
        }
1250
        // instantiate the class if possible
1251
        if ($reflector->isAbstract()) {
1252
            // nothing to instantiate, loading file was enough
1253
            // does not throw an exception so $instantiation_mode is unused
1254
            // $instantiation_mode = "1) no constructor abstract class";
1255
            return true;
1256
        }
1257
        if (empty($arguments) && $reflector->getConstructor() === null && $reflector->isInstantiable()) {
1258
            // no constructor = static methods only... nothing to instantiate, loading file was enough
1259
            // $instantiation_mode = "2) no constructor but instantiable";
1260
            return $reflector->newInstance();
1261
        }
1262
        if ($from_db && method_exists($class_name, 'new_instance_from_db')) {
1263
            // $instantiation_mode = "3) new_instance_from_db()";
1264
            return call_user_func_array(array($class_name, 'new_instance_from_db'), $arguments);
1265
        }
1266
        if (method_exists($class_name, 'new_instance')) {
1267
            // $instantiation_mode = "4) new_instance()";
1268
            return call_user_func_array(array($class_name, 'new_instance'), $arguments);
1269
        }
1270
        if (method_exists($class_name, 'instance')) {
1271
            // $instantiation_mode = "5) instance()";
1272
            return call_user_func_array(array($class_name, 'instance'), $arguments);
1273
        }
1274
        if ($reflector->isInstantiable()) {
1275
            // $instantiation_mode = "6) constructor";
1276
            return $reflector->newInstanceArgs($arguments);
1277
        }
1278
        // heh ? something's not right !
1279
        throw new EE_Error(
1280
            sprintf(
1281
                __('The %s file %s could not be instantiated.', 'event_espresso'),
1282
                $type,
1283
                $class_name
1284
            )
1285
        );
1286
    }
1287
1288
1289
1290
    /**
1291
     * @see http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
1292
     * @param array $array
1293
     * @return bool
1294
     */
1295
    protected function _array_is_numerically_and_sequentially_indexed(array $array)
1296
    {
1297
        return ! empty($array)
1298
            ? array_keys($array) === range(0, count($array) - 1)
1299
            : true;
1300
    }
1301
1302
1303
1304
    /**
1305
     * getReflectionClass
1306
     * checks if a ReflectionClass object has already been generated for a class
1307
     * and returns that instead of creating a new one
1308
     *
1309
     * @param string $class_name
1310
     * @return ReflectionClass
1311
     * @throws ReflectionException
1312
     */
1313 View Code Duplication
    public function get_ReflectionClass($class_name)
1314
    {
1315
        if (
1316
            ! isset($this->_reflectors[$class_name])
1317
            || ! $this->_reflectors[$class_name] instanceof ReflectionClass
1318
        ) {
1319
            $this->_reflectors[$class_name] = new ReflectionClass($class_name);
1320
        }
1321
        return $this->_reflectors[$class_name];
1322
    }
1323
1324
1325
1326
    /**
1327
     * _resolve_dependencies
1328
     * examines the constructor for the requested class to determine
1329
     * if any dependencies exist, and if they can be injected.
1330
     * If so, then those classes will be added to the array of arguments passed to the constructor
1331
     * PLZ NOTE: this is achieved by type hinting the constructor params
1332
     * For example:
1333
     *        if attempting to load a class "Foo" with the following constructor:
1334
     *        __construct( Bar $bar_class, Fighter $grohl_class )
1335
     *        then $bar_class and $grohl_class will be added to the $arguments array,
1336
     *        but only IF they are NOT already present in the incoming arguments array,
1337
     *        and the correct classes can be loaded
1338
     *
1339
     * @param ReflectionClass $reflector
1340
     * @param string          $class_name
1341
     * @param array           $arguments
1342
     * @return array
1343
     * @throws EE_Error
1344
     * @throws ReflectionException
1345
     */
1346
    protected function _resolve_dependencies(ReflectionClass $reflector, $class_name, $arguments = array())
1347
    {
1348
        // let's examine the constructor
1349
        $constructor = $reflector->getConstructor();
1350
        // whu? huh? nothing?
1351
        if (! $constructor) {
1352
            return $arguments;
1353
        }
1354
        // get constructor parameters
1355
        $params = $constructor->getParameters();
1356
        // and the keys for the incoming arguments array so that we can compare existing arguments with what is expected
1357
        $argument_keys = array_keys($arguments);
1358
        // now loop thru all of the constructors expected parameters
1359
        foreach ($params as $index => $param) {
1360
            // is this a dependency for a specific class ?
1361
            $param_class = $param->getClass()
1362
                ? $param->getClass()->name
1363
                : null;
1364
            // BUT WAIT !!! This class may be an alias for something else (or getting replaced at runtime)
1365
            $param_class = $this->_dependency_map->has_alias($param_class, $class_name)
1366
                ? $this->_dependency_map->get_alias($param_class, $class_name)
1367
                : $param_class;
1368
            if (
1369
                // param is not even a class
1370
                $param_class === null
1371
                // and something already exists in the incoming arguments for this param
1372
                && array_key_exists($index, $argument_keys)
1373
                && array_key_exists($argument_keys[$index], $arguments)
1374
            ) {
1375
                // so let's skip this argument and move on to the next
1376
                continue;
1377
            }
1378
            if (
1379
                // parameter is type hinted as a class, exists as an incoming argument, AND it's the correct class
1380
                $param_class !== null
1381
                && isset($argument_keys[$index], $arguments[$argument_keys[$index]])
1382
                && $arguments[$argument_keys[$index]] instanceof $param_class
1383
            ) {
1384
                // skip this argument and move on to the next
1385
                continue;
1386
            }
1387
            if (
1388
                // parameter is type hinted as a class, and should be injected
1389
                $param_class !== null
1390
                && $this->_dependency_map->has_dependency_for_class($class_name, $param_class)
1391
            ) {
1392
                $arguments = $this->_resolve_dependency(
1393
                    $class_name,
1394
                    $param_class,
1395
                    $arguments,
1396
                    $index,
1397
                    $argument_keys
1398
                );
1399
            } else {
1400
                try {
1401
                    $arguments[$index] = $param->isDefaultValueAvailable()
1402
                        ? $param->getDefaultValue()
1403
                        : null;
1404
                } catch (ReflectionException $e) {
1405
                    throw new ReflectionException(
1406
                        sprintf(
1407
                            esc_html__('%1$s for parameter "$%2$s on classname "%3$s"', 'event_espresso'),
1408
                            $e->getMessage(),
1409
                            $param->getName(),
1410
                            $class_name
1411
                        )
1412
                    );
1413
                }
1414
            }
1415
        }
1416
        return $arguments;
1417
    }
1418
1419
1420
1421
    /**
1422
     * @param string $class_name
1423
     * @param string $param_class
1424
     * @param array  $arguments
1425
     * @param mixed  $index
1426
     * @param array  $argument_keys
1427
     * @return array
1428
     * @throws EE_Error
1429
     * @throws ReflectionException
1430
     * @throws InvalidArgumentException
1431
     * @throws InvalidInterfaceException
1432
     * @throws InvalidDataTypeException
1433
     */
1434
    protected function _resolve_dependency($class_name, $param_class, $arguments, $index, array $argument_keys)
1435
    {
1436
        $dependency = null;
1437
        // should dependency be loaded from cache ?
1438
        $cache_on = $this->_dependency_map->loading_strategy_for_class_dependency(
1439
            $class_name,
1440
            $param_class
1441
        );
1442
        $cache_on = $cache_on !== EE_Dependency_Map::load_new_object;
1443
        // we might have a dependency...
1444
        // let's MAYBE try and find it in our cache if that's what's been requested
1445
        $cached_class = $cache_on
1446
            ? $this->_get_cached_class($param_class, '', $arguments)
1447
            : null;
1448
        // and grab it if it exists
1449
        if ($cached_class instanceof $param_class) {
1450
            $dependency = $cached_class;
1451
        } else if ($param_class !== $class_name) {
1452
            // obtain the loader method from the dependency map
1453
            $loader = $this->_dependency_map->class_loader($param_class);
1454
            // is loader a custom closure ?
1455
            if ($loader instanceof Closure) {
1456
                $dependency = $loader($arguments);
1457
            } else {
1458
                // set the cache on property for the recursive loading call
1459
                $this->_cache_on = $cache_on;
1460
                // if not, then let's try and load it via the registry
1461
                if ($loader && method_exists($this, $loader)) {
1462
                    $dependency = $this->{$loader}($param_class);
1463
                } else {
1464
                    $dependency = LoaderFactory::getLoader()->load(
1465
                        $param_class,
1466
                        array(),
1467
                        $cache_on
1468
                    );
1469
                }
1470
            }
1471
        }
1472
        // did we successfully find the correct dependency ?
1473
        if ($dependency instanceof $param_class) {
1474
            // then let's inject it into the incoming array of arguments at the correct location
1475
            $arguments[$index] = $dependency;
1476
        }
1477
        return $arguments;
1478
    }
1479
1480
1481
1482
    /**
1483
     * call any loader that's been registered in the EE_Dependency_Map::$_class_loaders array
1484
     *
1485
     * @param string $classname PLEASE NOTE: the class name needs to match what's registered
1486
     *                          in the EE_Dependency_Map::$_class_loaders array,
1487
     *                          including the class prefix, ie: "EE_", "EEM_", "EEH_", etc
1488
     * @param array  $arguments
1489
     * @return object
1490
     */
1491
    public static function factory($classname, $arguments = array())
1492
    {
1493
        $loader = self::instance()->_dependency_map->class_loader($classname);
1494
        if ($loader instanceof Closure) {
1495
            return $loader($arguments);
1496
        }
1497
        if (method_exists(self::instance(), $loader)) {
1498
            return self::instance()->{$loader}($classname, $arguments);
1499
        }
1500
        return null;
1501
    }
1502
1503
1504
1505
    /**
1506
     * Gets the addon by its class name
1507
     *
1508
     * @param string $class_name
1509
     * @return EE_Addon
1510
     */
1511
    public function getAddon($class_name)
1512
    {
1513
        $class_name = str_replace('\\', '_', $class_name);
1514
        return $this->addons->{$class_name};
1515
    }
1516
1517
1518
    /**
1519
     * removes the addon from the internal cache
1520
     *
1521
     * @param string $class_name
1522
     * @return void
1523
     */
1524
    public function removeAddon($class_name)
1525
    {
1526
        $class_name = str_replace('\\', '_', $class_name);
1527
        unset($this->addons->{$class_name});
1528
    }
1529
1530
1531
1532
    /**
1533
     * Gets the addon by its name/slug (not classname. For that, just
1534
     * use the get_addon() method above
1535
     *
1536
     * @param string $name
1537
     * @return EE_Addon
1538
     */
1539
    public function get_addon_by_name($name)
1540
    {
1541
        foreach ($this->addons as $addon) {
1542
            if ($addon->name() === $name) {
1543
                return $addon;
1544
            }
1545
        }
1546
        return null;
1547
    }
1548
1549
1550
1551
    /**
1552
     * Gets an array of all the registered addons, where the keys are their names.
1553
     * (ie, what each returns for their name() function)
1554
     * They're already available on EE_Registry::instance()->addons as properties,
1555
     * where each property's name is the addon's classname,
1556
     * So if you just want to get the addon by classname,
1557
     * OR use the get_addon() method above.
1558
     * PLEASE  NOTE:
1559
     * addons with Fully Qualified Class Names
1560
     * have had the namespace separators converted to underscores,
1561
     * so a classname like Fully\Qualified\ClassName
1562
     * would have been converted to Fully_Qualified_ClassName
1563
     *
1564
     * @return EE_Addon[] where the KEYS are the addon's name()
1565
     */
1566
    public function get_addons_by_name()
1567
    {
1568
        $addons = array();
1569
        foreach ($this->addons as $addon) {
1570
            $addons[$addon->name()] = $addon;
1571
        }
1572
        return $addons;
1573
    }
1574
1575
1576
    /**
1577
     * Resets the specified model's instance AND makes sure EE_Registry doesn't keep
1578
     * a stale copy of it around
1579
     *
1580
     * @param string $model_name
1581
     * @return \EEM_Base
1582
     * @throws \EE_Error
1583
     */
1584
    public function reset_model($model_name)
1585
    {
1586
        $model_class_name = strpos($model_name, 'EEM_') !== 0
1587
            ? "EEM_{$model_name}"
1588
            : $model_name;
1589
        if (! isset($this->LIB->{$model_class_name}) || ! $this->LIB->{$model_class_name} instanceof EEM_Base) {
1590
            return null;
1591
        }
1592
        //get that model reset it and make sure we nuke the old reference to it
1593
        if ($this->LIB->{$model_class_name} instanceof $model_class_name
1594
            && is_callable(
1595
                array($model_class_name, 'reset')
1596
            )) {
1597
            $this->LIB->{$model_class_name} = $this->LIB->{$model_class_name}->reset();
1598
        } else {
1599
            throw new EE_Error(sprintf(esc_html__('Model %s does not have a method "reset"', 'event_espresso'), $model_name));
1600
        }
1601
        return $this->LIB->{$model_class_name};
1602
    }
1603
1604
1605
1606
    /**
1607
     * Resets the registry.
1608
     * The criteria for what gets reset is based on what can be shared between sites on the same request when
1609
     * switch_to_blog is used in a multisite install.  Here is a list of things that are NOT reset.
1610
     * - $_dependency_map
1611
     * - $_class_abbreviations
1612
     * - $NET_CFG (EE_Network_Config): The config is shared network wide so no need to reset.
1613
     * - $REQ:  Still on the same request so no need to change.
1614
     * - $CAP: There is no site specific state in the EE_Capability class.
1615
     * - $SSN: Although ideally, the session should not be shared between site switches, we can't reset it because only
1616
     * one Session can be active in a single request.  Resetting could resolve in "headers already sent" errors.
1617
     * - $addons:  In multisite, the state of the addons is something controlled via hooks etc in a normal request.  So
1618
     *             for now, we won't reset the addons because it could break calls to an add-ons class/methods in the
1619
     *             switch or on the restore.
1620
     * - $modules
1621
     * - $shortcodes
1622
     * - $widgets
1623
     *
1624
     * @param boolean $hard             [deprecated]
1625
     * @param boolean $reinstantiate    whether to create new instances of EE_Registry's singletons too,
1626
     *                                  or just reset without re-instantiating (handy to set to FALSE if you're not
1627
     *                                  sure if you CAN currently reinstantiate the singletons at the moment)
1628
     * @param   bool  $reset_models     Defaults to true.  When false, then the models are not reset.  This is so
1629
     *                                  client
1630
     *                                  code instead can just change the model context to a different blog id if
1631
     *                                  necessary
1632
     * @return EE_Registry
1633
     * @throws EE_Error
1634
     * @throws ReflectionException
1635
     */
1636
    public static function reset($hard = false, $reinstantiate = true, $reset_models = true)
1637
    {
1638
        $instance = self::instance();
1639
        $instance->_cache_on = true;
1640
        // reset some "special" classes
1641
        EEH_Activation::reset();
1642
        $hard = apply_filters( 'FHEE__EE_Registry__reset__hard', $hard);
1643
        $instance->CFG = EE_Config::reset($hard, $reinstantiate);
1644
        $instance->CART = null;
1645
        $instance->MRM = null;
1646
        $instance->AssetsRegistry = $instance->create('EventEspresso\core\services\assets\Registry');
1647
        //messages reset
1648
        EED_Messages::reset();
1649
        //handle of objects cached on LIB
1650
        foreach (array('LIB', 'modules') as $cache) {
1651
            foreach ($instance->{$cache} as $class_name => $class) {
1652
                if (self::_reset_and_unset_object($class, $reset_models)) {
1653
                    unset($instance->{$cache}->{$class_name});
1654
                }
1655
            }
1656
        }
1657
        return $instance;
1658
    }
1659
1660
1661
1662
    /**
1663
     * if passed object implements ResettableInterface, then call it's reset() method
1664
     * if passed object implements InterminableInterface, then return false,
1665
     * to indicate that it should NOT be cleared from the Registry cache
1666
     *
1667
     * @param      $object
1668
     * @param bool $reset_models
1669
     * @return bool returns true if cached object should be unset
1670
     */
1671
    private static function _reset_and_unset_object($object, $reset_models)
1672
    {
1673
        if (! is_object($object)) {
1674
            // don't unset anything that's not an object
1675
            return false;
1676
        }
1677
        if ($object instanceof EED_Module) {
1678
            $object::reset();
1679
            // don't unset modules
1680
            return false;
1681
        }
1682
        if ($object instanceof ResettableInterface) {
1683
            if ($object instanceof EEM_Base) {
1684
                if ($reset_models) {
1685
                    $object->reset();
1686
                    return true;
1687
                }
1688
                return false;
1689
            }
1690
            $object->reset();
1691
            return true;
1692
        }
1693
        if (! $object instanceof InterminableInterface) {
1694
            return true;
1695
        }
1696
        return false;
1697
    }
1698
1699
1700
1701
    /**
1702
     * Gets all the custom post type models defined
1703
     *
1704
     * @return array keys are model "short names" (Eg "Event") and keys are classnames (eg "EEM_Event")
1705
     */
1706
    public function cpt_models()
1707
    {
1708
        $cpt_models = array();
1709
        foreach ($this->non_abstract_db_models as $short_name => $classname) {
1710
            if (is_subclass_of($classname, 'EEM_CPT_Base')) {
1711
                $cpt_models[$short_name] = $classname;
1712
            }
1713
        }
1714
        return $cpt_models;
1715
    }
1716
1717
1718
1719
    /**
1720
     * @return \EE_Config
1721
     */
1722
    public static function CFG()
1723
    {
1724
        return self::instance()->CFG;
1725
    }
1726
1727
1728
}
1729
// End of file EE_Registry.core.php
1730
// Location: ./core/EE_Registry.core.php
1731