Completed
Branch FET/11450/reserved-instance-in... (972752)
by
unknown
55:29 queued 42:15
created

EE_Registry::getIdentifierForArguments()   C

Complexity

Conditions 7
Paths 11

Size

Total Lines 23
Code Lines 18

Duplication

Lines 14
Ratio 60.87 %

Importance

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