Completed
Branch FET/11450/reserved-instance-in... (cfa977)
by
unknown
69:04 queued 55:58
created

EE_Registry   F

Complexity

Total Complexity 172

Size/Duplication

Total Lines 1607
Duplicated Lines 0.87 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
dl 14
loc 1607
rs 0.6314
c 0
b 0
f 0
wmc 172
lcom 1
cbo 16

43 Methods

Rating   Name   Duplication   Size   Complexity  
A add_module() 0 12 3
A init() 0 8 3
A localize_i18n_js_strings() 0 10 3
B instance() 0 16 5
A __construct() 0 13 1
B initialize() 0 34 1
A get_module() 0 6 2
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 52 15
D loadOrVerifyClassExists() 0 25 9
C _load() 7 59 15
A get_class_abbreviation() 0 6 2
C _get_cached_class() 0 22 7
B clear_cached_class() 0 23 6
B _resolve_path() 0 25 6
B _require_file() 0 48 5
A resolve_legacy_class_parent() 0 12 3
C _create_object() 0 58 13
A _array_is_numerically_and_sequentially_indexed() 0 6 2
C _resolve_dependencies() 0 72 13
C _resolve_dependency() 0 45 8
C _set_cached_class() 0 24 7
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
A get_ReflectionClass() 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
3
use EventEspresso\core\exceptions\InvalidClassException;
4
use EventEspresso\core\exceptions\InvalidDataTypeException;
5
use EventEspresso\core\exceptions\InvalidInterfaceException;
6
use EventEspresso\core\interfaces\InterminableInterface;
7
use EventEspresso\core\interfaces\ResettableInterface;
8
use EventEspresso\core\services\assets\Registry;
9
use EventEspresso\core\services\commands\CommandBusInterface;
10
use EventEspresso\core\services\container\Mirror;
11
use EventEspresso\core\services\container\RegistryContainer;
12
use EventEspresso\core\services\loaders\ClassInterfaceCache;
13
use EventEspresso\core\services\loaders\LoaderFactory;
14
15
defined('EVENT_ESPRESSO_VERSION') || exit;
16
17
18
19
/**
20
 * EE_Registry Class
21
 * Centralized Application Data Storage and Management
22
 *
23
 * @package                   Event Espresso
24
 * @subpackage                core
25
 * @author                    Brent Christensen
26
 */
27
class EE_Registry implements ResettableInterface
28
{
29
30
    /**
31
     * @var EE_Registry $_instance
32
     */
33
    private static $_instance;
34
35
    /**
36
     * @var EE_Dependency_Map $_dependency_map
37
     */
38
    protected $_dependency_map;
39
40
    /**
41
     * @var Mirror
42
     */
43
    private $mirror;
44
45
    /**
46
     * @var ClassInterfaceCache $class_cache
47
     */
48
    private $class_cache;
49
50
    /**
51
     * @var array $_class_abbreviations
52
     */
53
    protected $_class_abbreviations = array();
54
55
    /**
56
     * @var CommandBusInterface $BUS
57
     */
58
    public $BUS;
59
60
    /**
61
     * @var EE_Cart $CART
62
     */
63
    public $CART;
64
65
    /**
66
     * @var EE_Config $CFG
67
     */
68
    public $CFG;
69
70
    /**
71
     * @var EE_Network_Config $NET_CFG
72
     */
73
    public $NET_CFG;
74
75
    /**
76
     * StdClass object for storing library classes in
77
     *
78
     * @var StdClass $LIB
79
     */
80
    public $LIB;
81
82
    /**
83
     * @var EE_Request_Handler $REQ
84
     */
85
    public $REQ;
86
87
    /**
88
     * @var EE_Session $SSN
89
     */
90
    public $SSN;
91
92
    /**
93
     * @since 4.5.0
94
     * @var EE_Capabilities $CAP
95
     */
96
    public $CAP;
97
98
    /**
99
     * @since 4.9.0
100
     * @var EE_Message_Resource_Manager $MRM
101
     */
102
    public $MRM;
103
104
105
    /**
106
     * @var Registry $AssetsRegistry
107
     */
108
    public $AssetsRegistry;
109
110
    /**
111
     * StdClass object for holding addons which have registered themselves to work with EE core
112
     *
113
     * @var EE_Addon[] $addons
114
     */
115
    public $addons;
116
117
    /**
118
     * keys are 'short names' (eg Event), values are class names (eg 'EEM_Event')
119
     *
120
     * @var EEM_Base[] $models
121
     */
122
    public $models = array();
123
124
    /**
125
     * @var EED_Module[] $modules
126
     */
127
    public $modules;
128
129
    /**
130
     * @var EES_Shortcode[] $shortcodes
131
     */
132
    public $shortcodes;
133
134
    /**
135
     * @var WP_Widget[] $widgets
136
     */
137
    public $widgets;
138
139
    /**
140
     * this is an array of all implemented model names (i.e. not the parent abstract models, or models
141
     * which don't actually fetch items from the DB in the normal way (ie, are not children of EEM_Base)).
142
     * Keys are model "short names" (eg "Event") as used in model relations, and values are
143
     * classnames (eg "EEM_Event")
144
     *
145
     * @var array $non_abstract_db_models
146
     */
147
    public $non_abstract_db_models = array();
148
149
150
    /**
151
     * internationalization for JS strings
152
     *    usage:   EE_Registry::i18n_js_strings['string_key'] = esc_html__( 'string to translate.', 'event_espresso' );
153
     *    in js file:  var translatedString = eei18n.string_key;
154
     *
155
     * @var array $i18n_js_strings
156
     */
157
    public static $i18n_js_strings = array();
158
159
160
    /**
161
     * $main_file - path to espresso.php
162
     *
163
     * @var array $main_file
164
     */
165
    public $main_file;
166
167
    /**
168
     * array of ReflectionClass objects where the key is the class name
169
     *
170
     * @deprecated $VID:$
171
     * @var ReflectionClass[] $_reflectors
172
     */
173
    public $_reflectors;
174
175
    /**
176
     * boolean flag to indicate whether or not to load/save dependencies from/to the cache
177
     *
178
     * @var boolean $_cache_on
179
     */
180
    protected $_cache_on = true;
181
182
183
    /**
184
     * @singleton method used to instantiate class object
185
     * @param EE_Dependency_Map|null   $dependency_map
186
     * @param Mirror|null              $mirror
187
     * @param ClassInterfaceCache|null $class_cache
188
     * @return EE_Registry instance
189
     */
190
    public static function instance(
191
        EE_Dependency_Map $dependency_map = null,
192
        Mirror $mirror = null,
193
        ClassInterfaceCache $class_cache = null
194
    ) {
195
        // check if class object is instantiated
196
        if (
197
            ! EE_Registry::$_instance instanceof EE_Registry
198
            && $dependency_map instanceof EE_Dependency_Map
199
            && $mirror instanceof Mirror
200
            && $class_cache instanceof ClassInterfaceCache
201
        ) {
202
            EE_Registry::$_instance = new self($dependency_map, $mirror, $class_cache);
203
        }
204
        return EE_Registry::$_instance;
205
    }
206
207
208
    /**
209
     * protected constructor to prevent direct creation
210
     *
211
     * @Constructor
212
     * @param  EE_Dependency_Map  $dependency_map
213
     * @param Mirror              $mirror
214
     * @param ClassInterfaceCache $class_cache
215
     */
216
    protected function __construct(EE_Dependency_Map $dependency_map, Mirror $mirror, ClassInterfaceCache $class_cache)
217
    {
218
        $this->_dependency_map = $dependency_map;
219
        $this->mirror = $mirror;
220
        $this->class_cache = $class_cache;
221
        // $registry_container = new RegistryContainer();
222
        $this->LIB = new RegistryContainer();
223
        $this->addons = new RegistryContainer();
224
        $this->modules = new RegistryContainer();
225
        $this->shortcodes = new RegistryContainer();
226
        $this->widgets = new RegistryContainer();
227
        add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', array($this, 'initialize'));
228
    }
229
230
231
232
    /**
233
     * initialize
234
     *
235
     * @throws EE_Error
236
     * @throws ReflectionException
237
     */
238
    public function initialize()
239
    {
240
        $this->_class_abbreviations = apply_filters(
241
            'FHEE__EE_Registry____construct___class_abbreviations',
242
            array(
243
                'EE_Config'                                       => 'CFG',
244
                'EE_Session'                                      => 'SSN',
245
                'EE_Capabilities'                                 => 'CAP',
246
                'EE_Cart'                                         => 'CART',
247
                'EE_Network_Config'                               => 'NET_CFG',
248
                'EE_Request_Handler'                              => 'REQ',
249
                'EE_Message_Resource_Manager'                     => 'MRM',
250
                'EventEspresso\core\services\commands\CommandBus' => 'BUS',
251
                'EventEspresso\core\services\assets\Registry'     => 'AssetsRegistry',
252
            )
253
        );
254
        $this->load_core('Base', array(), true);
255
        // add our request and response objects to the cache
256
        $request_loader = $this->_dependency_map->class_loader(
257
            'EventEspresso\core\services\request\Request'
258
        );
259
        $this->_set_cached_class(
260
            $request_loader(),
261
            'EventEspresso\core\services\request\Request'
262
        );
263
        $response_loader = $this->_dependency_map->class_loader(
264
            'EventEspresso\core\services\request\Response'
265
        );
266
        $this->_set_cached_class(
267
            $response_loader(),
268
            'EventEspresso\core\services\request\Response'
269
        );
270
        add_action('AHEE__EE_System__set_hooks_for_core', array($this, 'init'));
271
    }
272
273
274
275
    /**
276
     * @return void
277
     */
278
    public function init()
279
    {
280
        // Get current page protocol
281
        $protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
282
        // Output admin-ajax.php URL with same protocol as current page
283
        self::$i18n_js_strings['ajax_url'] = admin_url('admin-ajax.php', $protocol);
284
        self::$i18n_js_strings['wp_debug'] = defined('WP_DEBUG') ? WP_DEBUG : false;
285
    }
286
287
288
289
    /**
290
     * localize_i18n_js_strings
291
     *
292
     * @return string
293
     */
294
    public static function localize_i18n_js_strings()
295
    {
296
        $i18n_js_strings = (array)self::$i18n_js_strings;
297
        foreach ($i18n_js_strings as $key => $value) {
298
            if (is_scalar($value)) {
299
                $i18n_js_strings[$key] = html_entity_decode((string)$value, ENT_QUOTES, 'UTF-8');
300
            }
301
        }
302
        return '/* <![CDATA[ */ var eei18n = ' . wp_json_encode($i18n_js_strings) . '; /* ]]> */';
303
    }
304
305
306
307
    /**
308
     * @param mixed string | EED_Module $module
309
     * @throws EE_Error
310
     * @throws ReflectionException
311
     */
312
    public function add_module($module)
313
    {
314
        if ($module instanceof EED_Module) {
315
            $module_class = get_class($module);
316
            $this->modules->{$module_class} = $module;
317
        } else {
318
            if ( ! class_exists('EE_Module_Request_Router', false)) {
319
                $this->load_core('Module_Request_Router');
320
            }
321
            EE_Module_Request_Router::module_factory($module);
322
        }
323
    }
324
325
326
327
    /**
328
     * @param string $module_name
329
     * @return mixed EED_Module | NULL
330
     */
331
    public function get_module($module_name = '')
332
    {
333
        return isset($this->modules->{$module_name})
334
            ? $this->modules->{$module_name}
335
            : null;
336
    }
337
338
339
340
    /**
341
     * loads core classes - must be singletons
342
     *
343
     * @param string $class_name - simple class name ie: session
344
     * @param mixed  $arguments
345
     * @param bool   $load_only
346
     * @return mixed
347
     * @throws EE_Error
348
     * @throws ReflectionException
349
     */
350
    public function load_core($class_name, $arguments = array(), $load_only = false)
351
    {
352
        $core_paths = apply_filters(
353
            'FHEE__EE_Registry__load_core__core_paths',
354
            array(
355
                EE_CORE,
356
                EE_ADMIN,
357
                EE_CPTS,
358
                EE_CORE . 'data_migration_scripts' . DS,
359
                EE_CORE . 'capabilities' . DS,
360
                EE_CORE . 'request_stack' . DS,
361
                EE_CORE . 'middleware' . DS,
362
            )
363
        );
364
        // retrieve instantiated class
365
        return $this->_load(
366
            $core_paths,
367
            'EE_',
368
            $class_name,
369
            'core',
370
            $arguments,
371
            false,
372
            true,
373
            $load_only
374
        );
375
    }
376
377
378
379
    /**
380
     * loads service classes
381
     *
382
     * @param string $class_name - simple class name ie: session
383
     * @param mixed  $arguments
384
     * @param bool   $load_only
385
     * @return mixed
386
     * @throws EE_Error
387
     * @throws ReflectionException
388
     */
389
    public function load_service($class_name, $arguments = array(), $load_only = false)
390
    {
391
        $service_paths = apply_filters(
392
            'FHEE__EE_Registry__load_service__service_paths',
393
            array(
394
                EE_CORE . 'services' . DS,
395
            )
396
        );
397
        // retrieve instantiated class
398
        return $this->_load(
399
            $service_paths,
400
            'EE_',
401
            $class_name,
402
            'class',
403
            $arguments,
404
            false,
405
            true,
406
            $load_only
407
        );
408
    }
409
410
411
412
    /**
413
     * loads data_migration_scripts
414
     *
415
     * @param string $class_name - class name for the DMS ie: EE_DMS_Core_4_2_0
416
     * @param mixed  $arguments
417
     * @return EE_Data_Migration_Script_Base|mixed
418
     * @throws EE_Error
419
     * @throws ReflectionException
420
     */
421
    public function load_dms($class_name, $arguments = array())
422
    {
423
        // retrieve instantiated class
424
        return $this->_load(
425
            EE_Data_Migration_Manager::instance()->get_data_migration_script_folders(),
426
            'EE_DMS_',
427
            $class_name,
428
            'dms',
429
            $arguments,
430
            false,
431
            false
432
        );
433
    }
434
435
436
437
    /**
438
     * loads object creating classes - must be singletons
439
     *
440
     * @param string $class_name - simple class name ie: attendee
441
     * @param mixed  $arguments  - an array of arguments to pass to the class
442
     * @param bool   $from_db    - some classes are instantiated from the db and thus call a different method to
443
     *                           instantiate
444
     * @param bool   $cache      if you don't want the class to be stored in the internal cache (non-persistent) then
445
     *                           set this to FALSE (ie. when instantiating model objects from client in a loop)
446
     * @param bool   $load_only  whether or not to just load the file and NOT instantiate, or load AND instantiate
447
     *                           (default)
448
     * @return EE_Base_Class | bool
449
     * @throws EE_Error
450
     * @throws ReflectionException
451
     */
452
    public function load_class($class_name, $arguments = array(), $from_db = false, $cache = true, $load_only = false)
453
    {
454
        $paths = apply_filters(
455
            'FHEE__EE_Registry__load_class__paths', array(
456
            EE_CORE,
457
            EE_CLASSES,
458
            EE_BUSINESS,
459
        )
460
        );
461
        // retrieve instantiated class
462
        return $this->_load(
463
            $paths,
464
            'EE_',
465
            $class_name,
466
            'class',
467
            $arguments,
468
            $from_db,
469
            $cache,
470
            $load_only
471
        );
472
    }
473
474
475
476
    /**
477
     * loads helper classes - must be singletons
478
     *
479
     * @param string $class_name - simple class name ie: price
480
     * @param mixed  $arguments
481
     * @param bool   $load_only
482
     * @return EEH_Base | bool
483
     * @throws EE_Error
484
     * @throws ReflectionException
485
     */
486
    public function load_helper($class_name, $arguments = array(), $load_only = true)
487
    {
488
        // todo: add doing_it_wrong() in a few versions after all addons have had calls to this method removed
489
        $helper_paths = apply_filters('FHEE__EE_Registry__load_helper__helper_paths', array(EE_HELPERS));
490
        // retrieve instantiated class
491
        return $this->_load(
492
            $helper_paths,
493
            'EEH_',
494
            $class_name,
495
            'helper',
496
            $arguments,
497
            false,
498
            true,
499
            $load_only
500
        );
501
    }
502
503
504
505
    /**
506
     * loads core classes - must be singletons
507
     *
508
     * @param string $class_name - simple class name ie: session
509
     * @param mixed  $arguments
510
     * @param bool   $load_only
511
     * @param bool   $cache      whether to cache the object or not.
512
     * @return mixed
513
     * @throws EE_Error
514
     * @throws ReflectionException
515
     */
516
    public function load_lib($class_name, $arguments = array(), $load_only = false, $cache = true)
517
    {
518
        $paths = array(
519
            EE_LIBRARIES,
520
            EE_LIBRARIES . 'messages' . DS,
521
            EE_LIBRARIES . 'shortcodes' . DS,
522
            EE_LIBRARIES . 'qtips' . DS,
523
            EE_LIBRARIES . 'payment_methods' . DS,
524
        );
525
        // retrieve instantiated class
526
        return $this->_load(
527
            $paths,
528
            'EE_',
529
            $class_name,
530
            'lib',
531
            $arguments,
532
            false,
533
            $cache,
534
            $load_only
535
        );
536
    }
537
538
539
540
    /**
541
     * loads model classes - must be singletons
542
     *
543
     * @param string $class_name - simple class name ie: price
544
     * @param mixed  $arguments
545
     * @param bool   $load_only
546
     * @return EEM_Base | bool
547
     * @throws EE_Error
548
     * @throws ReflectionException
549
     */
550
    public function load_model($class_name, $arguments = array(), $load_only = false)
551
    {
552
        $paths = apply_filters(
553
            'FHEE__EE_Registry__load_model__paths', array(
554
            EE_MODELS,
555
            EE_CORE,
556
        )
557
        );
558
        // retrieve instantiated class
559
        return $this->_load(
560
            $paths,
561
            'EEM_',
562
            $class_name,
563
            'model',
564
            $arguments,
565
            false,
566
            true,
567
            $load_only
568
        );
569
    }
570
571
572
573
    /**
574
     * loads model classes - must be singletons
575
     *
576
     * @param string $class_name - simple class name ie: price
577
     * @param mixed  $arguments
578
     * @param bool   $load_only
579
     * @return mixed | bool
580
     * @throws EE_Error
581
     * @throws ReflectionException
582
     */
583
    public function load_model_class($class_name, $arguments = array(), $load_only = true)
584
    {
585
        $paths = array(
586
            EE_MODELS . 'fields' . DS,
587
            EE_MODELS . 'helpers' . DS,
588
            EE_MODELS . 'relations' . DS,
589
            EE_MODELS . 'strategies' . DS,
590
        );
591
        // retrieve instantiated class
592
        return $this->_load(
593
            $paths,
594
            'EE_',
595
            $class_name,
596
            '',
597
            $arguments,
598
            false,
599
            true,
600
            $load_only
601
        );
602
    }
603
604
605
606
    /**
607
     * Determines if $model_name is the name of an actual EE model.
608
     *
609
     * @param string $model_name like Event, Attendee, Question_Group_Question, etc.
610
     * @return boolean
611
     */
612
    public function is_model_name($model_name)
613
    {
614
        return isset($this->models[$model_name]);
615
    }
616
617
618
619
    /**
620
     * generic class loader
621
     *
622
     * @param string $path_to_file - directory path to file location, not including filename
623
     * @param string $file_name    - file name  ie:  my_file.php, including extension
624
     * @param string $type         - file type - core? class? helper? model?
625
     * @param mixed  $arguments
626
     * @param bool   $load_only
627
     * @return mixed
628
     * @throws EE_Error
629
     * @throws ReflectionException
630
     */
631
    public function load_file($path_to_file, $file_name, $type = '', $arguments = array(), $load_only = true)
632
    {
633
        // retrieve instantiated class
634
        return $this->_load(
635
            $path_to_file,
636
            '',
637
            $file_name,
638
            $type,
639
            $arguments,
640
            false,
641
            true,
642
            $load_only
643
        );
644
    }
645
646
647
648
    /**
649
     * @param string $path_to_file - directory path to file location, not including filename
650
     * @param string $class_name   - full class name  ie:  My_Class
651
     * @param string $type         - file type - core? class? helper? model?
652
     * @param mixed  $arguments
653
     * @param bool   $load_only
654
     * @return bool|EE_Addon|object
655
     * @throws EE_Error
656
     * @throws ReflectionException
657
     */
658
    public function load_addon($path_to_file, $class_name, $type = 'class', $arguments = array(), $load_only = false)
659
    {
660
        // retrieve instantiated class
661
        return $this->_load(
662
            $path_to_file,
663
            'addon',
664
            $class_name,
665
            $type,
666
            $arguments,
667
            false,
668
            true,
669
            $load_only
670
        );
671
    }
672
673
674
    /**
675
     * instantiates, caches, and automatically resolves dependencies
676
     * for classes that use a Fully Qualified Class Name.
677
     * if the class is not capable of being loaded using PSR-4 autoloading,
678
     * then you need to use one of the existing load_*() methods
679
     * which can resolve the classname and filepath from the passed arguments
680
     *
681
     * @param bool|string $class_name   Fully Qualified Class Name
682
     * @param array       $arguments    an argument, or array of arguments to pass to the class upon instantiation
683
     * @param bool        $cache        whether to cache the instantiated object for reuse
684
     * @param bool        $from_db      some classes are instantiated from the db
685
     *                                  and thus call a different method to instantiate
686
     * @param bool        $load_only    if true, will only load the file, but will NOT instantiate an object
687
     * @param bool|string $addon        if true, will cache the object in the EE_Registry->$addons array
688
     * @return bool|null|mixed          null = failure to load or instantiate class object.
689
     *                                  object = class loaded and instantiated successfully.
690
     *                                  bool = fail or success when $load_only is true
691
     * @throws InvalidInterfaceException
692
     * @throws InvalidDataTypeException
693
     * @throws InvalidClassException
694
     * @throws EE_Error
695
     * @throws ReflectionException
696
     */
697
    public function create(
698
        $class_name = false,
699
        $arguments = array(),
700
        $cache = false,
701
        $from_db = false,
702
        $load_only = false,
703
        $addon = false
704
    ) {
705
        $class_name = ltrim($class_name, '\\');
706
        $class_name = $this->class_cache->getFqnForAlias($class_name);
707
        $class_exists = $this->loadOrVerifyClassExists($class_name, $arguments);
708
        // if a non-FQCN was passed, then verifyClassExists() might return an object
709
        // or it could return null if the class just could not be found anywhere
710
        if ($class_exists instanceof $class_name || $class_exists === null){
711
            // either way, return the results
712
            return $class_exists;
713
        }
714
        $class_name = $class_exists;
715
        // if we're only loading the class and it already exists, then let's just return true immediately
716
        if ($load_only) {
717
            return true;
718
        }
719
        $addon = $addon
720
            ? 'addon'
721
            : '';
722
        // $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
723
        // $cache is controlled by individual calls to separate Registry loader methods like load_class()
724
        // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
725 View Code Duplication
        if ($this->_cache_on && $cache && ! $load_only) {
726
            // return object if it's already cached
727
            $cached_class = $this->_get_cached_class($class_name, $addon);
728
            if ($cached_class !== null) {
729
                return $cached_class;
730
            }
731
        }
732
        // obtain the loader method from the dependency map
733
        $loader = $this->_dependency_map->class_loader($class_name);
734
        // instantiate the requested object
735
        if ($loader instanceof Closure) {
736
            $class_obj = $loader($arguments);
737
        } else if ($loader && method_exists($this, $loader)) {
738
            $class_obj = $this->{$loader}($class_name, $arguments);
739
        } else {
740
            $class_obj = $this->_create_object($class_name, $arguments, $addon, $from_db);
741
        }
742
        if (($this->_cache_on && $cache) || $this->get_class_abbreviation($class_name, '')) {
743
            // save it for later... kinda like gum  { : $
744
            $this->_set_cached_class($class_obj, $class_name, $addon, $from_db);
745
        }
746
        $this->_cache_on = true;
747
        return $class_obj;
748
    }
749
750
751
752
    /**
753
     * Recursively checks that a class exists and potentially attempts to load classes with non-FQCNs
754
     *
755
     * @param string $class_name
756
     * @param array  $arguments
757
     * @param int    $attempt
758
     * @return mixed
759
     */
760
    private function loadOrVerifyClassExists($class_name, array $arguments, $attempt = 1) {
761
        if (is_object($class_name) || class_exists($class_name)) {
762
            return $class_name;
763
        }
764
        switch ($attempt) {
765
            case 1:
766
                // if it's a FQCN then maybe the class is registered with a preceding \
767
                $class_name = strpos($class_name, '\\') !== false
768
                    ? '\\' . ltrim($class_name, '\\')
769
                    : $class_name;
770
                break;
771
            case 2:
772
                //
773
                $loader = $this->_dependency_map->class_loader($class_name);
774
                if ($loader && method_exists($this, $loader)) {
775
                    return $this->{$loader}($class_name, $arguments);
776
                }
777
                break;
778
            case 3:
779
            default;
780
                return null;
781
        }
782
        $attempt++;
783
        return $this->loadOrVerifyClassExists($class_name, $arguments, $attempt);
784
    }
785
786
787
788
    /**
789
     * instantiates, caches, and injects dependencies for classes
790
     *
791
     * @param array       $file_paths   an array of paths to folders to look in
792
     * @param string      $class_prefix EE  or EEM or... ???
793
     * @param bool|string $class_name   $class name
794
     * @param string      $type         file type - core? class? helper? model?
795
     * @param mixed       $arguments    an argument or array of arguments to pass to the class upon instantiation
796
     * @param bool        $from_db      some classes are instantiated from the db
797
     *                                  and thus call a different method to instantiate
798
     * @param bool        $cache        whether to cache the instantiated object for reuse
799
     * @param bool        $load_only    if true, will only load the file, but will NOT instantiate an object
800
     * @return bool|null|object null = failure to load or instantiate class object.
801
     *                                  object = class loaded and instantiated successfully.
802
     *                                  bool = fail or success when $load_only is true
803
     * @throws EE_Error
804
     * @throws ReflectionException
805
     * @throws InvalidInterfaceException
806
     * @throws InvalidDataTypeException
807
     * @throws InvalidClassException
808
     */
809
    protected function _load(
810
        $file_paths = array(),
811
        $class_prefix = 'EE_',
812
        $class_name = false,
813
        $type = 'class',
814
        $arguments = array(),
815
        $from_db = false,
816
        $cache = true,
817
        $load_only = false
818
    ) {
819
        $class_name = ltrim($class_name, '\\');
820
        // strip php file extension
821
        $class_name = str_replace('.php', '', trim($class_name));
822
        // does the class have a prefix ?
823
        if (! empty($class_prefix) && $class_prefix !== 'addon') {
824
            // make sure $class_prefix is uppercase
825
            $class_prefix = strtoupper(trim($class_prefix));
826
            // add class prefix ONCE!!!
827
            $class_name = $class_prefix . str_replace($class_prefix, '', $class_name);
828
        }
829
        $class_name = $this->class_cache->getFqnForAlias($class_name);
830
        $class_exists = class_exists($class_name, false);
831
        // if we're only loading the class and it already exists, then let's just return true immediately
832
        if ($load_only && $class_exists) {
833
            return true;
834
        }
835
        // $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
836
        // $cache is controlled by individual calls to separate Registry loader methods like load_class()
837
        // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
838 View Code Duplication
        if ($this->_cache_on && $cache && ! $load_only) {
839
            // return object if it's already cached
840
            $cached_class = $this->_get_cached_class($class_name, $class_prefix);
841
            if ($cached_class !== null) {
842
                return $cached_class;
843
            }
844
        }
845
        // if the class doesn't already exist.. then we need to try and find the file and load it
846
        if (! $class_exists) {
847
            // get full path to file
848
            $path = $this->_resolve_path($class_name, $type, $file_paths);
849
            // load the file
850
            $loaded = $this->_require_file($path, $class_name, $type, $file_paths);
851
            // if loading failed, or we are only loading a file but NOT instantiating an object
852
            if (! $loaded || $load_only) {
853
                // return boolean if only loading, or null if an object was expected
854
                return $load_only
855
                    ? $loaded
856
                    : null;
857
            }
858
        }
859
        // instantiate the requested object
860
        $class_obj = $this->_create_object($class_name, $arguments, $type, $from_db);
861
        if ($this->_cache_on && $cache) {
862
            // save it for later... kinda like gum  { : $
863
            $this->_set_cached_class($class_obj, $class_name, $class_prefix, $from_db);
864
        }
865
        $this->_cache_on = true;
866
        return $class_obj;
867
    }
868
869
870
871
    /**
872
     * @param string $class_name
873
     * @param string $default have to specify something, but not anything that will conflict
874
     * @return mixed|string
875
     */
876
    protected function get_class_abbreviation($class_name, $default = 'FANCY_BATMAN_PANTS')
877
    {
878
        return isset($this->_class_abbreviations[$class_name])
879
            ? $this->_class_abbreviations[$class_name]
880
            : $default;
881
    }
882
883
    /**
884
     * attempts to find a cached version of the requested class
885
     * by looking in the following places:
886
     *        $this->{$class_abbreviation}            ie:    $this->CART
887
     *        $this->{$class_name}                        ie:    $this->Some_Class
888
     *        $this->LIB->{$class_name}                ie:    $this->LIB->Some_Class
889
     *        $this->addon->{$class_name}    ie:    $this->addon->Some_Addon_Class
890
     *
891
     * @param string $class_name
892
     * @param string $class_prefix
893
     * @return mixed
894
     */
895
    protected function _get_cached_class($class_name, $class_prefix = '')
896
    {
897
        if ($class_name === 'EE_Registry') {
898
            return $this;
899
        }
900
        $class_abbreviation = $this->get_class_abbreviation($class_name);
901
        $class_name = str_replace('\\', '_', $class_name);
902
        // check if class has already been loaded, and return it if it has been
903
        if (isset($this->{$class_abbreviation})) {
904
            return $this->{$class_abbreviation};
905
        }
906
        if (isset ($this->{$class_name})) {
907
            return $this->{$class_name};
908
        }
909
        if (isset ($this->LIB->{$class_name})) {
910
            return $this->LIB->{$class_name};
911
        }
912
        if ($class_prefix === 'addon' && isset ($this->addons->{$class_name})) {
913
            return $this->addons->{$class_name};
914
        }
915
        return null;
916
    }
917
918
919
920
    /**
921
     * removes a cached version of the requested class
922
     *
923
     * @param string  $class_name
924
     * @param boolean $addon
925
     * @return boolean
926
     */
927
    public function clear_cached_class($class_name, $addon = false)
928
    {
929
        $class_abbreviation = $this->get_class_abbreviation($class_name);
930
        $class_name = str_replace('\\', '_', $class_name);
931
        // check if class has already been loaded, and return it if it has been
932
        if (isset($this->{$class_abbreviation})) {
933
            $this->{$class_abbreviation} = null;
934
            return true;
935
        }
936
        if (isset($this->{$class_name})) {
937
            $this->{$class_name} = null;
938
            return true;
939
        }
940
        if (isset($this->LIB->{$class_name})) {
941
            unset($this->LIB->{$class_name});
942
            return true;
943
        }
944
        if ($addon && isset($this->addons->{$class_name})) {
945
            unset($this->addons->{$class_name});
946
            return true;
947
        }
948
        return false;
949
    }
950
951
952
953
    /**
954
     * attempts to find a full valid filepath for the requested class.
955
     * loops thru each of the base paths in the $file_paths array and appends : "{classname} . {file type} . php"
956
     * then returns that path if the target file has been found and is readable
957
     *
958
     * @param string $class_name
959
     * @param string $type
960
     * @param array  $file_paths
961
     * @return string | bool
962
     */
963
    protected function _resolve_path($class_name, $type = '', $file_paths = array())
964
    {
965
        // make sure $file_paths is an array
966
        $file_paths = is_array($file_paths)
967
            ? $file_paths
968
            : array($file_paths);
969
        // cycle thru paths
970
        foreach ($file_paths as $key => $file_path) {
971
            // convert all separators to proper DS, if no filepath, then use EE_CLASSES
972
            $file_path = $file_path
973
                ? str_replace(array('/', '\\'), DS, $file_path)
974
                : EE_CLASSES;
975
            // prep file type
976
            $type = ! empty($type)
977
                ? trim($type, '.') . '.'
978
                : '';
979
            // build full file path
980
            $file_paths[$key] = rtrim($file_path, DS) . DS . $class_name . '.' . $type . 'php';
981
            //does the file exist and can be read ?
982
            if (is_readable($file_paths[$key])) {
983
                return $file_paths[$key];
984
            }
985
        }
986
        return false;
987
    }
988
989
990
991
    /**
992
     * basically just performs a require_once()
993
     * but with some error handling
994
     *
995
     * @param  string $path
996
     * @param  string $class_name
997
     * @param  string $type
998
     * @param  array  $file_paths
999
     * @return bool
1000
     * @throws EE_Error
1001
     * @throws ReflectionException
1002
     */
1003
    protected function _require_file($path, $class_name, $type = '', $file_paths = array())
1004
    {
1005
        $this->resolve_legacy_class_parent($class_name);
1006
        // don't give up! you gotta...
1007
        try {
1008
            //does the file exist and can it be read ?
1009
            if (! $path) {
1010
                // just in case the file has already been autoloaded,
1011
                // but discrepancies in the naming schema are preventing it from
1012
                // being loaded via one of the EE_Registry::load_*() methods,
1013
                // then let's try one last hail mary before throwing an exception
1014
                // and call class_exists() again, but with autoloading turned ON
1015
                if(class_exists($class_name)) {
1016
                    return true;
1017
                }
1018
                // so sorry, can't find the file
1019
                throw new EE_Error (
1020
                    sprintf(
1021
                        esc_html__(
1022
                            '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',
1023
                            'event_espresso'
1024
                        ),
1025
                        trim($type, '.'),
1026
                        $class_name,
1027
                        '<br />' . implode(',<br />', $file_paths)
1028
                    )
1029
                );
1030
            }
1031
            // get the file
1032
            require_once($path);
1033
            // if the class isn't already declared somewhere
1034
            if (class_exists($class_name, false) === false) {
1035
                // so sorry, not a class
1036
                throw new EE_Error(
1037
                    sprintf(
1038
                        esc_html__('The %s file %s does not appear to contain the %s Class.', 'event_espresso'),
1039
                        $type,
1040
                        $path,
1041
                        $class_name
1042
                    )
1043
                );
1044
            }
1045
        } catch (EE_Error $e) {
1046
            $e->get_error();
1047
            return false;
1048
        }
1049
        return true;
1050
    }
1051
1052
1053
1054
    /**
1055
     * Some of our legacy classes that extended a parent class would simply use a require() statement
1056
     * before their class declaration in order to ensure that the parent class was loaded.
1057
     * This is not ideal, but it's nearly impossible to determine the parent class of a non-namespaced class,
1058
     * without triggering a fatal error because the parent class has yet to be loaded and therefore doesn't exist.
1059
     *
1060
     * @param string $class_name
1061
     */
1062
    protected function resolve_legacy_class_parent($class_name = '')
1063
    {
1064
        try {
1065
            $legacy_parent_class_map = array(
1066
                'EE_Payment_Processor' => 'core/business/EE_Processor_Base.class.php'
1067
            );
1068
            if(isset($legacy_parent_class_map[$class_name])) {
1069
                require_once EE_PLUGIN_DIR_PATH . $legacy_parent_class_map[$class_name];
1070
            }
1071
        } catch (Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1072
        }
1073
    }
1074
1075
1076
    /**
1077
     * _create_object
1078
     * Attempts to instantiate the requested class via any of the
1079
     * commonly used instantiation methods employed throughout EE.
1080
     * The priority for instantiation is as follows:
1081
     *        - abstract classes or any class flagged as "load only" (no instantiation occurs)
1082
     *        - model objects via their 'new_instance_from_db' method
1083
     *        - model objects via their 'new_instance' method
1084
     *        - "singleton" classes" via their 'instance' method
1085
     *    - standard instantiable classes via their __constructor
1086
     * Prior to instantiation, if the classname exists in the dependency_map,
1087
     * then the constructor for the requested class will be examined to determine
1088
     * if any dependencies exist, and if they can be injected.
1089
     * If so, then those classes will be added to the array of arguments passed to the constructor
1090
     *
1091
     * @param string $class_name
1092
     * @param array  $arguments
1093
     * @param string $type
1094
     * @param bool   $from_db
1095
     * @return null|object
1096
     * @throws EE_Error
1097
     * @throws ReflectionException
1098
     * @throws InvalidDataTypeException
1099
     */
1100
    protected function _create_object($class_name, $arguments = array(), $type = '', $from_db = false)
1101
    {
1102
        // create reflection
1103
        $reflector = $this->mirror->getReflectionClass($class_name);
1104
        // make sure arguments are an array
1105
        $arguments = is_array($arguments)
1106
            ? $arguments
1107
            : array($arguments);
1108
        // and if arguments array is numerically and sequentially indexed, then we want it to remain as is,
1109
        // else wrap it in an additional array so that it doesn't get split into multiple parameters
1110
        $arguments = $this->_array_is_numerically_and_sequentially_indexed($arguments)
1111
            ? $arguments
1112
            : array($arguments);
1113
        // attempt to inject dependencies ?
1114
        if ($this->_dependency_map->has($class_name)) {
1115
            $arguments = $this->_resolve_dependencies($reflector, $class_name, $arguments);
1116
        }
1117
        // instantiate the class if possible
1118
        if ($reflector->isAbstract()) {
1119
            // nothing to instantiate, loading file was enough
1120
            // does not throw an exception so $instantiation_mode is unused
1121
            // $instantiation_mode = "1) no constructor abstract class";
1122
            return true;
1123
        }
1124
        if (
1125
            empty($arguments)
1126
            && $this->mirror->getConstructorFromReflection($reflector) === null
1127
            && $reflector->isInstantiable()
1128
        ) {
1129
            // no constructor = static methods only... nothing to instantiate, loading file was enough
1130
            // $instantiation_mode = "2) no constructor but instantiable";
1131
            return $reflector->newInstance();
1132
        }
1133
        if ($from_db && method_exists($class_name, 'new_instance_from_db')) {
1134
            // $instantiation_mode = "3) new_instance_from_db()";
1135
            return call_user_func_array(array($class_name, 'new_instance_from_db'), $arguments);
1136
        }
1137
        if (method_exists($class_name, 'new_instance')) {
1138
            // $instantiation_mode = "4) new_instance()";
1139
            return call_user_func_array(array($class_name, 'new_instance'), $arguments);
1140
        }
1141
        if (method_exists($class_name, 'instance')) {
1142
            // $instantiation_mode = "5) instance()";
1143
            return call_user_func_array(array($class_name, 'instance'), $arguments);
1144
        }
1145
        if ($reflector->isInstantiable()) {
1146
            // $instantiation_mode = "6) constructor";
1147
            return $reflector->newInstanceArgs($arguments);
1148
        }
1149
        // heh ? something's not right !
1150
        throw new EE_Error(
1151
            sprintf(
1152
                __('The %s file %s could not be instantiated.', 'event_espresso'),
1153
                $type,
1154
                $class_name
1155
            )
1156
        );
1157
    }
1158
1159
1160
1161
    /**
1162
     * @see http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
1163
     * @param array $array
1164
     * @return bool
1165
     */
1166
    protected function _array_is_numerically_and_sequentially_indexed(array $array)
1167
    {
1168
        return ! empty($array)
1169
            ? array_keys($array) === range(0, count($array) - 1)
1170
            : true;
1171
    }
1172
1173
1174
    /**
1175
     * _resolve_dependencies
1176
     * examines the constructor for the requested class to determine
1177
     * if any dependencies exist, and if they can be injected.
1178
     * If so, then those classes will be added to the array of arguments passed to the constructor
1179
     * PLZ NOTE: this is achieved by type hinting the constructor params
1180
     * For example:
1181
     *        if attempting to load a class "Foo" with the following constructor:
1182
     *        __construct( Bar $bar_class, Fighter $grohl_class )
1183
     *        then $bar_class and $grohl_class will be added to the $arguments array,
1184
     *        but only IF they are NOT already present in the incoming arguments array,
1185
     *        and the correct classes can be loaded
1186
     *
1187
     * @param ReflectionClass $reflector
1188
     * @param string          $class_name
1189
     * @param array           $arguments
1190
     * @return array
1191
     * @throws EE_Error
1192
     * @throws InvalidArgumentException
1193
     * @throws InvalidDataTypeException
1194
     * @throws InvalidInterfaceException
1195
     * @throws ReflectionException
1196
     * @throws InvalidClassException
1197
     */
1198
    protected function _resolve_dependencies(ReflectionClass $reflector, $class_name, array $arguments = array())
1199
    {
1200
        // let's examine the constructor
1201
        $constructor = $this->mirror->getConstructorFromReflection($reflector);
1202
        // whu? huh? nothing?
1203
        if (! $constructor) {
1204
            return $arguments;
1205
        }
1206
        // get constructor parameters
1207
        $params = $this->mirror->getParametersFromReflection($reflector);
1208
        // and the keys for the incoming arguments array so that we can compare existing arguments with what is expected
1209
        $argument_keys = array_keys($arguments);
1210
        // now loop thru all of the constructors expected parameters
1211
        foreach ($params as $index => $param) {
1212
            // is this a dependency for a specific class ?
1213
            $param_class = $this->mirror->getParameterClassName($param, $class_name, $index);
1214
            // BUT WAIT !!! This class may be an alias for something else (or getting replaced at runtime)
1215
            $param_class = $this->class_cache->isAlias($param_class, $class_name)
1216
                ? $this->class_cache->getFqnForAlias($param_class, $class_name)
1217
                : $param_class;
1218
            if (
1219
                // param is not even a class
1220
                $param_class === null
1221
                // and something already exists in the incoming arguments for this param
1222
                && array_key_exists($index, $argument_keys)
1223
                && array_key_exists($argument_keys[$index], $arguments)
1224
            ) {
1225
                // so let's skip this argument and move on to the next
1226
                continue;
1227
            }
1228
            if (
1229
                // parameter is type hinted as a class, exists as an incoming argument, AND it's the correct class
1230
                $param_class !== null
1231
                && isset($argument_keys[$index], $arguments[$argument_keys[$index]])
1232
                && $arguments[$argument_keys[$index]] instanceof $param_class
1233
            ) {
1234
                // skip this argument and move on to the next
1235
                continue;
1236
            }
1237
            if (
1238
                // parameter is type hinted as a class, and should be injected
1239
                $param_class !== null
1240
                && $this->_dependency_map->has_dependency_for_class($class_name, $param_class)
1241
            ) {
1242
                $arguments = $this->_resolve_dependency(
1243
                    $class_name,
1244
                    $param_class,
1245
                    $arguments,
1246
                    $index,
1247
                    $argument_keys
1248
                );
1249
            } else {
1250
                try {
1251
                    $arguments[$index] = $this->mirror->getParameterDefaultValue(
1252
                        $param,
1253
                        $class_name,
1254
                        $index
1255
                    );
1256
                } catch (ReflectionException $e) {
1257
                    throw new ReflectionException(
1258
                        sprintf(
1259
                            esc_html__('%1$s for parameter "$%2$s on classname "%3$s"', 'event_espresso'),
1260
                            $e->getMessage(),
1261
                            $param->getName(),
1262
                            $class_name
1263
                        )
1264
                    );
1265
                }
1266
            }
1267
        }
1268
        return $arguments;
1269
    }
1270
1271
1272
1273
    /**
1274
     * @param string $class_name
1275
     * @param string $param_class
1276
     * @param array  $arguments
1277
     * @param mixed  $index
1278
     * @param array  $argument_keys
1279
     * @return array
1280
     * @throws EE_Error
1281
     * @throws ReflectionException
1282
     * @throws InvalidArgumentException
1283
     * @throws InvalidInterfaceException
1284
     * @throws InvalidDataTypeException
1285
     */
1286
    protected function _resolve_dependency($class_name, $param_class, $arguments, $index, array $argument_keys)
1287
    {
1288
        $dependency = null;
1289
        // should dependency be loaded from cache ?
1290
        $cache_on = $this->_dependency_map->loading_strategy_for_class_dependency(
1291
            $class_name,
1292
            $param_class
1293
        );
1294
        $cache_on = $cache_on !== EE_Dependency_Map::load_new_object;
1295
        // we might have a dependency...
1296
        // let's MAYBE try and find it in our cache if that's what's been requested
1297
        $cached_class = $cache_on
1298
            ? $this->_get_cached_class($param_class)
1299
            : null;
1300
        // and grab it if it exists
1301
        if ($cached_class instanceof $param_class) {
1302
            $dependency = $cached_class;
1303
        } else if ($param_class !== $class_name) {
1304
            // obtain the loader method from the dependency map
1305
            $loader = $this->_dependency_map->class_loader($param_class);
1306
            // is loader a custom closure ?
1307
            if ($loader instanceof Closure) {
1308
                $dependency = $loader($arguments);
1309
            } else {
1310
                // set the cache on property for the recursive loading call
1311
                $this->_cache_on = $cache_on;
1312
                // if not, then let's try and load it via the registry
1313
                if ($loader && method_exists($this, $loader)) {
1314
                    $dependency = $this->{$loader}($param_class);
1315
                } else {
1316
                    $dependency = LoaderFactory::getLoader()->load(
1317
                        $param_class,
1318
                        array(),
1319
                        $cache_on
1320
                    );
1321
                }
1322
            }
1323
        }
1324
        // did we successfully find the correct dependency ?
1325
        if ($dependency instanceof $param_class) {
1326
            // then let's inject it into the incoming array of arguments at the correct location
1327
            $arguments[$index] = $dependency;
1328
        }
1329
        return $arguments;
1330
    }
1331
1332
1333
1334
    /**
1335
     * _set_cached_class
1336
     * attempts to cache the instantiated class locally
1337
     * in one of the following places, in the following order:
1338
     *        $this->{class_abbreviation}   ie:    $this->CART
1339
     *        $this->{$class_name}          ie:    $this->Some_Class
1340
     *        $this->addon->{$$class_name}    ie:    $this->addon->Some_Addon_Class
1341
     *        $this->LIB->{$class_name}     ie:    $this->LIB->Some_Class
1342
     *
1343
     * @param object $class_obj
1344
     * @param string $class_name
1345
     * @param string $class_prefix
1346
     * @param bool   $from_db
1347
     * @return void
1348
     */
1349
    protected function _set_cached_class($class_obj, $class_name, $class_prefix = '', $from_db = false)
1350
    {
1351
        if ($class_name === 'EE_Registry' || empty($class_obj)) {
1352
            return;
1353
        }
1354
        // return newly instantiated class
1355
        $class_abbreviation = $this->get_class_abbreviation($class_name, '');
1356
        if ($class_abbreviation) {
1357
            $this->{$class_abbreviation} = $class_obj;
1358
            return;
1359
        }
1360
        $class_name = str_replace('\\', '_', $class_name);
1361
        if (property_exists($this, $class_name)) {
1362
            $this->{$class_name} = $class_obj;
1363
            return;
1364
        }
1365
        if ($class_prefix === 'addon') {
1366
            $this->addons->{$class_name} = $class_obj;
1367
            return;
1368
        }
1369
        if (! $from_db) {
1370
            $this->LIB->{$class_name} = $class_obj;
1371
        }
1372
    }
1373
1374
1375
1376
    /**
1377
     * call any loader that's been registered in the EE_Dependency_Map::$_class_loaders array
1378
     *
1379
     * @param string $classname PLEASE NOTE: the class name needs to match what's registered
1380
     *                          in the EE_Dependency_Map::$_class_loaders array,
1381
     *                          including the class prefix, ie: "EE_", "EEM_", "EEH_", etc
1382
     * @param array  $arguments
1383
     * @return object
1384
     */
1385
    public static function factory($classname, $arguments = array())
1386
    {
1387
        $loader = self::instance()->_dependency_map->class_loader($classname);
1388
        if ($loader instanceof Closure) {
1389
            return $loader($arguments);
1390
        }
1391
        if (method_exists(self::instance(), $loader)) {
1392
            return self::instance()->{$loader}($classname, $arguments);
1393
        }
1394
        return null;
1395
    }
1396
1397
1398
1399
    /**
1400
     * Gets the addon by its class name
1401
     *
1402
     * @param string $class_name
1403
     * @return EE_Addon
1404
     */
1405
    public function getAddon($class_name)
1406
    {
1407
        $class_name = str_replace('\\', '_', $class_name);
1408
        return $this->addons->{$class_name};
1409
    }
1410
1411
1412
    /**
1413
     * removes the addon from the internal cache
1414
     *
1415
     * @param string $class_name
1416
     * @return void
1417
     */
1418
    public function removeAddon($class_name)
1419
    {
1420
        $class_name = str_replace('\\', '_', $class_name);
1421
        unset($this->addons->{$class_name});
1422
    }
1423
1424
1425
1426
    /**
1427
     * Gets the addon by its name/slug (not classname. For that, just
1428
     * use the get_addon() method above
1429
     *
1430
     * @param string $name
1431
     * @return EE_Addon
1432
     */
1433
    public function get_addon_by_name($name)
1434
    {
1435
        foreach ($this->addons as $addon) {
1436
            if ($addon->name() === $name) {
1437
                return $addon;
1438
            }
1439
        }
1440
        return null;
1441
    }
1442
1443
1444
1445
    /**
1446
     * Gets an array of all the registered addons, where the keys are their names.
1447
     * (ie, what each returns for their name() function)
1448
     * They're already available on EE_Registry::instance()->addons as properties,
1449
     * where each property's name is the addon's classname,
1450
     * So if you just want to get the addon by classname,
1451
     * OR use the get_addon() method above.
1452
     * PLEASE  NOTE:
1453
     * addons with Fully Qualified Class Names
1454
     * have had the namespace separators converted to underscores,
1455
     * so a classname like Fully\Qualified\ClassName
1456
     * would have been converted to Fully_Qualified_ClassName
1457
     *
1458
     * @return EE_Addon[] where the KEYS are the addon's name()
1459
     */
1460
    public function get_addons_by_name()
1461
    {
1462
        $addons = array();
1463
        foreach ($this->addons as $addon) {
1464
            $addons[$addon->name()] = $addon;
1465
        }
1466
        return $addons;
1467
    }
1468
1469
1470
    /**
1471
     * Resets the specified model's instance AND makes sure EE_Registry doesn't keep
1472
     * a stale copy of it around
1473
     *
1474
     * @param string $model_name
1475
     * @return \EEM_Base
1476
     * @throws \EE_Error
1477
     */
1478
    public function reset_model($model_name)
1479
    {
1480
        $model_class_name = strpos($model_name, 'EEM_') !== 0
1481
            ? "EEM_{$model_name}"
1482
            : $model_name;
1483
        if (! isset($this->LIB->{$model_class_name}) || ! $this->LIB->{$model_class_name} instanceof EEM_Base) {
1484
            return null;
1485
        }
1486
        //get that model reset it and make sure we nuke the old reference to it
1487
        if ($this->LIB->{$model_class_name} instanceof $model_class_name
1488
            && is_callable(
1489
                array($model_class_name, 'reset')
1490
            )) {
1491
            $this->LIB->{$model_class_name} = $this->LIB->{$model_class_name}->reset();
1492
        } else {
1493
            throw new EE_Error(sprintf(esc_html__('Model %s does not have a method "reset"', 'event_espresso'), $model_name));
1494
        }
1495
        return $this->LIB->{$model_class_name};
1496
    }
1497
1498
1499
1500
    /**
1501
     * Resets the registry.
1502
     * The criteria for what gets reset is based on what can be shared between sites on the same request when
1503
     * switch_to_blog is used in a multisite install.  Here is a list of things that are NOT reset.
1504
     * - $_dependency_map
1505
     * - $_class_abbreviations
1506
     * - $NET_CFG (EE_Network_Config): The config is shared network wide so no need to reset.
1507
     * - $REQ:  Still on the same request so no need to change.
1508
     * - $CAP: There is no site specific state in the EE_Capability class.
1509
     * - $SSN: Although ideally, the session should not be shared between site switches, we can't reset it because only
1510
     * one Session can be active in a single request.  Resetting could resolve in "headers already sent" errors.
1511
     * - $addons:  In multisite, the state of the addons is something controlled via hooks etc in a normal request.  So
1512
     *             for now, we won't reset the addons because it could break calls to an add-ons class/methods in the
1513
     *             switch or on the restore.
1514
     * - $modules
1515
     * - $shortcodes
1516
     * - $widgets
1517
     *
1518
     * @param boolean $hard             [deprecated]
1519
     * @param boolean $reinstantiate    whether to create new instances of EE_Registry's singletons too,
1520
     *                                  or just reset without re-instantiating (handy to set to FALSE if you're not
1521
     *                                  sure if you CAN currently reinstantiate the singletons at the moment)
1522
     * @param   bool  $reset_models     Defaults to true.  When false, then the models are not reset.  This is so
1523
     *                                  client
1524
     *                                  code instead can just change the model context to a different blog id if
1525
     *                                  necessary
1526
     * @return EE_Registry
1527
     * @throws EE_Error
1528
     * @throws ReflectionException
1529
     */
1530
    public static function reset($hard = false, $reinstantiate = true, $reset_models = true)
1531
    {
1532
        $instance = self::instance();
1533
        $instance->_cache_on = true;
1534
        // reset some "special" classes
1535
        EEH_Activation::reset();
1536
        $hard = apply_filters( 'FHEE__EE_Registry__reset__hard', $hard);
1537
        $instance->CFG = EE_Config::reset($hard, $reinstantiate);
1538
        $instance->CART = null;
1539
        $instance->MRM = null;
1540
        $instance->AssetsRegistry = $instance->create('EventEspresso\core\services\assets\Registry');
1541
        //messages reset
1542
        EED_Messages::reset();
1543
        //handle of objects cached on LIB
1544
        foreach (array('LIB', 'modules') as $cache) {
1545
            foreach ($instance->{$cache} as $class_name => $class) {
1546
                if (self::_reset_and_unset_object($class, $reset_models)) {
1547
                    unset($instance->{$cache}->{$class_name});
1548
                }
1549
            }
1550
        }
1551
        return $instance;
1552
    }
1553
1554
1555
1556
    /**
1557
     * if passed object implements ResettableInterface, then call it's reset() method
1558
     * if passed object implements InterminableInterface, then return false,
1559
     * to indicate that it should NOT be cleared from the Registry cache
1560
     *
1561
     * @param      $object
1562
     * @param bool $reset_models
1563
     * @return bool returns true if cached object should be unset
1564
     */
1565
    private static function _reset_and_unset_object($object, $reset_models)
1566
    {
1567
        if (! is_object($object)) {
1568
            // don't unset anything that's not an object
1569
            return false;
1570
        }
1571
        if ($object instanceof EED_Module) {
1572
            $object::reset();
1573
            // don't unset modules
1574
            return false;
1575
        }
1576
        if ($object instanceof ResettableInterface) {
1577
            if ($object instanceof EEM_Base) {
1578
                if ($reset_models) {
1579
                    $object->reset();
1580
                    return true;
1581
                }
1582
                return false;
1583
            }
1584
            $object->reset();
1585
            return true;
1586
        }
1587
        if (! $object instanceof InterminableInterface) {
1588
            return true;
1589
        }
1590
        return false;
1591
    }
1592
1593
1594
1595
    /**
1596
     * Gets all the custom post type models defined
1597
     *
1598
     * @return array keys are model "short names" (Eg "Event") and keys are classnames (eg "EEM_Event")
1599
     */
1600
    public function cpt_models()
1601
    {
1602
        $cpt_models = array();
1603
        foreach ($this->non_abstract_db_models as $short_name => $classname) {
1604
            if (is_subclass_of($classname, 'EEM_CPT_Base')) {
1605
                $cpt_models[$short_name] = $classname;
1606
            }
1607
        }
1608
        return $cpt_models;
1609
    }
1610
1611
1612
1613
    /**
1614
     * @return \EE_Config
1615
     */
1616
    public static function CFG()
1617
    {
1618
        return self::instance()->CFG;
1619
    }
1620
1621
1622
    /**
1623
     * @deprecated $VID:$
1624
     * @param string $class_name
1625
     * @return ReflectionClass
1626
     * @throws ReflectionException
1627
     * @throws InvalidDataTypeException
1628
     */
1629
    public function get_ReflectionClass($class_name)
1630
    {
1631
        return $this->mirror->getReflectionClass($class_name);
1632
    }
1633
}
1634
// End of file EE_Registry.core.php
1635
// Location: ./core/EE_Registry.core.php
1636