Completed
Branch FET/asset-manager (018c4e)
by
unknown
87:46 queued 74:14
created

EE_Registry::instance()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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