Completed
Branch FET/download-plugin-prompt (7814ea)
by
unknown
04:11 queued 15s
created
core/libraries/rest_api/RestIncomingQueryParamMetadata.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
     {
490 490
         // the value should be JSON or CSV
491 491
         $values = json_decode($sub_array_value);
492
-        if (! is_array($values)) {
492
+        if ( ! is_array($values)) {
493 493
             $values = array_filter(
494 494
                 array_map(
495 495
                     'trim',
@@ -512,7 +512,7 @@  discard block
 block discarded – undo
512 512
      */
513 513
     private function assertSimplifiedSpecifiedOperator()
514 514
     {
515
-        if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
515
+        if ( ! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
516 516
             throw new RestException(
517 517
                 'numerically_indexed_array_of_values_only',
518 518
                 sprintf(
@@ -573,7 +573,7 @@  discard block
 block discarded – undo
573 573
         $valid_operators   = $this->getContext()->getModel()->valid_operators();
574 574
         $query_param_value = $this->getQueryParamValue();
575 575
         return isset($query_param_value[0])
576
-               && isset($valid_operators[ $query_param_value[0] ]);
576
+               && isset($valid_operators[$query_param_value[0]]);
577 577
     }
578 578
 
579 579
 
Please login to merge, or discard this patch.
Indentation   +795 added lines, -795 removed lines patch added patch discarded remove patch
@@ -29,801 +29,801 @@
 block discarded – undo
29 29
  */
30 30
 class RestIncomingQueryParamMetadata
31 31
 {
32
-    /**
33
-     * @var string
34
-     */
35
-    private $query_param_key;
36
-
37
-    /**
38
-     * @var mixed
39
-     */
40
-    private $query_param_value;
41
-
42
-    /**
43
-     * @var RestIncomingQueryParamContext
44
-     */
45
-    private $context;
46
-
47
-    /**
48
-     * @var EE_Model_Field_Base|null
49
-     */
50
-    private $field;
51
-
52
-    /**
53
-     * @var string same as $query_param_key but has the * and anything after it removed
54
-     */
55
-    private $query_param_key_sans_stars;
56
-
57
-    /**
58
-     * @var string for timezone or timezone offset
59
-     */
60
-    private $timezone;
61
-
62
-    /**
63
-     * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
64
-     */
65
-    private $is_gmt_field = false;
66
-
67
-
68
-    /**
69
-     * RestIncomingQueryParamMetadata constructor.
70
-     * You probably want to call
71
-     *
72
-     * @param string                        $query_param_key
73
-     * @param mixed                         $query_param_value
74
-     * @param RestIncomingQueryParamContext $context
75
-     * @throws EE_Error
76
-     * @throws RestException
77
-     * @throws ReflectionException
78
-     */
79
-    public function __construct(string $query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
80
-    {
81
-        $this->query_param_key   = $query_param_key;
82
-        $this->query_param_value = $query_param_value;
83
-        $this->context           = $context;
84
-        $this->determineFieldAndTimezone();
85
-    }
86
-
87
-
88
-    /**
89
-     * Gets the query parameter key. This may have been modified (see setQueryParamValue())
90
-     *
91
-     * @return string
92
-     */
93
-    public function getQueryParamKey(): string
94
-    {
95
-        return $this->query_param_key;
96
-    }
97
-
98
-
99
-    /**
100
-     * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
101
-     * query parameters into the legacy structure)
102
-     *
103
-     * @param string|array|int|float $query_param_value
104
-     */
105
-    private function setQueryParamValue($query_param_value)
106
-    {
107
-        $this->query_param_value = $query_param_value;
108
-    }
109
-
110
-
111
-    /**
112
-     * Gets the original query parameter value passed in.
113
-     *
114
-     * @return mixed
115
-     */
116
-    public function getQueryParamValue()
117
-    {
118
-        return $this->query_param_value;
119
-    }
120
-
121
-
122
-    /**
123
-     * Gets the context object.
124
-     *
125
-     * @return RestIncomingQueryParamContext
126
-     */
127
-    public function getContext(): RestIncomingQueryParamContext
128
-    {
129
-        return $this->context;
130
-    }
131
-
132
-
133
-    /**
134
-     * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
135
-     *
136
-     * @param string $query_param_key
137
-     */
138
-    private function setQueryParamKey(string $query_param_key)
139
-    {
140
-        $this->query_param_key = $query_param_key;
141
-    }
142
-
143
-
144
-    /**
145
-     * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
146
-     * did not indicate a field, eg if it were `OR`).
147
-     *
148
-     * @return EE_Model_Field_Base|null
149
-     */
150
-    public function getField(): ?EE_Model_Field_Base
151
-    {
152
-        return $this->field;
153
-    }
154
-
155
-
156
-    /**
157
-     * Gets the query parameter key (with the star and everything afterwards removed).
158
-     *
159
-     * @return string
160
-     */
161
-    public function getQueryParamKeySansStars(): string
162
-    {
163
-        return $this->query_param_key_sans_stars;
164
-    }
165
-
166
-
167
-    /**
168
-     * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
169
-     *
170
-     * @return string
171
-     */
172
-    public function getTimezone(): string
173
-    {
174
-        return $this->timezone;
175
-    }
176
-
177
-
178
-    /**
179
-     * Returns whether or not this is a GMT field
180
-     *
181
-     * @return boolean
182
-     */
183
-    public function isGmtField(): bool
184
-    {
185
-        return $this->is_gmt_field;
186
-    }
187
-
188
-
189
-    /**
190
-     * Sets the field indicated by the query parameter key (might be null).
191
-     *
192
-     * @param EE_Model_Field_Base|null $field
193
-     */
194
-    private function setField(EE_Model_Field_Base $field = null)
195
-    {
196
-        $this->field = $field;
197
-    }
198
-
199
-
200
-    /**
201
-     * Sets the query parameter key-with-stars-removed.
202
-     *
203
-     * @param string $query_param_key_sans_stars
204
-     */
205
-    private function setQueryParamKeySansStars(string $query_param_key_sans_stars)
206
-    {
207
-        $this->query_param_key_sans_stars = $query_param_key_sans_stars;
208
-    }
209
-
210
-
211
-    /**
212
-     * Sets the timezone (this could be a timezone offset string).
213
-     *
214
-     * @param string $timezone
215
-     */
216
-    private function setTimezone(string $timezone)
217
-    {
218
-        $this->timezone = $timezone;
219
-    }
220
-
221
-
222
-    /**
223
-     * @param bool|int|string $is_gmt_field
224
-     */
225
-    private function setIsGmtField($is_gmt_field)
226
-    {
227
-        $this->is_gmt_field = filter_var($is_gmt_field, FILTER_VALIDATE_BOOLEAN);
228
-    }
229
-
230
-
231
-    /**
232
-     * Determines what field, query param name, and query param name without stars, and timezone to use.
233
-     *
234
-     * @return void {
235
-     * @throws EE_Error
236
-     * @throws RestException
237
-     * @throws ReflectionException
238
-     * @since 4.9.72.p
239
-     *
240
-     */
241
-    private function determineFieldAndTimezone()
242
-    {
243
-        $this->setQueryParamKeySansStars(
244
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
245
-                $this->getQueryParamKey()
246
-            )
247
-        );
248
-        $this->setField(
249
-            ModelDataTranslator::deduceFieldFromQueryParam(
250
-                $this->getQueryParamKeySansStars(),
251
-                $this->getContext()->getModel()
252
-            )
253
-        );
254
-        // double-check is it a *_gmt field?
255
-        if (
256
-            ! $this->getField() instanceof EE_Model_Field_Base
257
-            && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
258
-        ) {
259
-            // yep, take off '_gmt', and find the field
260
-            $this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
261
-            $this->setField(
262
-                ModelDataTranslator::deduceFieldFromQueryParam(
263
-                    $this->getQueryParamKey(),
264
-                    $this->context->getModel()
265
-                )
266
-            );
267
-            $this->setTimezone('UTC');
268
-            $this->setIsGmtField(true);
269
-        } elseif ($this->getField() instanceof EE_Datetime_Field) {
270
-            // so it's not a GMT field. Set the timezone on the model to the default
271
-            $this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
272
-        } else {
273
-            // just keep using what's already set for the timezone
274
-            $this->setTimezone($this->context->getModel()->get_timezone());
275
-        }
276
-        $this->assertOnlyAdminCanReadPasswordFields();
277
-    }
278
-
279
-
280
-    /**
281
-     * Throws an exception if a non-admin is trying to query by password.
282
-     *
283
-     * @throws RestException
284
-     * @since 4.9.74.p
285
-     */
286
-    private function assertOnlyAdminCanReadPasswordFields()
287
-    {
288
-        if (
289
-            $this->getField() instanceof EE_Password_Field
290
-            && ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())
291
-        ) {
292
-            // only full admins can query by password. sorry bub!
293
-            throw new RestException(
294
-                'only_admins_can_query_by_password',
295
-                // @codingStandardsIgnoreStart
296
-                esc_html__(
297
-                    'You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.',
298
-                    'event_espresso'
299
-                ),
300
-                // @codingStandardsIgnoreEnd
301
-                [
302
-                    'status' => 403,
303
-                ]
304
-            );
305
-        }
306
-    }
307
-
308
-
309
-    /**
310
-     * Given a ton of input, determines the value to use for the models.
311
-     *
312
-     * @return array|null
313
-     * @throws DomainException
314
-     * @throws EE_Error
315
-     * @throws RestException
316
-     * @throws DomainException
317
-     * @since 4.9.72.p
318
-     */
319
-    public function determineConditionsQueryParameterValue(): ?array
320
-    {
321
-        if ($this->valueIsArrayDuringRead()) {
322
-            return $this->determineModelValueGivenRestInputArray();
323
-        }
324
-        return ModelDataTranslator::prepareFieldValueFromJson(
325
-            $this->getField(),
326
-            $this->getQueryParamValue(),
327
-            $this->getContext()->getRequestedVersion(),
328
-            $this->getTimezone()
329
-        );
330
-    }
331
-
332
-
333
-    /**
334
-     * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
335
-     *
336
-     * @return array|null
337
-     * @throws EE_Error
338
-     * @throws RestException
339
-     * @since 4.9.72.p
340
-     */
341
-    private function determineModelValueGivenRestInputArray(): ?array
342
-    {
343
-        $this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
344
-        // did they specify an operator?
345
-        if ($this->valueIsLegacySpecifiedOperator()) {
346
-            $query_param_value = $this->getQueryParamValue();
347
-            $sub_array_key     = $query_param_value[0];
348
-            $translated_value  = [$sub_array_key];
349
-            if ($this->operatorIsNAry($sub_array_key)) {
350
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
351
-            } elseif ($this->operatorIsTernary($sub_array_key)) {
352
-                $translated_value[] = [
353
-                    $this->prepareValuesFromJson($query_param_value[1][0]),
354
-                    $this->prepareValuesFromJson($query_param_value[1][1]),
355
-                ];
356
-            } elseif ($this->operatorIsLike($sub_array_key)) {
357
-                // we want to leave this value mostly-as-is (eg don't force it to be a float
358
-                // or a boolean or an enum value. Leave it as-is with wildcards etc)
359
-                // but do verify it at least doesn't have any serialized data
360
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
361
-                $translated_value[] = $query_param_value[1];
362
-            } elseif ($this->operatorIsUnary($sub_array_key)) {
363
-                // no arguments should have been provided, so don't look for any
364
-            } elseif ($this->operatorIsBinary($sub_array_key)) {
365
-                // it's a valid operator, but none of the exceptions. Treat it normally.
366
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
367
-            } else {
368
-                // so they provided a valid operator, but wrong number of arguments
369
-                $this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
370
-                $translated_value = null;
371
-            }
372
-        } else {
373
-            // so they didn't provide a valid operator
374
-            // if we aren't in debug mode, then just try our best to fulfill the user's request
375
-            $this->throwInvalidOperatorExceptionIfDebugging();
376
-            $translated_value = null;
377
-        }
378
-        return $translated_value;
379
-    }
380
-
381
-
382
-    /**
383
-     * Returns if this request is a "read" request and the value provided was an array.
384
-     * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
385
-     *
386
-     * @return boolean
387
-     * @since 4.9.72.p
388
-     */
389
-    private function valueIsArrayDuringRead(): bool
390
-    {
391
-        return ! $this->getContext()->isWriting() && is_array($this->getQueryParamValue());
392
-    }
393
-
394
-
395
-    /**
396
-     * Returns if the value provided was an associative array (we should have already verified it's an array of some
397
-     * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
398
-     *
399
-     * @return boolean
400
-     * @since 4.9.72.p
401
-     */
402
-    private function valueIsAssociativeArray(): bool
403
-    {
404
-        return ! EEH_Array::is_array_numerically_and_sequentially_indexed((array) $this->getQueryParamValue());
405
-    }
406
-
407
-
408
-    /**
409
-     * Checks if the array value is itself an array that fits into the simplified specified operator structure
410
-     * (eg `array('!=' => 123)`).
411
-     *
412
-     * @return boolean
413
-     * @since 4.9.72.p
414
-     */
415
-    private function valueIsSimplifiedSpecifiedOperator(): bool
416
-    {
417
-        return count($this->getQueryParamValue()) === 1
418
-               && array_key_exists(
419
-                   key($this->getQueryParamValue()),
420
-                   $this->getContext()->getModel()->valid_operators()
421
-               );
422
-    }
423
-
424
-
425
-    /**
426
-     * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
427
-     * of either comma-separated-values, or a JSON array.
428
-     *
429
-     * @param $sub_array_key
430
-     * @param $sub_array_value
431
-     * @throws RestException
432
-     * @since 4.9.72.p
433
-     */
434
-    private function assertSubValueIsNotArray($sub_array_key, $sub_array_value)
435
-    {
436
-        if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
437
-            throw new RestException(
438
-                'csv_or_json_string_only',
439
-                sprintf(
440
-                /* translators: 1: variable name*/
441
-                    esc_html__(
442
-                        'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
443
-                        'event_espresso'
444
-                    ),
445
-                    $sub_array_key
446
-                ),
447
-                [
448
-                    'status' => 400,
449
-                ]
450
-            );
451
-        }
452
-    }
453
-
454
-
455
-    /**
456
-     * Determines if the sub-array key is an operator taking 3 or more operators.
457
-     *
458
-     * @param $sub_array_key
459
-     * @return boolean
460
-     * @since 4.9.72.p
461
-     */
462
-    private function subArrayKeyIsNonBinaryOperator($sub_array_key): bool
463
-    {
464
-        return array_key_exists(
465
-            $sub_array_key,
466
-            array_merge(
467
-                $this->getContext()->getModel()->valid_in_style_operators(),
468
-                $this->getContext()->getModel()->valid_between_style_operators()
469
-            )
470
-        );
471
-    }
472
-
473
-
474
-    /**
475
-     * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
476
-     *
477
-     * @param string $sub_array_key
478
-     * @return boolean
479
-     * @since 4.9.72.p
480
-     */
481
-    private function subArrayKeyIsUnaryOperator(string $sub_array_key): bool
482
-    {
483
-        return array_key_exists(
484
-            $sub_array_key,
485
-            $this->getContext()->getModel()->valid_null_style_operators()
486
-        );
487
-    }
488
-
489
-
490
-    /**
491
-     * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
492
-     * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
493
-     *
494
-     * @param $sub_array_value
495
-     * @return array|mixed|object
496
-     * @since 4.9.72.p
497
-     */
498
-    private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
499
-    {
500
-        // the value should be JSON or CSV
501
-        $values = json_decode($sub_array_value);
502
-        if (! is_array($values)) {
503
-            $values = array_filter(
504
-                array_map(
505
-                    'trim',
506
-                    explode(
507
-                        ',',
508
-                        $sub_array_value
509
-                    )
510
-                )
511
-            );
512
-        }
513
-        return $values;
514
-    }
515
-
516
-
517
-    /**
518
-     * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
519
-     *
520
-     * @throws RestException
521
-     * @since 4.9.72.p
522
-     */
523
-    private function assertSimplifiedSpecifiedOperator()
524
-    {
525
-        if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
526
-            throw new RestException(
527
-                'numerically_indexed_array_of_values_only',
528
-                sprintf(
529
-                /* translators: 1: variable name*/
530
-                    esc_html__(
531
-                        'The array provided for the parameter "%1$s" should be numerically indexed.',
532
-                        'event_espresso'
533
-                    ),
534
-                    $this->getQueryParamKey()
535
-                ),
536
-                [
537
-                    'status' => 400,
538
-                ]
539
-            );
540
-        }
541
-    }
542
-
543
-
544
-    /**
545
-     * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
546
-     *
547
-     * @throws RestException
548
-     * @since 4.9.72.p
549
-     */
550
-    private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
551
-    {
552
-        if ($this->valueIsAssociativeArray()) {
553
-            $this->assertSimplifiedSpecifiedOperator();
554
-            $query_param_value = $this->getQueryParamValue();
555
-            $sub_array_value   = reset($query_param_value);
556
-            $sub_array_key     = key($query_param_value);
557
-            $this->assertSubValueIsNotArray($sub_array_key, $sub_array_value);
558
-            // they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
559
-            if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
560
-                $this->setQueryParamValue([
561
-                                              $sub_array_key,
562
-                                              $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value),
563
-                                          ]);
564
-            } elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
565
-                $this->setQueryParamValue([$sub_array_key]);
566
-            } else {
567
-                $this->setQueryParamValue([$sub_array_key, $sub_array_value]);
568
-            }
569
-        }
570
-    }
571
-
572
-
573
-    /**
574
-     * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
575
-     *
576
-     * @return boolean
577
-     * @since 4.9.72.p
578
-     */
579
-    private function valueIsLegacySpecifiedOperator(): bool
580
-    {
581
-        $valid_operators   = $this->getContext()->getModel()->valid_operators();
582
-        $query_param_value = $this->getQueryParamValue();
583
-        return isset($query_param_value[0])
584
-               && isset($valid_operators[ $query_param_value[0] ]);
585
-    }
586
-
587
-
588
-    /**
589
-     * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
590
-     *
591
-     * @param $operator
592
-     * @return boolean
593
-     * @since 4.9.72.p
594
-     */
595
-    private function operatorIsNAry($operator): bool
596
-    {
597
-        $valueArray = $this->getQueryParamValue();
598
-        return array_key_exists(
599
-            $operator,
600
-            $this->getContext()->getModel()->valid_in_style_operators()
601
-        )
602
-               && isset($valueArray[1])
603
-               && is_array($valueArray[1])
604
-               && ! isset($valueArray[2]);
605
-    }
606
-
607
-
608
-    /**
609
-     * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
610
-     * So we're looking for a value that looks like
611
-     * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
612
-     *
613
-     * @param $operator
614
-     * @return boolean
615
-     * @since 4.9.72.p
616
-     */
617
-    private function operatorIsTernary($operator): bool
618
-    {
619
-        $query_param_value = $this->getQueryParamValue();
620
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
621
-               && isset($query_param_value[1])
622
-               && is_array($query_param_value[1])
623
-               && isset($query_param_value[1][0], $query_param_value[1][1])
624
-               && ! isset($query_param_value[1][2])
625
-               && ! isset($query_param_value[2]);
626
-    }
627
-
628
-
629
-    /**
630
-     * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
631
-     *
632
-     * @param $operator
633
-     * @return boolean
634
-     * @since 4.9.72.p
635
-     */
636
-    private function operatorIsLike($operator): bool
637
-    {
638
-        $query_param_value = $this->getQueryParamValue();
639
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
640
-               && isset($query_param_value[1])
641
-               && ! isset($query_param_value[2]);
642
-    }
643
-
644
-
645
-    /**
646
-     * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
647
-     *
648
-     * @param $operator
649
-     * @return boolean
650
-     * @since 4.9.72.p
651
-     */
652
-    private function operatorIsUnary($operator): bool
653
-    {
654
-        $query_param_value = $this->getQueryParamValue();
655
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
656
-               && ! isset($query_param_value[1]);
657
-    }
658
-
659
-
660
-    /**
661
-     * Returns true if the operator specified is a binary operator (eg `=`, `!=`)
662
-     *
663
-     * @param $operator
664
-     * @return boolean
665
-     * @since 4.9.72.p
666
-     */
667
-    private function operatorIsBinary($operator): bool
668
-    {
669
-        $query_param_value = $this->getQueryParamValue();
670
-        $model             = $this->getContext()->getModel();
671
-        return isset($query_param_value[1])
672
-               && ! isset($query_param_value[2])
673
-               && ! array_key_exists(
674
-                   $operator,
675
-                   array_merge(
676
-                       $model->valid_in_style_operators(),
677
-                       $model->valid_null_style_operators(),
678
-                       $model->valid_like_style_operators(),
679
-                       $model->valid_between_style_operators()
680
-                   )
681
-               );
682
-    }
683
-
684
-
685
-    /**
686
-     * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
687
-     *
688
-     * @param $operator
689
-     * @throws RestException
690
-     * @since 4.9.72.p
691
-     */
692
-    private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
693
-    {
694
-        if (EED_Core_Rest_Api::debugMode()) {
695
-            throw new RestException(
696
-                'wrong_number_of_arguments',
697
-                sprintf(
698
-                    esc_html__(
699
-                        'The operator you provided, "%1$s" had the wrong number of arguments',
700
-                        'event_espresso'
701
-                    ),
702
-                    $operator
703
-                ),
704
-                [
705
-                    'status' => 400,
706
-                ]
707
-            );
708
-        }
709
-    }
710
-
711
-
712
-    /**
713
-     * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
714
-     *
715
-     * @param $value
716
-     * @return mixed
717
-     * @throws EE_Error
718
-     * @throws RestException
719
-     * @since 4.9.72.p
720
-     */
721
-    private function prepareValuesFromJson($value)
722
-    {
723
-        return ModelDataTranslator::prepareFieldValuesFromJson(
724
-            $this->getField(),
725
-            $value,
726
-            $this->getContext()->getRequestedVersion(),
727
-            $this->getTimezone()
728
-        );
729
-    }
730
-
731
-
732
-    /**
733
-     * Throws an exception if an invalid operator was specified and we're debugging.
734
-     *
735
-     * @throws RestException
736
-     * @since 4.9.72.p
737
-     */
738
-    private function throwInvalidOperatorExceptionIfDebugging()
739
-    {
740
-        // so they didn't provide a valid operator
741
-        if (EED_Core_Rest_Api::debugMode()) {
742
-            throw new RestException(
743
-                'invalid_operator',
744
-                sprintf(
745
-                    esc_html__(
746
-                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
747
-                        'event_espresso'
748
-                    ),
749
-                    $this->getQueryParamKey(),
750
-                    $this->getQueryParamValue()
751
-                ),
752
-                [
753
-                    'status' => 400,
754
-                ]
755
-            );
756
-        }
757
-    }
758
-
759
-
760
-    /**
761
-     * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
762
-     *
763
-     * @return boolean
764
-     * @since 4.9.72.p
765
-     */
766
-    private function isLogicQueryParam(): bool
767
-    {
768
-        return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
769
-    }
770
-
771
-
772
-    /**
773
-     * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
774
-     *
775
-     * @return array
776
-     * @throws DomainException
777
-     * @throws EE_Error
778
-     * @throws RestException
779
-     * @throws InvalidDataTypeException
780
-     * @throws InvalidInterfaceException
781
-     * @throws InvalidArgumentException
782
-     * @since 4.9.72.p
783
-     */
784
-    public function determineNestedConditionQueryParameters(): ?array
785
-    {
786
-        // so this param doesn't correspond to a field eh?
787
-        if ($this->getContext()->isWriting()) {
788
-            // always tell API clients about invalid parameters when they're creating data. Otherwise,
789
-            // they are probably going to create invalid data
790
-            throw new RestException(
791
-                'invalid_field',
792
-                sprintf(
793
-                /* translators: 1: variable name */
794
-                    esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
795
-                    $this->getQueryParamKey()
796
-                )
797
-            );
798
-        }
799
-        // so it's not for a field, is it a logic query param key?
800
-        if ($this->isLogicQueryParam()) {
801
-            return ModelDataTranslator::prepareConditionsQueryParamsForModels(
802
-                $this->getQueryParamValue(),
803
-                $this->getContext()->getModel(),
804
-                $this->getContext()->getRequestedVersion()
805
-            );
806
-        }
807
-        if (EED_Core_Rest_Api::debugMode()) {
808
-            // only tell API clients they got it wrong if we're in debug mode
809
-            // otherwise try our best ot fulfill their request by ignoring this invalid data
810
-            throw new RestException(
811
-                'invalid_parameter',
812
-                sprintf(
813
-                /* translators: 1: variable name */
814
-                    esc_html__(
815
-                        'You provided an invalid parameter, with key "%1$s"',
816
-                        'event_espresso'
817
-                    ),
818
-                    $this->getQueryParamKey()
819
-                ),
820
-                [
821
-                    'status' => 400,
822
-                ]
823
-            );
824
-        }
825
-        return null;
826
-    }
32
+	/**
33
+	 * @var string
34
+	 */
35
+	private $query_param_key;
36
+
37
+	/**
38
+	 * @var mixed
39
+	 */
40
+	private $query_param_value;
41
+
42
+	/**
43
+	 * @var RestIncomingQueryParamContext
44
+	 */
45
+	private $context;
46
+
47
+	/**
48
+	 * @var EE_Model_Field_Base|null
49
+	 */
50
+	private $field;
51
+
52
+	/**
53
+	 * @var string same as $query_param_key but has the * and anything after it removed
54
+	 */
55
+	private $query_param_key_sans_stars;
56
+
57
+	/**
58
+	 * @var string for timezone or timezone offset
59
+	 */
60
+	private $timezone;
61
+
62
+	/**
63
+	 * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
64
+	 */
65
+	private $is_gmt_field = false;
66
+
67
+
68
+	/**
69
+	 * RestIncomingQueryParamMetadata constructor.
70
+	 * You probably want to call
71
+	 *
72
+	 * @param string                        $query_param_key
73
+	 * @param mixed                         $query_param_value
74
+	 * @param RestIncomingQueryParamContext $context
75
+	 * @throws EE_Error
76
+	 * @throws RestException
77
+	 * @throws ReflectionException
78
+	 */
79
+	public function __construct(string $query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
80
+	{
81
+		$this->query_param_key   = $query_param_key;
82
+		$this->query_param_value = $query_param_value;
83
+		$this->context           = $context;
84
+		$this->determineFieldAndTimezone();
85
+	}
86
+
87
+
88
+	/**
89
+	 * Gets the query parameter key. This may have been modified (see setQueryParamValue())
90
+	 *
91
+	 * @return string
92
+	 */
93
+	public function getQueryParamKey(): string
94
+	{
95
+		return $this->query_param_key;
96
+	}
97
+
98
+
99
+	/**
100
+	 * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
101
+	 * query parameters into the legacy structure)
102
+	 *
103
+	 * @param string|array|int|float $query_param_value
104
+	 */
105
+	private function setQueryParamValue($query_param_value)
106
+	{
107
+		$this->query_param_value = $query_param_value;
108
+	}
109
+
110
+
111
+	/**
112
+	 * Gets the original query parameter value passed in.
113
+	 *
114
+	 * @return mixed
115
+	 */
116
+	public function getQueryParamValue()
117
+	{
118
+		return $this->query_param_value;
119
+	}
120
+
121
+
122
+	/**
123
+	 * Gets the context object.
124
+	 *
125
+	 * @return RestIncomingQueryParamContext
126
+	 */
127
+	public function getContext(): RestIncomingQueryParamContext
128
+	{
129
+		return $this->context;
130
+	}
131
+
132
+
133
+	/**
134
+	 * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
135
+	 *
136
+	 * @param string $query_param_key
137
+	 */
138
+	private function setQueryParamKey(string $query_param_key)
139
+	{
140
+		$this->query_param_key = $query_param_key;
141
+	}
142
+
143
+
144
+	/**
145
+	 * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
146
+	 * did not indicate a field, eg if it were `OR`).
147
+	 *
148
+	 * @return EE_Model_Field_Base|null
149
+	 */
150
+	public function getField(): ?EE_Model_Field_Base
151
+	{
152
+		return $this->field;
153
+	}
154
+
155
+
156
+	/**
157
+	 * Gets the query parameter key (with the star and everything afterwards removed).
158
+	 *
159
+	 * @return string
160
+	 */
161
+	public function getQueryParamKeySansStars(): string
162
+	{
163
+		return $this->query_param_key_sans_stars;
164
+	}
165
+
166
+
167
+	/**
168
+	 * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
169
+	 *
170
+	 * @return string
171
+	 */
172
+	public function getTimezone(): string
173
+	{
174
+		return $this->timezone;
175
+	}
176
+
177
+
178
+	/**
179
+	 * Returns whether or not this is a GMT field
180
+	 *
181
+	 * @return boolean
182
+	 */
183
+	public function isGmtField(): bool
184
+	{
185
+		return $this->is_gmt_field;
186
+	}
187
+
188
+
189
+	/**
190
+	 * Sets the field indicated by the query parameter key (might be null).
191
+	 *
192
+	 * @param EE_Model_Field_Base|null $field
193
+	 */
194
+	private function setField(EE_Model_Field_Base $field = null)
195
+	{
196
+		$this->field = $field;
197
+	}
198
+
199
+
200
+	/**
201
+	 * Sets the query parameter key-with-stars-removed.
202
+	 *
203
+	 * @param string $query_param_key_sans_stars
204
+	 */
205
+	private function setQueryParamKeySansStars(string $query_param_key_sans_stars)
206
+	{
207
+		$this->query_param_key_sans_stars = $query_param_key_sans_stars;
208
+	}
209
+
210
+
211
+	/**
212
+	 * Sets the timezone (this could be a timezone offset string).
213
+	 *
214
+	 * @param string $timezone
215
+	 */
216
+	private function setTimezone(string $timezone)
217
+	{
218
+		$this->timezone = $timezone;
219
+	}
220
+
221
+
222
+	/**
223
+	 * @param bool|int|string $is_gmt_field
224
+	 */
225
+	private function setIsGmtField($is_gmt_field)
226
+	{
227
+		$this->is_gmt_field = filter_var($is_gmt_field, FILTER_VALIDATE_BOOLEAN);
228
+	}
229
+
230
+
231
+	/**
232
+	 * Determines what field, query param name, and query param name without stars, and timezone to use.
233
+	 *
234
+	 * @return void {
235
+	 * @throws EE_Error
236
+	 * @throws RestException
237
+	 * @throws ReflectionException
238
+	 * @since 4.9.72.p
239
+	 *
240
+	 */
241
+	private function determineFieldAndTimezone()
242
+	{
243
+		$this->setQueryParamKeySansStars(
244
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
245
+				$this->getQueryParamKey()
246
+			)
247
+		);
248
+		$this->setField(
249
+			ModelDataTranslator::deduceFieldFromQueryParam(
250
+				$this->getQueryParamKeySansStars(),
251
+				$this->getContext()->getModel()
252
+			)
253
+		);
254
+		// double-check is it a *_gmt field?
255
+		if (
256
+			! $this->getField() instanceof EE_Model_Field_Base
257
+			&& ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
258
+		) {
259
+			// yep, take off '_gmt', and find the field
260
+			$this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
261
+			$this->setField(
262
+				ModelDataTranslator::deduceFieldFromQueryParam(
263
+					$this->getQueryParamKey(),
264
+					$this->context->getModel()
265
+				)
266
+			);
267
+			$this->setTimezone('UTC');
268
+			$this->setIsGmtField(true);
269
+		} elseif ($this->getField() instanceof EE_Datetime_Field) {
270
+			// so it's not a GMT field. Set the timezone on the model to the default
271
+			$this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
272
+		} else {
273
+			// just keep using what's already set for the timezone
274
+			$this->setTimezone($this->context->getModel()->get_timezone());
275
+		}
276
+		$this->assertOnlyAdminCanReadPasswordFields();
277
+	}
278
+
279
+
280
+	/**
281
+	 * Throws an exception if a non-admin is trying to query by password.
282
+	 *
283
+	 * @throws RestException
284
+	 * @since 4.9.74.p
285
+	 */
286
+	private function assertOnlyAdminCanReadPasswordFields()
287
+	{
288
+		if (
289
+			$this->getField() instanceof EE_Password_Field
290
+			&& ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())
291
+		) {
292
+			// only full admins can query by password. sorry bub!
293
+			throw new RestException(
294
+				'only_admins_can_query_by_password',
295
+				// @codingStandardsIgnoreStart
296
+				esc_html__(
297
+					'You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.',
298
+					'event_espresso'
299
+				),
300
+				// @codingStandardsIgnoreEnd
301
+				[
302
+					'status' => 403,
303
+				]
304
+			);
305
+		}
306
+	}
307
+
308
+
309
+	/**
310
+	 * Given a ton of input, determines the value to use for the models.
311
+	 *
312
+	 * @return array|null
313
+	 * @throws DomainException
314
+	 * @throws EE_Error
315
+	 * @throws RestException
316
+	 * @throws DomainException
317
+	 * @since 4.9.72.p
318
+	 */
319
+	public function determineConditionsQueryParameterValue(): ?array
320
+	{
321
+		if ($this->valueIsArrayDuringRead()) {
322
+			return $this->determineModelValueGivenRestInputArray();
323
+		}
324
+		return ModelDataTranslator::prepareFieldValueFromJson(
325
+			$this->getField(),
326
+			$this->getQueryParamValue(),
327
+			$this->getContext()->getRequestedVersion(),
328
+			$this->getTimezone()
329
+		);
330
+	}
331
+
332
+
333
+	/**
334
+	 * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
335
+	 *
336
+	 * @return array|null
337
+	 * @throws EE_Error
338
+	 * @throws RestException
339
+	 * @since 4.9.72.p
340
+	 */
341
+	private function determineModelValueGivenRestInputArray(): ?array
342
+	{
343
+		$this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
344
+		// did they specify an operator?
345
+		if ($this->valueIsLegacySpecifiedOperator()) {
346
+			$query_param_value = $this->getQueryParamValue();
347
+			$sub_array_key     = $query_param_value[0];
348
+			$translated_value  = [$sub_array_key];
349
+			if ($this->operatorIsNAry($sub_array_key)) {
350
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
351
+			} elseif ($this->operatorIsTernary($sub_array_key)) {
352
+				$translated_value[] = [
353
+					$this->prepareValuesFromJson($query_param_value[1][0]),
354
+					$this->prepareValuesFromJson($query_param_value[1][1]),
355
+				];
356
+			} elseif ($this->operatorIsLike($sub_array_key)) {
357
+				// we want to leave this value mostly-as-is (eg don't force it to be a float
358
+				// or a boolean or an enum value. Leave it as-is with wildcards etc)
359
+				// but do verify it at least doesn't have any serialized data
360
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
361
+				$translated_value[] = $query_param_value[1];
362
+			} elseif ($this->operatorIsUnary($sub_array_key)) {
363
+				// no arguments should have been provided, so don't look for any
364
+			} elseif ($this->operatorIsBinary($sub_array_key)) {
365
+				// it's a valid operator, but none of the exceptions. Treat it normally.
366
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
367
+			} else {
368
+				// so they provided a valid operator, but wrong number of arguments
369
+				$this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
370
+				$translated_value = null;
371
+			}
372
+		} else {
373
+			// so they didn't provide a valid operator
374
+			// if we aren't in debug mode, then just try our best to fulfill the user's request
375
+			$this->throwInvalidOperatorExceptionIfDebugging();
376
+			$translated_value = null;
377
+		}
378
+		return $translated_value;
379
+	}
380
+
381
+
382
+	/**
383
+	 * Returns if this request is a "read" request and the value provided was an array.
384
+	 * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
385
+	 *
386
+	 * @return boolean
387
+	 * @since 4.9.72.p
388
+	 */
389
+	private function valueIsArrayDuringRead(): bool
390
+	{
391
+		return ! $this->getContext()->isWriting() && is_array($this->getQueryParamValue());
392
+	}
393
+
394
+
395
+	/**
396
+	 * Returns if the value provided was an associative array (we should have already verified it's an array of some
397
+	 * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
398
+	 *
399
+	 * @return boolean
400
+	 * @since 4.9.72.p
401
+	 */
402
+	private function valueIsAssociativeArray(): bool
403
+	{
404
+		return ! EEH_Array::is_array_numerically_and_sequentially_indexed((array) $this->getQueryParamValue());
405
+	}
406
+
407
+
408
+	/**
409
+	 * Checks if the array value is itself an array that fits into the simplified specified operator structure
410
+	 * (eg `array('!=' => 123)`).
411
+	 *
412
+	 * @return boolean
413
+	 * @since 4.9.72.p
414
+	 */
415
+	private function valueIsSimplifiedSpecifiedOperator(): bool
416
+	{
417
+		return count($this->getQueryParamValue()) === 1
418
+			   && array_key_exists(
419
+				   key($this->getQueryParamValue()),
420
+				   $this->getContext()->getModel()->valid_operators()
421
+			   );
422
+	}
423
+
424
+
425
+	/**
426
+	 * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
427
+	 * of either comma-separated-values, or a JSON array.
428
+	 *
429
+	 * @param $sub_array_key
430
+	 * @param $sub_array_value
431
+	 * @throws RestException
432
+	 * @since 4.9.72.p
433
+	 */
434
+	private function assertSubValueIsNotArray($sub_array_key, $sub_array_value)
435
+	{
436
+		if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
437
+			throw new RestException(
438
+				'csv_or_json_string_only',
439
+				sprintf(
440
+				/* translators: 1: variable name*/
441
+					esc_html__(
442
+						'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
443
+						'event_espresso'
444
+					),
445
+					$sub_array_key
446
+				),
447
+				[
448
+					'status' => 400,
449
+				]
450
+			);
451
+		}
452
+	}
453
+
454
+
455
+	/**
456
+	 * Determines if the sub-array key is an operator taking 3 or more operators.
457
+	 *
458
+	 * @param $sub_array_key
459
+	 * @return boolean
460
+	 * @since 4.9.72.p
461
+	 */
462
+	private function subArrayKeyIsNonBinaryOperator($sub_array_key): bool
463
+	{
464
+		return array_key_exists(
465
+			$sub_array_key,
466
+			array_merge(
467
+				$this->getContext()->getModel()->valid_in_style_operators(),
468
+				$this->getContext()->getModel()->valid_between_style_operators()
469
+			)
470
+		);
471
+	}
472
+
473
+
474
+	/**
475
+	 * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
476
+	 *
477
+	 * @param string $sub_array_key
478
+	 * @return boolean
479
+	 * @since 4.9.72.p
480
+	 */
481
+	private function subArrayKeyIsUnaryOperator(string $sub_array_key): bool
482
+	{
483
+		return array_key_exists(
484
+			$sub_array_key,
485
+			$this->getContext()->getModel()->valid_null_style_operators()
486
+		);
487
+	}
488
+
489
+
490
+	/**
491
+	 * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
492
+	 * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
493
+	 *
494
+	 * @param $sub_array_value
495
+	 * @return array|mixed|object
496
+	 * @since 4.9.72.p
497
+	 */
498
+	private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
499
+	{
500
+		// the value should be JSON or CSV
501
+		$values = json_decode($sub_array_value);
502
+		if (! is_array($values)) {
503
+			$values = array_filter(
504
+				array_map(
505
+					'trim',
506
+					explode(
507
+						',',
508
+						$sub_array_value
509
+					)
510
+				)
511
+			);
512
+		}
513
+		return $values;
514
+	}
515
+
516
+
517
+	/**
518
+	 * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
519
+	 *
520
+	 * @throws RestException
521
+	 * @since 4.9.72.p
522
+	 */
523
+	private function assertSimplifiedSpecifiedOperator()
524
+	{
525
+		if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
526
+			throw new RestException(
527
+				'numerically_indexed_array_of_values_only',
528
+				sprintf(
529
+				/* translators: 1: variable name*/
530
+					esc_html__(
531
+						'The array provided for the parameter "%1$s" should be numerically indexed.',
532
+						'event_espresso'
533
+					),
534
+					$this->getQueryParamKey()
535
+				),
536
+				[
537
+					'status' => 400,
538
+				]
539
+			);
540
+		}
541
+	}
542
+
543
+
544
+	/**
545
+	 * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
546
+	 *
547
+	 * @throws RestException
548
+	 * @since 4.9.72.p
549
+	 */
550
+	private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
551
+	{
552
+		if ($this->valueIsAssociativeArray()) {
553
+			$this->assertSimplifiedSpecifiedOperator();
554
+			$query_param_value = $this->getQueryParamValue();
555
+			$sub_array_value   = reset($query_param_value);
556
+			$sub_array_key     = key($query_param_value);
557
+			$this->assertSubValueIsNotArray($sub_array_key, $sub_array_value);
558
+			// they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
559
+			if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
560
+				$this->setQueryParamValue([
561
+											  $sub_array_key,
562
+											  $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value),
563
+										  ]);
564
+			} elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
565
+				$this->setQueryParamValue([$sub_array_key]);
566
+			} else {
567
+				$this->setQueryParamValue([$sub_array_key, $sub_array_value]);
568
+			}
569
+		}
570
+	}
571
+
572
+
573
+	/**
574
+	 * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
575
+	 *
576
+	 * @return boolean
577
+	 * @since 4.9.72.p
578
+	 */
579
+	private function valueIsLegacySpecifiedOperator(): bool
580
+	{
581
+		$valid_operators   = $this->getContext()->getModel()->valid_operators();
582
+		$query_param_value = $this->getQueryParamValue();
583
+		return isset($query_param_value[0])
584
+			   && isset($valid_operators[ $query_param_value[0] ]);
585
+	}
586
+
587
+
588
+	/**
589
+	 * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
590
+	 *
591
+	 * @param $operator
592
+	 * @return boolean
593
+	 * @since 4.9.72.p
594
+	 */
595
+	private function operatorIsNAry($operator): bool
596
+	{
597
+		$valueArray = $this->getQueryParamValue();
598
+		return array_key_exists(
599
+			$operator,
600
+			$this->getContext()->getModel()->valid_in_style_operators()
601
+		)
602
+			   && isset($valueArray[1])
603
+			   && is_array($valueArray[1])
604
+			   && ! isset($valueArray[2]);
605
+	}
606
+
607
+
608
+	/**
609
+	 * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
610
+	 * So we're looking for a value that looks like
611
+	 * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
612
+	 *
613
+	 * @param $operator
614
+	 * @return boolean
615
+	 * @since 4.9.72.p
616
+	 */
617
+	private function operatorIsTernary($operator): bool
618
+	{
619
+		$query_param_value = $this->getQueryParamValue();
620
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
621
+			   && isset($query_param_value[1])
622
+			   && is_array($query_param_value[1])
623
+			   && isset($query_param_value[1][0], $query_param_value[1][1])
624
+			   && ! isset($query_param_value[1][2])
625
+			   && ! isset($query_param_value[2]);
626
+	}
627
+
628
+
629
+	/**
630
+	 * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
631
+	 *
632
+	 * @param $operator
633
+	 * @return boolean
634
+	 * @since 4.9.72.p
635
+	 */
636
+	private function operatorIsLike($operator): bool
637
+	{
638
+		$query_param_value = $this->getQueryParamValue();
639
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
640
+			   && isset($query_param_value[1])
641
+			   && ! isset($query_param_value[2]);
642
+	}
643
+
644
+
645
+	/**
646
+	 * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
647
+	 *
648
+	 * @param $operator
649
+	 * @return boolean
650
+	 * @since 4.9.72.p
651
+	 */
652
+	private function operatorIsUnary($operator): bool
653
+	{
654
+		$query_param_value = $this->getQueryParamValue();
655
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
656
+			   && ! isset($query_param_value[1]);
657
+	}
658
+
659
+
660
+	/**
661
+	 * Returns true if the operator specified is a binary operator (eg `=`, `!=`)
662
+	 *
663
+	 * @param $operator
664
+	 * @return boolean
665
+	 * @since 4.9.72.p
666
+	 */
667
+	private function operatorIsBinary($operator): bool
668
+	{
669
+		$query_param_value = $this->getQueryParamValue();
670
+		$model             = $this->getContext()->getModel();
671
+		return isset($query_param_value[1])
672
+			   && ! isset($query_param_value[2])
673
+			   && ! array_key_exists(
674
+				   $operator,
675
+				   array_merge(
676
+					   $model->valid_in_style_operators(),
677
+					   $model->valid_null_style_operators(),
678
+					   $model->valid_like_style_operators(),
679
+					   $model->valid_between_style_operators()
680
+				   )
681
+			   );
682
+	}
683
+
684
+
685
+	/**
686
+	 * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
687
+	 *
688
+	 * @param $operator
689
+	 * @throws RestException
690
+	 * @since 4.9.72.p
691
+	 */
692
+	private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
693
+	{
694
+		if (EED_Core_Rest_Api::debugMode()) {
695
+			throw new RestException(
696
+				'wrong_number_of_arguments',
697
+				sprintf(
698
+					esc_html__(
699
+						'The operator you provided, "%1$s" had the wrong number of arguments',
700
+						'event_espresso'
701
+					),
702
+					$operator
703
+				),
704
+				[
705
+					'status' => 400,
706
+				]
707
+			);
708
+		}
709
+	}
710
+
711
+
712
+	/**
713
+	 * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
714
+	 *
715
+	 * @param $value
716
+	 * @return mixed
717
+	 * @throws EE_Error
718
+	 * @throws RestException
719
+	 * @since 4.9.72.p
720
+	 */
721
+	private function prepareValuesFromJson($value)
722
+	{
723
+		return ModelDataTranslator::prepareFieldValuesFromJson(
724
+			$this->getField(),
725
+			$value,
726
+			$this->getContext()->getRequestedVersion(),
727
+			$this->getTimezone()
728
+		);
729
+	}
730
+
731
+
732
+	/**
733
+	 * Throws an exception if an invalid operator was specified and we're debugging.
734
+	 *
735
+	 * @throws RestException
736
+	 * @since 4.9.72.p
737
+	 */
738
+	private function throwInvalidOperatorExceptionIfDebugging()
739
+	{
740
+		// so they didn't provide a valid operator
741
+		if (EED_Core_Rest_Api::debugMode()) {
742
+			throw new RestException(
743
+				'invalid_operator',
744
+				sprintf(
745
+					esc_html__(
746
+						'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
747
+						'event_espresso'
748
+					),
749
+					$this->getQueryParamKey(),
750
+					$this->getQueryParamValue()
751
+				),
752
+				[
753
+					'status' => 400,
754
+				]
755
+			);
756
+		}
757
+	}
758
+
759
+
760
+	/**
761
+	 * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
762
+	 *
763
+	 * @return boolean
764
+	 * @since 4.9.72.p
765
+	 */
766
+	private function isLogicQueryParam(): bool
767
+	{
768
+		return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
769
+	}
770
+
771
+
772
+	/**
773
+	 * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
774
+	 *
775
+	 * @return array
776
+	 * @throws DomainException
777
+	 * @throws EE_Error
778
+	 * @throws RestException
779
+	 * @throws InvalidDataTypeException
780
+	 * @throws InvalidInterfaceException
781
+	 * @throws InvalidArgumentException
782
+	 * @since 4.9.72.p
783
+	 */
784
+	public function determineNestedConditionQueryParameters(): ?array
785
+	{
786
+		// so this param doesn't correspond to a field eh?
787
+		if ($this->getContext()->isWriting()) {
788
+			// always tell API clients about invalid parameters when they're creating data. Otherwise,
789
+			// they are probably going to create invalid data
790
+			throw new RestException(
791
+				'invalid_field',
792
+				sprintf(
793
+				/* translators: 1: variable name */
794
+					esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
795
+					$this->getQueryParamKey()
796
+				)
797
+			);
798
+		}
799
+		// so it's not for a field, is it a logic query param key?
800
+		if ($this->isLogicQueryParam()) {
801
+			return ModelDataTranslator::prepareConditionsQueryParamsForModels(
802
+				$this->getQueryParamValue(),
803
+				$this->getContext()->getModel(),
804
+				$this->getContext()->getRequestedVersion()
805
+			);
806
+		}
807
+		if (EED_Core_Rest_Api::debugMode()) {
808
+			// only tell API clients they got it wrong if we're in debug mode
809
+			// otherwise try our best ot fulfill their request by ignoring this invalid data
810
+			throw new RestException(
811
+				'invalid_parameter',
812
+				sprintf(
813
+				/* translators: 1: variable name */
814
+					esc_html__(
815
+						'You provided an invalid parameter, with key "%1$s"',
816
+						'event_espresso'
817
+					),
818
+					$this->getQueryParamKey()
819
+				),
820
+				[
821
+					'status' => 400,
822
+				]
823
+			);
824
+		}
825
+		return null;
826
+	}
827 827
 }
828 828
 // End of file RestQueryParamMetadata.php
829 829
 // Location: EventEspresso\core\libraries\rest_api/RestQueryParamMetadata.php
Please login to merge, or discard this patch.
core/services/notifications/AdminNotification.php 2 patches
Indentation   +434 added lines, -434 removed lines patch added patch discarded remove patch
@@ -11,438 +11,438 @@
 block discarded – undo
11 11
 
12 12
 class AdminNotification implements RequiresCapCheckInterface
13 13
 {
14
-    const STATUS_OPEN      = 'status-open';
15
-
16
-    const STATUS_CLOSED    = 'status-closed';
17
-
18
-    const STATUS_DISMISSED = 'status-dismissed';
19
-
20
-    const STATUS_VIEWED    = 'status-viewed';
21
-
22
-    const TYPE_CUSTOM      = 'notice-custom';
23
-
24
-    const TYPE_ERROR       = 'notice-error';
25
-
26
-    const TYPE_INFO        = 'notice-info';
27
-
28
-    const TYPE_SUCCESS     = 'notice-success';
29
-
30
-    const TYPE_WARNING     = 'notice-warning';
31
-
32
-
33
-    /**
34
-     * @var array
35
-     */
36
-    protected $allowed_tags = [];
37
-
38
-    /**
39
-     * @var CapCheckInterface
40
-     */
41
-    protected $cap_check;
42
-
43
-    /**
44
-     * @var string
45
-     */
46
-    protected $capability = '';
47
-
48
-    /**
49
-     * @var string
50
-     */
51
-    protected $cap_context = '';
52
-
53
-    /**
54
-     * @var array
55
-     */
56
-    protected $css_classes = [];
57
-
58
-    /**
59
-     * @var string
60
-     */
61
-    protected $identifier = '';
62
-
63
-    /**
64
-     * @var bool
65
-     */
66
-    protected $is_dismissible = false;
67
-
68
-    /**
69
-     * @var string
70
-     */
71
-    protected $message = '';
72
-
73
-    /**
74
-     * @var string
75
-     */
76
-    protected $role = '';
77
-
78
-    /**
79
-     * @var string
80
-     */
81
-    protected $status = '';
82
-
83
-    /**
84
-     * @var array
85
-     */
86
-    protected $valid_statuses = [];
87
-
88
-    /**
89
-     * @var string
90
-     */
91
-    protected $type;
92
-
93
-    /**
94
-     * @var array
95
-     */
96
-    protected $valid_types = [
97
-        AdminNotification::TYPE_CUSTOM,
98
-        AdminNotification::TYPE_ERROR,
99
-        AdminNotification::TYPE_INFO,
100
-        AdminNotification::TYPE_SUCCESS,
101
-        AdminNotification::TYPE_WARNING,
102
-    ];
103
-
104
-
105
-    /**
106
-     * PersistentAdminNotice constructor
107
-     *
108
-     * @param string     $identifier   [required] the name, or key of the Admin Notice
109
-     * @param string     $message      [required] the message to be displayed to users
110
-     * @param string     $type         whether notice is an error, info, success, or warning
111
-     * @param bool       $autoload     whether to automatically hook into "admin_notices" to display notice
112
-     * @param array|null $allowed_tags tags & attributes passed to wp_kses for sanitizing notice content
113
-     */
114
-    public function __construct(
115
-        string $identifier,
116
-        string $message,
117
-        string $type = AdminNotification::TYPE_INFO,
118
-        bool $autoload = true,
119
-        ?array $allowed_tags = []
120
-    ) {
121
-        $this->identifier = sanitize_key($identifier);
122
-        $this->setType($type);
123
-        $this->setMessage($message);
124
-        $this->setAllowedTags($allowed_tags);
125
-        if ($autoload) {
126
-            add_action('admin_notices', [$this, 'displayNotification']);
127
-        }
128
-    }
129
-
130
-
131
-    /**
132
-     * user capability required to view this notice
133
-     *
134
-     * @return string
135
-     */
136
-    public function capability(): string
137
-    {
138
-        return $this->capability;
139
-    }
140
-
141
-
142
-    /**
143
-     * description for why the cap check is being performed
144
-     *
145
-     * @return string
146
-     */
147
-    public function capContext(): string
148
-    {
149
-        return $this->cap_context;
150
-    }
151
-
152
-
153
-    /**
154
-     * @return array
155
-     */
156
-    public function cssClasses(): array
157
-    {
158
-        return $this->css_classes;
159
-    }
160
-
161
-
162
-    /**
163
-     * @return string
164
-     */
165
-    public function cssClassString(): string
166
-    {
167
-        return implode(' ', $this->css_classes);
168
-    }
169
-
170
-
171
-    /**
172
-     * @return CapCheckInterface
173
-     */
174
-    public function getCapCheck(): CapCheckInterface
175
-    {
176
-        if (! $this->cap_check instanceof CapCheckInterface) {
177
-            $this->setCapCheck(
178
-                new CapCheck($this->capability, $this->cap_context)
179
-            );
180
-        }
181
-        return $this->cap_check;
182
-    }
183
-
184
-
185
-    public function displayNotification($return = false): string
186
-    {
187
-        $id           = esc_attr($this->identifier);
188
-        $class        = esc_attr($this->cssClassString());
189
-        $notification = "<div id='$id' class='$class'>$this->message</div>";
190
-        if ($return) {
191
-            return $notification;
192
-        }
193
-        echo wp_kses($notification, $this->allowed_tags);
194
-        return '';
195
-    }
196
-
197
-
198
-    /**
199
-     * name/identifier for notice
200
-     *
201
-     * @return string
202
-     */
203
-    public function identifier(): string
204
-    {
205
-        return $this->identifier;
206
-    }
207
-
208
-
209
-    /**
210
-     * @return bool
211
-     */
212
-    public function isDismissible(): bool
213
-    {
214
-        return $this->is_dismissible;
215
-    }
216
-
217
-
218
-    /**
219
-     * the actual content displayed to the user
220
-     *
221
-     * @return string
222
-     */
223
-    public function message(): string
224
-    {
225
-        return $this->message;
226
-    }
227
-
228
-
229
-    /**
230
-     * notice will only displayed to users with this role
231
-     *
232
-     * @return string
233
-     */
234
-    public function role(): string
235
-    {
236
-        return $this->role;
237
-    }
238
-
239
-
240
-    /**
241
-     * whether notice is open/dismissed/viewed/etc one of the AdminNotification::STATUS_* constants
242
-     *
243
-     * @return string
244
-     */
245
-    public function status(): string
246
-    {
247
-        return $this->status;
248
-    }
249
-
250
-
251
-    /**
252
-     * array of valid notification status options (AdminNotification::STATUS_* constants)
253
-     *
254
-     * @return array|string[]
255
-     */
256
-    public function validStatuses(): array
257
-    {
258
-        return $this->valid_statuses;
259
-    }
260
-
261
-
262
-    /**
263
-     * array of valid notification types (AdminNotification::TYPE_* constants)
264
-     *
265
-     * @return array|string[]
266
-     */
267
-    public function validTypes(): array
268
-    {
269
-        return $this->valid_types;
270
-    }
271
-
272
-
273
-    /**
274
-     * @param array $allowed_tags
275
-     */
276
-    public function setAllowedTags(array $allowed_tags = []): void
277
-    {
278
-        $this->allowed_tags = ! empty($allowed_tags) ? $allowed_tags : AllowedTags::getWithFullTags();
279
-    }
280
-
281
-
282
-    /**
283
-     * @param string|null $capability user capability required to view this notice [default] 'manage_options'
284
-     */
285
-    protected function setCapability(?string $capability = 'manage_options')
286
-    {
287
-        $this->capability = sanitize_key($capability);
288
-    }
289
-
290
-
291
-    /**
292
-     * @param CapCheckInterface|null $cap_check
293
-     */
294
-    protected function setCapCheck(?CapCheckInterface $cap_check = null)
295
-    {
296
-        $this->cap_check = $cap_check;
297
-    }
298
-
299
-
300
-    /**
301
-     * @param string $cap_context description for why the cap check is being performed
302
-     */
303
-    protected function setCapContext(string $cap_context = 'view admin notice')
304
-    {
305
-        $this->cap_context = sanitize_text_field($cap_context);
306
-    }
307
-
308
-
309
-    /**
310
-     * @param string $css_class
311
-     * @param bool   $remove_duplicates
312
-     */
313
-    public function addCssClass(string $css_class, bool $remove_duplicates = true): void
314
-    {
315
-        $this->css_classes[] = sanitize_key($css_class);
316
-        if ($remove_duplicates) {
317
-            $this->removeDuplicateCssClasses();
318
-        }
319
-    }
320
-
321
-
322
-    /**
323
-     * @param array $css_classes
324
-     */
325
-    public function addCssClasses(array $css_classes): void
326
-    {
327
-        foreach ($css_classes as $css_class) {
328
-            $this->addCssClass($css_class, false);
329
-        }
330
-        $this->removeDuplicateCssClasses();
331
-    }
332
-
333
-
334
-    /**
335
-     * @return void
336
-     */
337
-    public function removeDuplicateCssClasses(): void
338
-    {
339
-        $this->css_classes = array_unique($this->css_classes, SORT_STRING);
340
-    }
341
-
342
-
343
-    /**
344
-     * @param string $css_class
345
-     */
346
-    public function removeCssClass(string $css_class): void
347
-    {
348
-        $this->css_classes = array_filter(
349
-            $this->css_classes,
350
-            function ($existing_class) use ($css_class) {
351
-                return $existing_class !== $css_class;
352
-            }
353
-        );
354
-    }
355
-
356
-
357
-    /**
358
-     * @param bool $is_dismissible
359
-     */
360
-    public function setIsDismissible(bool $is_dismissible = false): void
361
-    {
362
-        $this->is_dismissible = filter_var($is_dismissible, FILTER_VALIDATE_BOOLEAN);
363
-        if ($this->is_dismissible) {
364
-            $this->addCssClass('is-dismissible');
365
-        } else {
366
-            $this->removeCssClass('is-dismissible');
367
-        }
368
-    }
369
-
370
-
371
-    /**
372
-     * @param string $message the actual content displayed to the user
373
-     */
374
-    protected function setMessage(string $message)
375
-    {
376
-        $this->message = $message;
377
-    }
378
-
379
-
380
-    /**
381
-     * @param string $role notice will only displayed to users with this role
382
-     */
383
-    protected function setRole(string $role): void
384
-    {
385
-        $this->role = $role;
386
-    }
387
-
388
-
389
-    /**
390
-     * @param string $status whether notice is open/dismissed/viewed/etc
391
-     */
392
-    protected function setStatus(string $status): void
393
-    {
394
-        if (! in_array($status, $this->valid_statuses, true)) {
395
-            throw new InvalidStatusException($status, esc_html__('AdminNotification', 'event_espresso'));
396
-        }
397
-        $this->status = $status;
398
-    }
399
-
400
-
401
-    /**
402
-     * @param string $type whether notice is an error, info, success, or warning (AdminNotification::TYPE_* constants)
403
-     */
404
-    protected function setType(string $type): void
405
-    {
406
-        if (! in_array($type, $this->valid_types, true)) {
407
-            throw new DomainException(
408
-                sprintf(
409
-                    esc_html__(
410
-                        'Invalid or missing admin notification type (%1$s). Please use one of the AdminNotification::TYPE_* constants.',
411
-                        'event_espresso'
412
-                    ),
413
-                    $type
414
-                )
415
-            );
416
-        }
417
-        $this->type = $type;
418
-        // remove existing type class from notice before adding new one
419
-        foreach ($this->valid_types as $valid_type) {
420
-            $this->removeCssClass($valid_type);
421
-        }
422
-        if ($this->type === AdminNotification::TYPE_CUSTOM) {
423
-            $this->addCssClass('espresso-admin-notice');
424
-        } else {
425
-            $this->addCssClass($type);
426
-            $this->removeCssClass('espresso-admin-notice');
427
-            $this->addCssClass('notice');
428
-        }
429
-    }
430
-
431
-
432
-    /**
433
-     * @param array $valid_statuses array of valid notification status options (AdminNotification::STATUS_* constants)
434
-     */
435
-    protected function setValidStatuses(array $valid_statuses): void
436
-    {
437
-        $this->valid_statuses = $valid_statuses;
438
-    }
439
-
440
-
441
-    /**
442
-     * @param array $valid_types array of valid notification types (AdminNotification::TYPE_* constants)
443
-     */
444
-    protected function setValidTypes(array $valid_types): void
445
-    {
446
-        $this->valid_types = $valid_types;
447
-    }
14
+	const STATUS_OPEN      = 'status-open';
15
+
16
+	const STATUS_CLOSED    = 'status-closed';
17
+
18
+	const STATUS_DISMISSED = 'status-dismissed';
19
+
20
+	const STATUS_VIEWED    = 'status-viewed';
21
+
22
+	const TYPE_CUSTOM      = 'notice-custom';
23
+
24
+	const TYPE_ERROR       = 'notice-error';
25
+
26
+	const TYPE_INFO        = 'notice-info';
27
+
28
+	const TYPE_SUCCESS     = 'notice-success';
29
+
30
+	const TYPE_WARNING     = 'notice-warning';
31
+
32
+
33
+	/**
34
+	 * @var array
35
+	 */
36
+	protected $allowed_tags = [];
37
+
38
+	/**
39
+	 * @var CapCheckInterface
40
+	 */
41
+	protected $cap_check;
42
+
43
+	/**
44
+	 * @var string
45
+	 */
46
+	protected $capability = '';
47
+
48
+	/**
49
+	 * @var string
50
+	 */
51
+	protected $cap_context = '';
52
+
53
+	/**
54
+	 * @var array
55
+	 */
56
+	protected $css_classes = [];
57
+
58
+	/**
59
+	 * @var string
60
+	 */
61
+	protected $identifier = '';
62
+
63
+	/**
64
+	 * @var bool
65
+	 */
66
+	protected $is_dismissible = false;
67
+
68
+	/**
69
+	 * @var string
70
+	 */
71
+	protected $message = '';
72
+
73
+	/**
74
+	 * @var string
75
+	 */
76
+	protected $role = '';
77
+
78
+	/**
79
+	 * @var string
80
+	 */
81
+	protected $status = '';
82
+
83
+	/**
84
+	 * @var array
85
+	 */
86
+	protected $valid_statuses = [];
87
+
88
+	/**
89
+	 * @var string
90
+	 */
91
+	protected $type;
92
+
93
+	/**
94
+	 * @var array
95
+	 */
96
+	protected $valid_types = [
97
+		AdminNotification::TYPE_CUSTOM,
98
+		AdminNotification::TYPE_ERROR,
99
+		AdminNotification::TYPE_INFO,
100
+		AdminNotification::TYPE_SUCCESS,
101
+		AdminNotification::TYPE_WARNING,
102
+	];
103
+
104
+
105
+	/**
106
+	 * PersistentAdminNotice constructor
107
+	 *
108
+	 * @param string     $identifier   [required] the name, or key of the Admin Notice
109
+	 * @param string     $message      [required] the message to be displayed to users
110
+	 * @param string     $type         whether notice is an error, info, success, or warning
111
+	 * @param bool       $autoload     whether to automatically hook into "admin_notices" to display notice
112
+	 * @param array|null $allowed_tags tags & attributes passed to wp_kses for sanitizing notice content
113
+	 */
114
+	public function __construct(
115
+		string $identifier,
116
+		string $message,
117
+		string $type = AdminNotification::TYPE_INFO,
118
+		bool $autoload = true,
119
+		?array $allowed_tags = []
120
+	) {
121
+		$this->identifier = sanitize_key($identifier);
122
+		$this->setType($type);
123
+		$this->setMessage($message);
124
+		$this->setAllowedTags($allowed_tags);
125
+		if ($autoload) {
126
+			add_action('admin_notices', [$this, 'displayNotification']);
127
+		}
128
+	}
129
+
130
+
131
+	/**
132
+	 * user capability required to view this notice
133
+	 *
134
+	 * @return string
135
+	 */
136
+	public function capability(): string
137
+	{
138
+		return $this->capability;
139
+	}
140
+
141
+
142
+	/**
143
+	 * description for why the cap check is being performed
144
+	 *
145
+	 * @return string
146
+	 */
147
+	public function capContext(): string
148
+	{
149
+		return $this->cap_context;
150
+	}
151
+
152
+
153
+	/**
154
+	 * @return array
155
+	 */
156
+	public function cssClasses(): array
157
+	{
158
+		return $this->css_classes;
159
+	}
160
+
161
+
162
+	/**
163
+	 * @return string
164
+	 */
165
+	public function cssClassString(): string
166
+	{
167
+		return implode(' ', $this->css_classes);
168
+	}
169
+
170
+
171
+	/**
172
+	 * @return CapCheckInterface
173
+	 */
174
+	public function getCapCheck(): CapCheckInterface
175
+	{
176
+		if (! $this->cap_check instanceof CapCheckInterface) {
177
+			$this->setCapCheck(
178
+				new CapCheck($this->capability, $this->cap_context)
179
+			);
180
+		}
181
+		return $this->cap_check;
182
+	}
183
+
184
+
185
+	public function displayNotification($return = false): string
186
+	{
187
+		$id           = esc_attr($this->identifier);
188
+		$class        = esc_attr($this->cssClassString());
189
+		$notification = "<div id='$id' class='$class'>$this->message</div>";
190
+		if ($return) {
191
+			return $notification;
192
+		}
193
+		echo wp_kses($notification, $this->allowed_tags);
194
+		return '';
195
+	}
196
+
197
+
198
+	/**
199
+	 * name/identifier for notice
200
+	 *
201
+	 * @return string
202
+	 */
203
+	public function identifier(): string
204
+	{
205
+		return $this->identifier;
206
+	}
207
+
208
+
209
+	/**
210
+	 * @return bool
211
+	 */
212
+	public function isDismissible(): bool
213
+	{
214
+		return $this->is_dismissible;
215
+	}
216
+
217
+
218
+	/**
219
+	 * the actual content displayed to the user
220
+	 *
221
+	 * @return string
222
+	 */
223
+	public function message(): string
224
+	{
225
+		return $this->message;
226
+	}
227
+
228
+
229
+	/**
230
+	 * notice will only displayed to users with this role
231
+	 *
232
+	 * @return string
233
+	 */
234
+	public function role(): string
235
+	{
236
+		return $this->role;
237
+	}
238
+
239
+
240
+	/**
241
+	 * whether notice is open/dismissed/viewed/etc one of the AdminNotification::STATUS_* constants
242
+	 *
243
+	 * @return string
244
+	 */
245
+	public function status(): string
246
+	{
247
+		return $this->status;
248
+	}
249
+
250
+
251
+	/**
252
+	 * array of valid notification status options (AdminNotification::STATUS_* constants)
253
+	 *
254
+	 * @return array|string[]
255
+	 */
256
+	public function validStatuses(): array
257
+	{
258
+		return $this->valid_statuses;
259
+	}
260
+
261
+
262
+	/**
263
+	 * array of valid notification types (AdminNotification::TYPE_* constants)
264
+	 *
265
+	 * @return array|string[]
266
+	 */
267
+	public function validTypes(): array
268
+	{
269
+		return $this->valid_types;
270
+	}
271
+
272
+
273
+	/**
274
+	 * @param array $allowed_tags
275
+	 */
276
+	public function setAllowedTags(array $allowed_tags = []): void
277
+	{
278
+		$this->allowed_tags = ! empty($allowed_tags) ? $allowed_tags : AllowedTags::getWithFullTags();
279
+	}
280
+
281
+
282
+	/**
283
+	 * @param string|null $capability user capability required to view this notice [default] 'manage_options'
284
+	 */
285
+	protected function setCapability(?string $capability = 'manage_options')
286
+	{
287
+		$this->capability = sanitize_key($capability);
288
+	}
289
+
290
+
291
+	/**
292
+	 * @param CapCheckInterface|null $cap_check
293
+	 */
294
+	protected function setCapCheck(?CapCheckInterface $cap_check = null)
295
+	{
296
+		$this->cap_check = $cap_check;
297
+	}
298
+
299
+
300
+	/**
301
+	 * @param string $cap_context description for why the cap check is being performed
302
+	 */
303
+	protected function setCapContext(string $cap_context = 'view admin notice')
304
+	{
305
+		$this->cap_context = sanitize_text_field($cap_context);
306
+	}
307
+
308
+
309
+	/**
310
+	 * @param string $css_class
311
+	 * @param bool   $remove_duplicates
312
+	 */
313
+	public function addCssClass(string $css_class, bool $remove_duplicates = true): void
314
+	{
315
+		$this->css_classes[] = sanitize_key($css_class);
316
+		if ($remove_duplicates) {
317
+			$this->removeDuplicateCssClasses();
318
+		}
319
+	}
320
+
321
+
322
+	/**
323
+	 * @param array $css_classes
324
+	 */
325
+	public function addCssClasses(array $css_classes): void
326
+	{
327
+		foreach ($css_classes as $css_class) {
328
+			$this->addCssClass($css_class, false);
329
+		}
330
+		$this->removeDuplicateCssClasses();
331
+	}
332
+
333
+
334
+	/**
335
+	 * @return void
336
+	 */
337
+	public function removeDuplicateCssClasses(): void
338
+	{
339
+		$this->css_classes = array_unique($this->css_classes, SORT_STRING);
340
+	}
341
+
342
+
343
+	/**
344
+	 * @param string $css_class
345
+	 */
346
+	public function removeCssClass(string $css_class): void
347
+	{
348
+		$this->css_classes = array_filter(
349
+			$this->css_classes,
350
+			function ($existing_class) use ($css_class) {
351
+				return $existing_class !== $css_class;
352
+			}
353
+		);
354
+	}
355
+
356
+
357
+	/**
358
+	 * @param bool $is_dismissible
359
+	 */
360
+	public function setIsDismissible(bool $is_dismissible = false): void
361
+	{
362
+		$this->is_dismissible = filter_var($is_dismissible, FILTER_VALIDATE_BOOLEAN);
363
+		if ($this->is_dismissible) {
364
+			$this->addCssClass('is-dismissible');
365
+		} else {
366
+			$this->removeCssClass('is-dismissible');
367
+		}
368
+	}
369
+
370
+
371
+	/**
372
+	 * @param string $message the actual content displayed to the user
373
+	 */
374
+	protected function setMessage(string $message)
375
+	{
376
+		$this->message = $message;
377
+	}
378
+
379
+
380
+	/**
381
+	 * @param string $role notice will only displayed to users with this role
382
+	 */
383
+	protected function setRole(string $role): void
384
+	{
385
+		$this->role = $role;
386
+	}
387
+
388
+
389
+	/**
390
+	 * @param string $status whether notice is open/dismissed/viewed/etc
391
+	 */
392
+	protected function setStatus(string $status): void
393
+	{
394
+		if (! in_array($status, $this->valid_statuses, true)) {
395
+			throw new InvalidStatusException($status, esc_html__('AdminNotification', 'event_espresso'));
396
+		}
397
+		$this->status = $status;
398
+	}
399
+
400
+
401
+	/**
402
+	 * @param string $type whether notice is an error, info, success, or warning (AdminNotification::TYPE_* constants)
403
+	 */
404
+	protected function setType(string $type): void
405
+	{
406
+		if (! in_array($type, $this->valid_types, true)) {
407
+			throw new DomainException(
408
+				sprintf(
409
+					esc_html__(
410
+						'Invalid or missing admin notification type (%1$s). Please use one of the AdminNotification::TYPE_* constants.',
411
+						'event_espresso'
412
+					),
413
+					$type
414
+				)
415
+			);
416
+		}
417
+		$this->type = $type;
418
+		// remove existing type class from notice before adding new one
419
+		foreach ($this->valid_types as $valid_type) {
420
+			$this->removeCssClass($valid_type);
421
+		}
422
+		if ($this->type === AdminNotification::TYPE_CUSTOM) {
423
+			$this->addCssClass('espresso-admin-notice');
424
+		} else {
425
+			$this->addCssClass($type);
426
+			$this->removeCssClass('espresso-admin-notice');
427
+			$this->addCssClass('notice');
428
+		}
429
+	}
430
+
431
+
432
+	/**
433
+	 * @param array $valid_statuses array of valid notification status options (AdminNotification::STATUS_* constants)
434
+	 */
435
+	protected function setValidStatuses(array $valid_statuses): void
436
+	{
437
+		$this->valid_statuses = $valid_statuses;
438
+	}
439
+
440
+
441
+	/**
442
+	 * @param array $valid_types array of valid notification types (AdminNotification::TYPE_* constants)
443
+	 */
444
+	protected function setValidTypes(array $valid_types): void
445
+	{
446
+		$this->valid_types = $valid_types;
447
+	}
448 448
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
      */
174 174
     public function getCapCheck(): CapCheckInterface
175 175
     {
176
-        if (! $this->cap_check instanceof CapCheckInterface) {
176
+        if ( ! $this->cap_check instanceof CapCheckInterface) {
177 177
             $this->setCapCheck(
178 178
                 new CapCheck($this->capability, $this->cap_context)
179 179
             );
@@ -347,7 +347,7 @@  discard block
 block discarded – undo
347 347
     {
348 348
         $this->css_classes = array_filter(
349 349
             $this->css_classes,
350
-            function ($existing_class) use ($css_class) {
350
+            function($existing_class) use ($css_class) {
351 351
                 return $existing_class !== $css_class;
352 352
             }
353 353
         );
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
      */
392 392
     protected function setStatus(string $status): void
393 393
     {
394
-        if (! in_array($status, $this->valid_statuses, true)) {
394
+        if ( ! in_array($status, $this->valid_statuses, true)) {
395 395
             throw new InvalidStatusException($status, esc_html__('AdminNotification', 'event_espresso'));
396 396
         }
397 397
         $this->status = $status;
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
      */
404 404
     protected function setType(string $type): void
405 405
     {
406
-        if (! in_array($type, $this->valid_types, true)) {
406
+        if ( ! in_array($type, $this->valid_types, true)) {
407 407
             throw new DomainException(
408 408
                 sprintf(
409 409
                     esc_html__(
Please login to merge, or discard this patch.
core/services/activation/plugin_prompt/DownloadPluginPrompt.php 2 patches
Indentation   +110 added lines, -110 removed lines patch added patch discarded remove patch
@@ -6,95 +6,95 @@  discard block
 block discarded – undo
6 6
 
7 7
 class DownloadPluginPrompt extends AdminNotification
8 8
 {
9
-    /**
10
-     * @var string
11
-     */
12
-    protected $dependency = '';
13
-
14
-    /**
15
-     * @var string
16
-     */
17
-    protected $heading = '';
18
-
19
-    /**
20
-     * @var string
21
-     */
22
-    protected $image = '';
23
-
24
-    /**
25
-     * @var int
26
-     */
27
-    protected $image_size = 0;
28
-
29
-    /**
30
-     * @var string
31
-     */
32
-    protected $message = '';
33
-
34
-    /**
35
-     * @var string
36
-     */
37
-    protected $plugin_name = '';
38
-
39
-    /**
40
-     * @var string
41
-     */
42
-    protected $plugin_url = '';
43
-
44
-
45
-    /**
46
-     * @param string      $plugin_name [required] the name, or key of the Persistent Admin Notice to be stored
47
-     * @param string      $plugin_url  [required] the message to be stored persistently until dismissed
48
-     * @param string      $dependency
49
-     * @param string|null $message
50
-     * @param string|null $image
51
-     * @param int|null    $image_size
52
-     */
53
-    public function __construct(
54
-        string $plugin_name,
55
-        string $plugin_url,
56
-        string $dependency,
57
-        ?string $heading = '',
58
-        ?string $message = '',
59
-        ?string $image = '',
60
-        ?int $image_size = 480
61
-    ) {
62
-        $this->plugin_name = $plugin_name;
63
-        $this->plugin_url  = $plugin_url;
64
-        $this->dependency  = $dependency;
65
-        $this->heading     = $heading;
66
-        $this->message     = $message;
67
-        $this->image       = $image;
68
-        $this->image_size  = absint($image_size) ?? 480;
69
-        $identifier        = "$plugin_name-download-plugin-prompt";
70
-        parent::__construct($identifier, $this->content(), AdminNotification::TYPE_CUSTOM, false);
71
-        $this->addCssClass("ee-download-plugin-prompt");
72
-        $this->setIsDismissible(true);
73
-        $this->setCapability('activate_plugins');
74
-        $this->setCapContext('view download plugin prompts');
75
-    }
76
-
77
-
78
-    private function content(): string
79
-    {
80
-        $heading = $this->heading ?? esc_html__("Don't miss out on exciting new features!", 'event_espresso');
81
-        $message = $this->message ?? sprintf(
82
-        /* translators: 'Some Feature' needs the 'Plugin Name' plugin in order provide your site with the maximum functionality it can offer. */
83
-            esc_html__(
84
-                '%1$s needs the %2$s plugin in order provide your site with the maximum functionality it can offer.',
85
-                'event_espresso'
86
-            ),
87
-            // 'Some Feature'
88
-            $this->dependency,
89
-            // 'Plugin Name & Link'
90
-            "<a href='$this->plugin_url' target='_blank'>$this->plugin_name</a>"
91
-        );
92
-        $button  = sprintf(
93
-        /* translators: 'Download and Activate Plugin Name */
94
-            esc_html__('Download and Activate %1$s', 'event_espresso'),
95
-            $this->plugin_name
96
-        );
97
-        return "
9
+	/**
10
+	 * @var string
11
+	 */
12
+	protected $dependency = '';
13
+
14
+	/**
15
+	 * @var string
16
+	 */
17
+	protected $heading = '';
18
+
19
+	/**
20
+	 * @var string
21
+	 */
22
+	protected $image = '';
23
+
24
+	/**
25
+	 * @var int
26
+	 */
27
+	protected $image_size = 0;
28
+
29
+	/**
30
+	 * @var string
31
+	 */
32
+	protected $message = '';
33
+
34
+	/**
35
+	 * @var string
36
+	 */
37
+	protected $plugin_name = '';
38
+
39
+	/**
40
+	 * @var string
41
+	 */
42
+	protected $plugin_url = '';
43
+
44
+
45
+	/**
46
+	 * @param string      $plugin_name [required] the name, or key of the Persistent Admin Notice to be stored
47
+	 * @param string      $plugin_url  [required] the message to be stored persistently until dismissed
48
+	 * @param string      $dependency
49
+	 * @param string|null $message
50
+	 * @param string|null $image
51
+	 * @param int|null    $image_size
52
+	 */
53
+	public function __construct(
54
+		string $plugin_name,
55
+		string $plugin_url,
56
+		string $dependency,
57
+		?string $heading = '',
58
+		?string $message = '',
59
+		?string $image = '',
60
+		?int $image_size = 480
61
+	) {
62
+		$this->plugin_name = $plugin_name;
63
+		$this->plugin_url  = $plugin_url;
64
+		$this->dependency  = $dependency;
65
+		$this->heading     = $heading;
66
+		$this->message     = $message;
67
+		$this->image       = $image;
68
+		$this->image_size  = absint($image_size) ?? 480;
69
+		$identifier        = "$plugin_name-download-plugin-prompt";
70
+		parent::__construct($identifier, $this->content(), AdminNotification::TYPE_CUSTOM, false);
71
+		$this->addCssClass("ee-download-plugin-prompt");
72
+		$this->setIsDismissible(true);
73
+		$this->setCapability('activate_plugins');
74
+		$this->setCapContext('view download plugin prompts');
75
+	}
76
+
77
+
78
+	private function content(): string
79
+	{
80
+		$heading = $this->heading ?? esc_html__("Don't miss out on exciting new features!", 'event_espresso');
81
+		$message = $this->message ?? sprintf(
82
+		/* translators: 'Some Feature' needs the 'Plugin Name' plugin in order provide your site with the maximum functionality it can offer. */
83
+			esc_html__(
84
+				'%1$s needs the %2$s plugin in order provide your site with the maximum functionality it can offer.',
85
+				'event_espresso'
86
+			),
87
+			// 'Some Feature'
88
+			$this->dependency,
89
+			// 'Plugin Name & Link'
90
+			"<a href='$this->plugin_url' target='_blank'>$this->plugin_name</a>"
91
+		);
92
+		$button  = sprintf(
93
+		/* translators: 'Download and Activate Plugin Name */
94
+			esc_html__('Download and Activate %1$s', 'event_espresso'),
95
+			$this->plugin_name
96
+		);
97
+		return "
98 98
         <div class='ee-download-plugin-prompt__content'>
99 99
             <div class='ee-download-plugin-prompt__message'>
100 100
                 <span class='dashicons dashicons-admin-plugins'></span>
@@ -110,29 +110,29 @@  discard block
 block discarded – undo
110 110
             </div>
111 111
             {$this->image()}
112 112
         </div>";
113
-    }
113
+	}
114 114
 
115 115
 
116
-    private function image(): string
117
-    {
118
-        $image = $this->findImage();
119
-        $alt   = esc_html__("download plugin prompt", 'event_espresso');
120
-        return $image ?
121
-            "<div class='ee-download-plugin-prompt__image'>
116
+	private function image(): string
117
+	{
118
+		$image = $this->findImage();
119
+		$alt   = esc_html__("download plugin prompt", 'event_espresso');
120
+		return $image ?
121
+			"<div class='ee-download-plugin-prompt__image'>
122 122
                 <img src='{$image}' alt='$alt' width='{$this->image_size}px'/>
123 123
             </div>"
124
-            : '';
125
-    }
126
-
127
-
128
-    private function findImage(): string
129
-    {
130
-        // if image is already a URL then just return it
131
-        if (strpos($this->image, 'http') === 0) {
132
-            return $this->image;
133
-        }
134
-        // otherwise look in /global_assets/images/download_plugin_prompt/
135
-        $file_path = EE_TEMPLATES . "global_assets/images/download_plugin_prompt/{$this->image}";
136
-        return file_exists($file_path) ? EE_GLOBAL_ASSETS_URL . "images/download_plugin_prompt/{$this->image}" : '';
137
-    }
124
+			: '';
125
+	}
126
+
127
+
128
+	private function findImage(): string
129
+	{
130
+		// if image is already a URL then just return it
131
+		if (strpos($this->image, 'http') === 0) {
132
+			return $this->image;
133
+		}
134
+		// otherwise look in /global_assets/images/download_plugin_prompt/
135
+		$file_path = EE_TEMPLATES . "global_assets/images/download_plugin_prompt/{$this->image}";
136
+		return file_exists($file_path) ? EE_GLOBAL_ASSETS_URL . "images/download_plugin_prompt/{$this->image}" : '';
137
+	}
138 138
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -89,7 +89,7 @@  discard block
 block discarded – undo
89 89
             // 'Plugin Name & Link'
90 90
             "<a href='$this->plugin_url' target='_blank'>$this->plugin_name</a>"
91 91
         );
92
-        $button  = sprintf(
92
+        $button = sprintf(
93 93
         /* translators: 'Download and Activate Plugin Name */
94 94
             esc_html__('Download and Activate %1$s', 'event_espresso'),
95 95
             $this->plugin_name
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
             return $this->image;
133 133
         }
134 134
         // otherwise look in /global_assets/images/download_plugin_prompt/
135
-        $file_path = EE_TEMPLATES . "global_assets/images/download_plugin_prompt/{$this->image}";
135
+        $file_path = EE_TEMPLATES."global_assets/images/download_plugin_prompt/{$this->image}";
136 136
         return file_exists($file_path) ? EE_GLOBAL_ASSETS_URL . "images/download_plugin_prompt/{$this->image}" : '';
137 137
     }
138 138
 }
Please login to merge, or discard this patch.
core/EE_Dependency_Map.core.php 1 patch
Indentation   +1172 added lines, -1172 removed lines patch added patch discarded remove patch
@@ -19,1176 +19,1176 @@
 block discarded – undo
19 19
  */
20 20
 class EE_Dependency_Map
21 21
 {
22
-    /**
23
-     * This means that the requested class dependency is not present in the dependency map
24
-     */
25
-    const not_registered = 0;
26
-
27
-    /**
28
-     * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
29
-     */
30
-    const load_new_object = 1;
31
-
32
-    /**
33
-     * This instructs class loaders to return a previously instantiated and cached object for the requested class.
34
-     * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
35
-     */
36
-    const load_from_cache = 2;
37
-
38
-    /**
39
-     * When registering a dependency,
40
-     * this indicates to keep any existing dependencies that already exist,
41
-     * and simply discard any new dependencies declared in the incoming data
42
-     */
43
-    const KEEP_EXISTING_DEPENDENCIES = 0;
44
-
45
-    /**
46
-     * When registering a dependency,
47
-     * this indicates to overwrite any existing dependencies that already exist using the incoming data
48
-     */
49
-    const OVERWRITE_DEPENDENCIES = 1;
50
-
51
-
52
-    /**
53
-     * @type EE_Dependency_Map $_instance
54
-     */
55
-    protected static $_instance;
56
-
57
-    /**
58
-     * @var ClassInterfaceCache $class_cache
59
-     */
60
-    private $class_cache;
61
-
62
-    /**
63
-     * @type RequestInterface $request
64
-     */
65
-    protected $request;
66
-
67
-    /**
68
-     * @type LegacyRequestInterface $legacy_request
69
-     */
70
-    protected $legacy_request;
71
-
72
-    /**
73
-     * @type ResponseInterface $response
74
-     */
75
-    protected $response;
76
-
77
-    /**
78
-     * @type LoaderInterface $loader
79
-     */
80
-    protected $loader;
81
-
82
-    /**
83
-     * @type array $_dependency_map
84
-     */
85
-    protected $_dependency_map = [];
86
-
87
-    /**
88
-     * @type array $_class_loaders
89
-     */
90
-    protected $_class_loaders = [];
91
-
92
-
93
-    /**
94
-     * EE_Dependency_Map constructor.
95
-     *
96
-     * @param ClassInterfaceCache $class_cache
97
-     */
98
-    protected function __construct(ClassInterfaceCache $class_cache)
99
-    {
100
-        $this->class_cache = $class_cache;
101
-        do_action('EE_Dependency_Map____construct', $this);
102
-    }
103
-
104
-
105
-    /**
106
-     * @return void
107
-     */
108
-    public function initialize()
109
-    {
110
-        $this->_register_core_dependencies();
111
-        $this->_register_core_class_loaders();
112
-        $this->_register_core_aliases();
113
-    }
114
-
115
-
116
-    /**
117
-     * @singleton method used to instantiate class object
118
-     * @param ClassInterfaceCache|null $class_cache
119
-     * @return EE_Dependency_Map
120
-     */
121
-    public static function instance(ClassInterfaceCache $class_cache = null)
122
-    {
123
-        // check if class object is instantiated, and instantiated properly
124
-        if (
125
-            ! self::$_instance instanceof EE_Dependency_Map
126
-            && $class_cache instanceof ClassInterfaceCache
127
-        ) {
128
-            self::$_instance = new EE_Dependency_Map($class_cache);
129
-        }
130
-        return self::$_instance;
131
-    }
132
-
133
-
134
-    /**
135
-     * @param RequestInterface $request
136
-     */
137
-    public function setRequest(RequestInterface $request)
138
-    {
139
-        $this->request = $request;
140
-    }
141
-
142
-
143
-    /**
144
-     * @param LegacyRequestInterface $legacy_request
145
-     */
146
-    public function setLegacyRequest(LegacyRequestInterface $legacy_request)
147
-    {
148
-        $this->legacy_request = $legacy_request;
149
-    }
150
-
151
-
152
-    /**
153
-     * @param ResponseInterface $response
154
-     */
155
-    public function setResponse(ResponseInterface $response)
156
-    {
157
-        $this->response = $response;
158
-    }
159
-
160
-
161
-    /**
162
-     * @param LoaderInterface $loader
163
-     */
164
-    public function setLoader(LoaderInterface $loader)
165
-    {
166
-        $this->loader = $loader;
167
-    }
168
-
169
-
170
-    /**
171
-     * @param string $class
172
-     * @param array  $dependencies
173
-     * @param int    $overwrite
174
-     * @return bool
175
-     */
176
-    public static function register_dependencies(
177
-        $class,
178
-        array $dependencies,
179
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
180
-    ) {
181
-        return self::$_instance->registerDependencies($class, $dependencies, $overwrite);
182
-    }
183
-
184
-
185
-    /**
186
-     * Assigns an array of class names and corresponding load sources (new or cached)
187
-     * to the class specified by the first parameter.
188
-     * IMPORTANT !!!
189
-     * The order of elements in the incoming $dependencies array MUST match
190
-     * the order of the constructor parameters for the class in question.
191
-     * This is especially important when overriding any existing dependencies that are registered.
192
-     * the third parameter controls whether any duplicate dependencies are overwritten or not.
193
-     *
194
-     * @param string $class
195
-     * @param array  $dependencies
196
-     * @param int    $overwrite
197
-     * @return bool
198
-     */
199
-    public function registerDependencies(
200
-        $class,
201
-        array $dependencies,
202
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
203
-    ) {
204
-        $class      = trim($class, '\\');
205
-        $registered = false;
206
-        if (empty(self::$_instance->_dependency_map[ $class ])) {
207
-            self::$_instance->_dependency_map[ $class ] = [];
208
-        }
209
-        // we need to make sure that any aliases used when registering a dependency
210
-        // get resolved to the correct class name
211
-        foreach ($dependencies as $dependency => $load_source) {
212
-            $alias = self::$_instance->getFqnForAlias($dependency);
213
-            if (
214
-                $overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
215
-                || ! isset(self::$_instance->_dependency_map[ $class ][ $alias ])
216
-            ) {
217
-                unset($dependencies[ $dependency ]);
218
-                $dependencies[ $alias ] = $load_source;
219
-                $registered             = true;
220
-            }
221
-        }
222
-        // now add our two lists of dependencies together.
223
-        // using Union (+=) favours the arrays in precedence from left to right,
224
-        // so $dependencies is NOT overwritten because it is listed first
225
-        // ie: with A = B + C, entries in B take precedence over duplicate entries in C
226
-        // Union is way faster than array_merge() but should be used with caution...
227
-        // especially with numerically indexed arrays
228
-        $dependencies += self::$_instance->_dependency_map[ $class ];
229
-        // now we need to ensure that the resulting dependencies
230
-        // array only has the entries that are required for the class
231
-        // so first count how many dependencies were originally registered for the class
232
-        $dependency_count = count(self::$_instance->_dependency_map[ $class ]);
233
-        // if that count is non-zero (meaning dependencies were already registered)
234
-        self::$_instance->_dependency_map[ $class ] = $dependency_count
235
-            // then truncate the  final array to match that count
236
-            ? array_slice($dependencies, 0, $dependency_count)
237
-            // otherwise just take the incoming array because nothing previously existed
238
-            : $dependencies;
239
-        return $registered;
240
-    }
241
-
242
-
243
-    /**
244
-     * @param string $class_name
245
-     * @param string $loader
246
-     * @param bool   $overwrite
247
-     * @return bool
248
-     * @throws DomainException
249
-     */
250
-    public static function register_class_loader($class_name, $loader = 'load_core', $overwrite = false)
251
-    {
252
-        if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
253
-            throw new DomainException(
254
-                esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
255
-            );
256
-        }
257
-        // check that loader is callable or method starts with "load_" and exists in EE_Registry
258
-        if (
259
-            ! is_callable($loader)
260
-            && (
261
-                strpos($loader, 'load_') !== 0
262
-                || ! method_exists('EE_Registry', $loader)
263
-            )
264
-        ) {
265
-            throw new DomainException(
266
-                sprintf(
267
-                    esc_html__(
268
-                        '"%1$s" is not a valid loader method on EE_Registry.',
269
-                        'event_espresso'
270
-                    ),
271
-                    $loader
272
-                )
273
-            );
274
-        }
275
-        $class_name = self::$_instance->getFqnForAlias($class_name);
276
-        if ($overwrite || ! isset(self::$_instance->_class_loaders[ $class_name ])) {
277
-            self::$_instance->_class_loaders[ $class_name ] = $loader;
278
-            return true;
279
-        }
280
-        return false;
281
-    }
282
-
283
-
284
-    /**
285
-     * @return array
286
-     */
287
-    public function dependency_map()
288
-    {
289
-        return $this->_dependency_map;
290
-    }
291
-
292
-
293
-    /**
294
-     * returns TRUE if dependency map contains a listing for the provided class name
295
-     *
296
-     * @param string $class_name
297
-     * @return boolean
298
-     */
299
-    public function has($class_name = '')
300
-    {
301
-        // all legacy models have the same dependencies
302
-        if (strpos($class_name, 'EEM_') === 0) {
303
-            $class_name = 'LEGACY_MODELS';
304
-        }
305
-        return isset($this->_dependency_map[ $class_name ]);
306
-    }
307
-
308
-
309
-    /**
310
-     * returns TRUE if dependency map contains a listing for the provided class name AND dependency
311
-     *
312
-     * @param string $class_name
313
-     * @param string $dependency
314
-     * @return bool
315
-     */
316
-    public function has_dependency_for_class($class_name = '', $dependency = '')
317
-    {
318
-        // all legacy models have the same dependencies
319
-        if (strpos($class_name, 'EEM_') === 0) {
320
-            $class_name = 'LEGACY_MODELS';
321
-        }
322
-        $dependency = $this->getFqnForAlias($dependency, $class_name);
323
-        return isset($this->_dependency_map[ $class_name ][ $dependency ]);
324
-    }
325
-
326
-
327
-    /**
328
-     * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
329
-     *
330
-     * @param string $class_name
331
-     * @param string $dependency
332
-     * @return int
333
-     */
334
-    public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
335
-    {
336
-        // all legacy models have the same dependencies
337
-        if (strpos($class_name, 'EEM_') === 0) {
338
-            $class_name = 'LEGACY_MODELS';
339
-        }
340
-        $dependency = $this->getFqnForAlias($dependency);
341
-        return $this->has_dependency_for_class($class_name, $dependency)
342
-            ? $this->_dependency_map[ $class_name ][ $dependency ]
343
-            : EE_Dependency_Map::not_registered;
344
-    }
345
-
346
-
347
-    /**
348
-     * @param string $class_name
349
-     * @return string | Closure
350
-     */
351
-    public function class_loader($class_name)
352
-    {
353
-        // all legacy models use load_model()
354
-        if (strpos($class_name, 'EEM_') === 0) {
355
-            return 'load_model';
356
-        }
357
-        // EE_CPT_*_Strategy classes like EE_CPT_Event_Strategy, EE_CPT_Venue_Strategy, etc
358
-        // perform strpos() first to avoid loading regex every time we load a class
359
-        if (
360
-            strpos($class_name, 'EE_CPT_') === 0
361
-            && preg_match('/^EE_CPT_([a-zA-Z]+)_Strategy$/', $class_name)
362
-        ) {
363
-            return 'load_core';
364
-        }
365
-        $class_name = $this->getFqnForAlias($class_name);
366
-        return isset($this->_class_loaders[ $class_name ]) ? $this->_class_loaders[ $class_name ] : '';
367
-    }
368
-
369
-
370
-    /**
371
-     * @return array
372
-     */
373
-    public function class_loaders()
374
-    {
375
-        return $this->_class_loaders;
376
-    }
377
-
378
-
379
-    /**
380
-     * adds an alias for a classname
381
-     *
382
-     * @param string $fqcn      the class name that should be used (concrete class to replace interface)
383
-     * @param string $alias     the class name that would be type hinted for (abstract parent or interface)
384
-     * @param string $for_class the class that has the dependency (is type hinting for the interface)
385
-     */
386
-    public function add_alias($fqcn, $alias, $for_class = '')
387
-    {
388
-        $this->class_cache->addAlias($fqcn, $alias, $for_class);
389
-    }
390
-
391
-
392
-    /**
393
-     * Returns TRUE if the provided fully qualified name IS an alias
394
-     * WHY?
395
-     * Because if a class is type hinting for a concretion,
396
-     * then why would we need to find another class to supply it?
397
-     * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
398
-     * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
399
-     * Don't go looking for some substitute.
400
-     * Whereas if a class is type hinting for an interface...
401
-     * then we need to find an actual class to use.
402
-     * So the interface IS the alias for some other FQN,
403
-     * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
404
-     * represents some other class.
405
-     *
406
-     * @param string $fqn
407
-     * @param string $for_class
408
-     * @return bool
409
-     */
410
-    public function isAlias($fqn = '', $for_class = '')
411
-    {
412
-        return $this->class_cache->isAlias($fqn, $for_class);
413
-    }
414
-
415
-
416
-    /**
417
-     * Returns a FQN for provided alias if one exists, otherwise returns the original $alias
418
-     * functions recursively, so that multiple aliases can be used to drill down to a FQN
419
-     *  for example:
420
-     *      if the following two entries were added to the _aliases array:
421
-     *          array(
422
-     *              'interface_alias'           => 'some\namespace\interface'
423
-     *              'some\namespace\interface'  => 'some\namespace\classname'
424
-     *          )
425
-     *      then one could use EE_Registry::instance()->create( 'interface_alias' )
426
-     *      to load an instance of 'some\namespace\classname'
427
-     *
428
-     * @param string $alias
429
-     * @param string $for_class
430
-     * @return string
431
-     */
432
-    public function getFqnForAlias($alias = '', $for_class = '')
433
-    {
434
-        return $this->class_cache->getFqnForAlias($alias, $for_class);
435
-    }
436
-
437
-
438
-    /**
439
-     * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
440
-     * if one exists, or whether a new object should be generated every time the requested class is loaded.
441
-     * This is done by using the following class constants:
442
-     *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
443
-     *        EE_Dependency_Map::load_new_object - generates a new object every time
444
-     */
445
-    protected function _register_core_dependencies()
446
-    {
447
-        $this->_dependency_map = [
448
-            'EE_Admin'                                                                                          => [
449
-                'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
450
-            ],
451
-            'EE_Request_Handler'                                                                                          => [
452
-                'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
453
-                'EventEspresso\core\services\request\Response'    => EE_Dependency_Map::load_from_cache,
454
-            ],
455
-            'EE_System'                                                                                                   => [
456
-                'EE_Registry'                                 => EE_Dependency_Map::load_from_cache,
457
-                'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
458
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
459
-                'EE_Maintenance_Mode'                         => EE_Dependency_Map::load_from_cache,
460
-            ],
461
-            'EE_Session'                                                                                                  => [
462
-                'EventEspresso\core\services\cache\TransientCacheStorage'  => EE_Dependency_Map::load_from_cache,
463
-                'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
464
-                'EventEspresso\core\services\request\Request'              => EE_Dependency_Map::load_from_cache,
465
-                'EventEspresso\core\services\session\SessionStartHandler'  => EE_Dependency_Map::load_from_cache,
466
-                'EE_Encryption'                                            => EE_Dependency_Map::load_from_cache,
467
-            ],
468
-            'EE_Cart'                                                                                                     => [
469
-                'EE_Session' => EE_Dependency_Map::load_from_cache,
470
-            ],
471
-            'EE_Front_Controller'                                                                                         => [
472
-                'EE_Registry'                                     => EE_Dependency_Map::load_from_cache,
473
-                'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
474
-                'EE_Module_Request_Router'                        => EE_Dependency_Map::load_from_cache,
475
-            ],
476
-            'EE_Messenger_Collection_Loader'                                                                              => [
477
-                'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
478
-            ],
479
-            'EE_Message_Type_Collection_Loader'                                                                           => [
480
-                'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
481
-            ],
482
-            'EE_Message_Resource_Manager'                                                                                 => [
483
-                'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
484
-                'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
485
-                'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
486
-            ],
487
-            'EE_Message_Factory'                                                                                          => [
488
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
489
-            ],
490
-            'EE_messages'                                                                                                 => [
491
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
492
-            ],
493
-            'EE_Messages_Generator'                                                                                       => [
494
-                'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
495
-                'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
496
-                'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
497
-                'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
498
-            ],
499
-            'EE_Messages_Processor'                                                                                       => [
500
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
501
-            ],
502
-            'EE_Messages_Queue'                                                                                           => [
503
-                'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
504
-            ],
505
-            'EE_Messages_Template_Defaults'                                                                               => [
506
-                'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
507
-                'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
508
-            ],
509
-            'EE_Message_To_Generate_From_Request'                                                                         => [
510
-                'EE_Message_Resource_Manager'                 => EE_Dependency_Map::load_from_cache,
511
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
512
-            ],
513
-            'EventEspresso\core\services\commands\CommandBus'                                                             => [
514
-                'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
515
-            ],
516
-            'EventEspresso\services\commands\CommandHandler'                                                              => [
517
-                'EE_Registry'         => EE_Dependency_Map::load_from_cache,
518
-                'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
519
-            ],
520
-            'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => [
521
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
522
-            ],
523
-            'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => [
524
-                'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
525
-                'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
526
-            ],
527
-            'EventEspresso\core\services\commands\CommandFactory'                                                         => [
528
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
529
-            ],
530
-            'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => [
531
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
532
-            ],
533
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => [
534
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
535
-            ],
536
-            'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => [
537
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
538
-            ],
539
-            'EventEspresso\core\domain\services\commands\registration\CreateRegistrationCommandHandler'                          => [
540
-                'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
541
-            ],
542
-            'EventEspresso\core\domain\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => [
543
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
544
-            ],
545
-            'EventEspresso\core\domain\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => [
546
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
547
-            ],
548
-            'EventEspresso\core\domain\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => [
549
-                'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
550
-            ],
551
-            'EventEspresso\core\domain\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => [
552
-                'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
553
-            ],
554
-            'EventEspresso\core\domain\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => [
555
-                'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
556
-            ],
557
-            'EventEspresso\core\domain\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => [
558
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
559
-            ],
560
-            'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => [
561
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
562
-            ],
563
-            'EventEspresso\core\domain\services\commands\attendee\CreateAttendeeCommandHandler'                                  => [
564
-                'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
565
-            ],
566
-            'EventEspresso\core\services\database\TableManager'                                                           => [
567
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
568
-            ],
569
-            'EE_Data_Migration_Class_Base'                                                                                => [
570
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
571
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
572
-            ],
573
-            'EE_DMS_Core_4_1_0'                                                                                           => [
574
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
575
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
576
-            ],
577
-            'EE_DMS_Core_4_2_0'                                                                                           => [
578
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
579
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
580
-            ],
581
-            'EE_DMS_Core_4_3_0'                                                                                           => [
582
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
583
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
584
-            ],
585
-            'EE_DMS_Core_4_4_0'                                                                                           => [
586
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
587
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
588
-            ],
589
-            'EE_DMS_Core_4_5_0'                                                                                           => [
590
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
591
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
592
-            ],
593
-            'EE_DMS_Core_4_6_0'                                                                                           => [
594
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
595
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
596
-            ],
597
-            'EE_DMS_Core_4_7_0'                                                                                           => [
598
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
599
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
600
-            ],
601
-            'EE_DMS_Core_4_8_0'                                                                                           => [
602
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
603
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
604
-            ],
605
-            'EE_DMS_Core_4_9_0'                                                                                           => [
606
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
607
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
608
-            ],
609
-            'EE_DMS_Core_4_10_0'                                                                                          => [
610
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
611
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
612
-                'EE_DMS_Core_4_9_0'                                  => EE_Dependency_Map::load_from_cache,
613
-            ],
614
-            'EventEspresso\core\services\assets\I18nRegistry'                                                             => [
615
-                'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
616
-            ],
617
-            'EventEspresso\core\services\assets\Registry'                                                                 => [
618
-                'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
619
-                'EventEspresso\core\services\assets\I18nRegistry'    => EE_Dependency_Map::load_from_cache,
620
-            ],
621
-            'EventEspresso\core\domain\entities\shortcodes\EspressoCancelled'                                             => [
622
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
623
-            ],
624
-            'EventEspresso\core\domain\entities\shortcodes\EspressoCheckout'                                              => [
625
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
626
-            ],
627
-            'EventEspresso\core\domain\entities\shortcodes\EspressoEventAttendees'                                        => [
628
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
629
-            ],
630
-            'EventEspresso\core\domain\entities\shortcodes\EspressoEvents'                                                => [
631
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
632
-            ],
633
-            'EventEspresso\core\domain\entities\shortcodes\EspressoThankYou'                                              => [
634
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
635
-            ],
636
-            'EventEspresso\core\domain\entities\shortcodes\EspressoTicketSelector'                                        => [
637
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
638
-            ],
639
-            'EventEspresso\core\domain\entities\shortcodes\EspressoTxnPage'                                               => [
640
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
641
-            ],
642
-            'EventEspresso\core\services\cache\BasicCacheManager'                                                         => [
643
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
644
-            ],
645
-            'EventEspresso\core\services\cache\PostRelatedCacheManager'                                                   => [
646
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
647
-            ],
648
-            'EventEspresso\core\domain\services\validation\email\EmailValidationService'                                  => [
649
-                'EE_Registration_Config'                     => EE_Dependency_Map::load_from_cache,
650
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
651
-            ],
652
-            'EventEspresso\core\domain\values\EmailAddress'                                                               => [
653
-                null,
654
-                'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
655
-            ],
656
-            'EventEspresso\core\services\orm\ModelFieldFactory'                                                           => [
657
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
658
-            ],
659
-            'LEGACY_MODELS'                                                                                               => [
660
-                null,
661
-                'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
662
-            ],
663
-            'EE_Module_Request_Router'                                                                                    => [
664
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
665
-            ],
666
-            'EE_Registration_Processor'                                                                                   => [
667
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
668
-            ],
669
-            'EventEspresso\core\services\notifications\PersistentAdminNoticeManager'                                      => [
670
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
671
-                'EventEspresso\core\services\request\Request'                         => EE_Dependency_Map::load_from_cache,
672
-            ],
673
-            'EventEspresso\core\services\licensing\LicenseService'                                                        => [
674
-                'EventEspresso\core\domain\services\pue\Stats'  => EE_Dependency_Map::load_from_cache,
675
-                'EventEspresso\core\domain\services\pue\Config' => EE_Dependency_Map::load_from_cache,
676
-            ],
677
-            'EE_Admin_Transactions_List_Table'                                                                            => [
678
-                null,
679
-                'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
680
-            ],
681
-            'EventEspresso\core\domain\services\pue\Stats'                                                                => [
682
-                'EventEspresso\core\domain\services\pue\Config'        => EE_Dependency_Map::load_from_cache,
683
-                'EE_Maintenance_Mode'                                  => EE_Dependency_Map::load_from_cache,
684
-                'EventEspresso\core\domain\services\pue\StatsGatherer' => EE_Dependency_Map::load_from_cache,
685
-            ],
686
-            'EventEspresso\core\domain\services\pue\Config'                                                               => [
687
-                'EE_Network_Config' => EE_Dependency_Map::load_from_cache,
688
-                'EE_Config'         => EE_Dependency_Map::load_from_cache,
689
-            ],
690
-            'EventEspresso\core\domain\services\pue\StatsGatherer'                                                        => [
691
-                'EEM_Payment_Method' => EE_Dependency_Map::load_from_cache,
692
-                'EEM_Event'          => EE_Dependency_Map::load_from_cache,
693
-                'EEM_Datetime'       => EE_Dependency_Map::load_from_cache,
694
-                'EEM_Ticket'         => EE_Dependency_Map::load_from_cache,
695
-                'EEM_Registration'   => EE_Dependency_Map::load_from_cache,
696
-                'EEM_Transaction'    => EE_Dependency_Map::load_from_cache,
697
-                'EE_Config'          => EE_Dependency_Map::load_from_cache,
698
-            ],
699
-            'EventEspresso\core\domain\services\admin\ExitModal'                                                          => [
700
-                'EventEspresso\core\services\assets\Registry' => EE_Dependency_Map::load_from_cache,
701
-            ],
702
-            'EventEspresso\core\domain\services\admin\PluginUpsells'                                                      => [
703
-                'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
704
-            ],
705
-            'EventEspresso\caffeinated\modules\recaptcha_invisible\InvisibleRecaptcha'                                    => [
706
-                'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
707
-                'EE_Session'             => EE_Dependency_Map::load_from_cache,
708
-            ],
709
-            'EventEspresso\caffeinated\modules\recaptcha_invisible\RecaptchaAdminSettings'                                => [
710
-                'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
711
-            ],
712
-            'EventEspresso\modules\ticket_selector\DisplayTicketSelector' => [
713
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
714
-                'EE_Ticket_Selector_Config'                   => EE_Dependency_Map::load_from_cache,
715
-            ],
716
-            'EventEspresso\modules\ticket_selector\ProcessTicketSelector'                                                 => [
717
-                'EE_Core_Config'                                                          => EE_Dependency_Map::load_from_cache,
718
-                'EventEspresso\core\services\request\Request'                             => EE_Dependency_Map::load_from_cache,
719
-                'EE_Session'                                                              => EE_Dependency_Map::load_from_cache,
720
-                'EEM_Ticket'                                                              => EE_Dependency_Map::load_from_cache,
721
-                'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker' => EE_Dependency_Map::load_from_cache,
722
-            ],
723
-            'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker'                                     => [
724
-                'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
725
-            ],
726
-            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'                              => [
727
-                'EE_Core_Config'                             => EE_Dependency_Map::load_from_cache,
728
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
729
-            ],
730
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'                                => [
731
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
732
-            ],
733
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'                               => [
734
-                'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
735
-            ],
736
-            'EE_CPT_Strategy'                                                                                             => [
737
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
738
-                'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
739
-            ],
740
-            'EventEspresso\core\services\loaders\ObjectIdentifier'                                                        => [
741
-                'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
742
-            ],
743
-            'EventEspresso\core\domain\services\assets\CoreAssetManager'                                                  => [
744
-                'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
745
-                'EE_Currency_Config'                                 => EE_Dependency_Map::load_from_cache,
746
-                'EE_Template_Config'                                 => EE_Dependency_Map::load_from_cache,
747
-                'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
748
-                'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
749
-            ],
750
-            'EventEspresso\core\domain\services\admin\privacy\policy\PrivacyPolicy'                                       => [
751
-                'EEM_Payment_Method'                                       => EE_Dependency_Map::load_from_cache,
752
-                'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
753
-            ],
754
-            'EventEspresso\core\domain\services\admin\privacy\export\ExportAttendee'                                      => [
755
-                'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
756
-            ],
757
-            'EventEspresso\core\domain\services\admin\privacy\export\ExportAttendeeBillingData'                           => [
758
-                'EEM_Attendee'       => EE_Dependency_Map::load_from_cache,
759
-                'EEM_Payment_Method' => EE_Dependency_Map::load_from_cache,
760
-            ],
761
-            'EventEspresso\core\domain\services\admin\privacy\export\ExportCheckins'                                      => [
762
-                'EEM_Checkin' => EE_Dependency_Map::load_from_cache,
763
-            ],
764
-            'EventEspresso\core\domain\services\admin\privacy\export\ExportRegistration'                                  => [
765
-                'EEM_Registration' => EE_Dependency_Map::load_from_cache,
766
-            ],
767
-            'EventEspresso\core\domain\services\admin\privacy\export\ExportTransaction'                                   => [
768
-                'EEM_Transaction' => EE_Dependency_Map::load_from_cache,
769
-            ],
770
-            'EventEspresso\core\domain\services\admin\privacy\erasure\EraseAttendeeData'                                  => [
771
-                'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
772
-            ],
773
-            'EventEspresso\core\domain\services\admin\privacy\erasure\EraseAnswers'                                       => [
774
-                'EEM_Answer'   => EE_Dependency_Map::load_from_cache,
775
-                'EEM_Question' => EE_Dependency_Map::load_from_cache,
776
-            ],
777
-            'EventEspresso\core\CPTs\CptQueryModifier'                                                                    => [
778
-                null,
779
-                null,
780
-                null,
781
-                'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
782
-                'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
783
-                'EventEspresso\core\services\loaders\Loader'      => EE_Dependency_Map::load_from_cache,
784
-            ],
785
-            'EventEspresso\core\domain\services\admin\privacy\forms\PrivacySettingsFormHandler'                           => [
786
-                'EE_Registry' => EE_Dependency_Map::load_from_cache,
787
-                'EE_Config'   => EE_Dependency_Map::load_from_cache,
788
-            ],
789
-            'EventEspresso\core\services\editor\BlockRegistrationManager'                                                 => [
790
-                'EventEspresso\core\services\assets\BlockAssetManagerCollection'         => EE_Dependency_Map::load_from_cache,
791
-                'EventEspresso\core\domain\entities\editor\BlockCollection'              => EE_Dependency_Map::load_from_cache,
792
-                'EventEspresso\core\services\route_match\RouteMatchSpecificationManager' => EE_Dependency_Map::load_from_cache,
793
-                'EventEspresso\core\services\request\Request'                            => EE_Dependency_Map::load_from_cache,
794
-            ],
795
-            'EventEspresso\core\domain\entities\editor\CoreBlocksAssetManager'                                            => [
796
-                'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
797
-                'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
798
-                'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
799
-            ],
800
-            'EventEspresso\core\domain\services\blocks\EventAttendeesBlockRenderer'                                       => [
801
-                'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
802
-                'EEM_Attendee'                     => EE_Dependency_Map::load_from_cache,
803
-            ],
804
-            'EventEspresso\core\domain\entities\editor\blocks\EventAttendees'                                             => [
805
-                'EventEspresso\core\domain\entities\editor\CoreBlocksAssetManager'      => self::load_from_cache,
806
-                'EventEspresso\core\services\request\Request'                           => EE_Dependency_Map::load_from_cache,
807
-                'EventEspresso\core\domain\services\blocks\EventAttendeesBlockRenderer' => self::load_from_cache,
808
-            ],
809
-            'EventEspresso\core\services\route_match\RouteMatchSpecificationDependencyResolver'                           => [
810
-                'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
811
-                'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
812
-                'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
813
-            ],
814
-            'EventEspresso\core\services\route_match\RouteMatchSpecificationFactory'                                      => [
815
-                'EventEspresso\core\services\route_match\RouteMatchSpecificationDependencyResolver' => EE_Dependency_Map::load_from_cache,
816
-                'EventEspresso\core\services\loaders\Loader'                                        => EE_Dependency_Map::load_from_cache,
817
-            ],
818
-            'EventEspresso\core\services\route_match\RouteMatchSpecificationManager'                                      => [
819
-                'EventEspresso\core\services\route_match\RouteMatchSpecificationCollection' => EE_Dependency_Map::load_from_cache,
820
-                'EventEspresso\core\services\route_match\RouteMatchSpecificationFactory'    => EE_Dependency_Map::load_from_cache,
821
-            ],
822
-            'EventEspresso\core\libraries\rest_api\CalculatedModelFields'                                                 => [
823
-                'EventEspresso\core\libraries\rest_api\calculations\CalculatedModelFieldsFactory' => EE_Dependency_Map::load_from_cache,
824
-            ],
825
-            'EventEspresso\core\libraries\rest_api\calculations\CalculatedModelFieldsFactory'                             => [
826
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
827
-            ],
828
-            'EventEspresso\core\libraries\rest_api\controllers\model\Read'                                                => [
829
-                'EventEspresso\core\libraries\rest_api\CalculatedModelFields' => EE_Dependency_Map::load_from_cache,
830
-            ],
831
-            'EventEspresso\core\libraries\rest_api\calculations\Datetime'                                                 => [
832
-                'EEM_Datetime'     => EE_Dependency_Map::load_from_cache,
833
-                'EEM_Registration' => EE_Dependency_Map::load_from_cache,
834
-            ],
835
-            'EventEspresso\core\libraries\rest_api\calculations\Event'                                                    => [
836
-                'EEM_Event'        => EE_Dependency_Map::load_from_cache,
837
-                'EEM_Registration' => EE_Dependency_Map::load_from_cache,
838
-            ],
839
-            'EventEspresso\core\libraries\rest_api\calculations\Registration'                                             => [
840
-                'EEM_Registration' => EE_Dependency_Map::load_from_cache,
841
-            ],
842
-            'EventEspresso\core\services\session\SessionStartHandler'                                                     => [
843
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
844
-            ],
845
-            'EE_URL_Validation_Strategy'                                                                                  => [
846
-                null,
847
-                null,
848
-                'EventEspresso\core\services\validators\URLValidator' => EE_Dependency_Map::load_from_cache,
849
-            ],
850
-            'EventEspresso\admin_pages\general_settings\OrganizationSettings'                                             => [
851
-                'EE_Registry'                                             => EE_Dependency_Map::load_from_cache,
852
-                'EE_Organization_Config'                                  => EE_Dependency_Map::load_from_cache,
853
-                'EE_Core_Config'                                          => EE_Dependency_Map::load_from_cache,
854
-                'EE_Network_Core_Config'                                  => EE_Dependency_Map::load_from_cache,
855
-                'EventEspresso\core\services\address\CountrySubRegionDao' => EE_Dependency_Map::load_from_cache,
856
-            ],
857
-            'EventEspresso\core\services\address\CountrySubRegionDao'                                                     => [
858
-                'EEM_State'                                            => EE_Dependency_Map::load_from_cache,
859
-                'EventEspresso\core\services\validators\JsonValidator' => EE_Dependency_Map::load_from_cache,
860
-            ],
861
-            'EventEspresso\core\domain\services\admin\ajax\WordpressHeartbeat'                                            => [
862
-                'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
863
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
864
-            ],
865
-            'EventEspresso\core\domain\services\admin\ajax\EventEditorHeartbeat'                                          => [
866
-                'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
867
-                'EE_Environment_Config'            => EE_Dependency_Map::load_from_cache,
868
-            ],
869
-            'EventEspresso\core\services\request\files\FilesDataHandler'                                                  => [
870
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
871
-            ],
872
-            'EventEspressoBatchRequest\BatchRequestProcessor'                                                             => [
873
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
874
-            ],
875
-            'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder'                              => [
876
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
877
-                'EEM_Registration'                            => EE_Dependency_Map::load_from_cache,
878
-                null,
879
-            ],
880
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader'          => [
881
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
882
-                'EEM_Attendee'                                => EE_Dependency_Map::load_from_cache,
883
-            ],
884
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader'              => [
885
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
886
-                'EEM_Datetime'                                => EE_Dependency_Map::load_from_cache,
887
-            ],
888
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader'             => [
889
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
890
-                'EEM_Event'                                   => EE_Dependency_Map::load_from_cache,
891
-            ],
892
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader'            => [
893
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
894
-                'EEM_Ticket'                                  => EE_Dependency_Map::load_from_cache,
895
-            ],
896
-            'EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion'                                                  => [
897
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
898
-            ],
899
-            'EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'                                                  => [
900
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
901
-            ],
902
-            'EventEspresso\core\domain\services\admin\events\data\PreviewDeletion'                                        => [
903
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
904
-                'EEM_Event'                                                   => EE_Dependency_Map::load_from_cache,
905
-                'EEM_Datetime'                                                => EE_Dependency_Map::load_from_cache,
906
-                'EEM_Registration'                                            => EE_Dependency_Map::load_from_cache,
907
-            ],
908
-            'EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion'                                        => [
909
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
910
-            ],
911
-            'EventEspresso\core\services\request\CurrentPage'                                                             => [
912
-                'EE_CPT_Strategy'                             => EE_Dependency_Map::load_from_cache,
913
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
914
-            ],
915
-            'EventEspresso\core\services\shortcodes\LegacyShortcodesManager'                                              => [
916
-                'EE_Registry'                                     => EE_Dependency_Map::load_from_cache,
917
-                'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
918
-            ],
919
-            'EventEspresso\core\services\shortcodes\ShortcodesManager'                                                    => [
920
-                'EventEspresso\core\services\shortcodes\LegacyShortcodesManager' => EE_Dependency_Map::load_from_cache,
921
-                'EventEspresso\core\services\request\CurrentPage'                => EE_Dependency_Map::load_from_cache,
922
-            ],
923
-            'EventEspresso\core\services\activation\plugin_prompt\DownloadPluginPromptManager' => [
924
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache
925
-            ],
926
-        ];
927
-    }
928
-
929
-
930
-    /**
931
-     * Registers how core classes are loaded.
932
-     * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
933
-     *        'EE_Request_Handler' => 'load_core'
934
-     *        'EE_Messages_Queue'  => 'load_lib'
935
-     *        'EEH_Debug_Tools'    => 'load_helper'
936
-     * or, if greater control is required, by providing a custom closure. For example:
937
-     *        'Some_Class' => function () {
938
-     *            return new Some_Class();
939
-     *        },
940
-     * This is required for instantiating dependencies
941
-     * where an interface has been type hinted in a class constructor. For example:
942
-     *        'Required_Interface' => function () {
943
-     *            return new A_Class_That_Implements_Required_Interface();
944
-     *        },
945
-     */
946
-    protected function _register_core_class_loaders()
947
-    {
948
-        $this->_class_loaders = [
949
-            // load_core
950
-            'EE_Dependency_Map'                            => function () {
951
-                return $this;
952
-            },
953
-            'EE_Capabilities'                              => 'load_core',
954
-            'EE_Encryption'                                => 'load_core',
955
-            'EE_Front_Controller'                          => 'load_core',
956
-            'EE_Module_Request_Router'                     => 'load_core',
957
-            'EE_Registry'                                  => 'load_core',
958
-            'EE_Request'                                   => function () {
959
-                return $this->legacy_request;
960
-            },
961
-            'EventEspresso\core\services\request\Request'  => function () {
962
-                return $this->request;
963
-            },
964
-            'EventEspresso\core\services\request\Response' => function () {
965
-                return $this->response;
966
-            },
967
-            'EE_Base'                                      => 'load_core',
968
-            'EE_Request_Handler'                           => 'load_core',
969
-            'EE_Session'                                   => 'load_core',
970
-            'EE_Cron_Tasks'                                => 'load_core',
971
-            'EE_System'                                    => 'load_core',
972
-            'EE_Maintenance_Mode'                          => 'load_core',
973
-            'EE_Register_CPTs'                             => 'load_core',
974
-            'EE_Admin'                                     => 'load_core',
975
-            'EE_CPT_Strategy'                              => 'load_core',
976
-            // load_class
977
-            'EE_Registration_Processor'                    => 'load_class',
978
-            // load_lib
979
-            'EE_Message_Resource_Manager'                  => 'load_lib',
980
-            'EE_Message_Type_Collection'                   => 'load_lib',
981
-            'EE_Message_Type_Collection_Loader'            => 'load_lib',
982
-            'EE_Messenger_Collection'                      => 'load_lib',
983
-            'EE_Messenger_Collection_Loader'               => 'load_lib',
984
-            'EE_Messages_Processor'                        => 'load_lib',
985
-            'EE_Message_Repository'                        => 'load_lib',
986
-            'EE_Messages_Queue'                            => 'load_lib',
987
-            'EE_Messages_Data_Handler_Collection'          => 'load_lib',
988
-            'EE_Message_Template_Group_Collection'         => 'load_lib',
989
-            'EE_Payment_Method_Manager'                    => 'load_lib',
990
-            'EE_DMS_Core_4_1_0'                            => 'load_dms',
991
-            'EE_DMS_Core_4_2_0'                            => 'load_dms',
992
-            'EE_DMS_Core_4_3_0'                            => 'load_dms',
993
-            'EE_DMS_Core_4_5_0'                            => 'load_dms',
994
-            'EE_DMS_Core_4_6_0'                            => 'load_dms',
995
-            'EE_DMS_Core_4_7_0'                            => 'load_dms',
996
-            'EE_DMS_Core_4_8_0'                            => 'load_dms',
997
-            'EE_DMS_Core_4_9_0'                            => 'load_dms',
998
-            'EE_DMS_Core_4_10_0'                           => 'load_dms',
999
-            'EE_Messages_Generator'                        => function () {
1000
-                return EE_Registry::instance()->load_lib(
1001
-                    'Messages_Generator',
1002
-                    [],
1003
-                    false,
1004
-                    false
1005
-                );
1006
-            },
1007
-            'EE_Messages_Template_Defaults'                => function ($arguments = []) {
1008
-                return EE_Registry::instance()->load_lib(
1009
-                    'Messages_Template_Defaults',
1010
-                    $arguments,
1011
-                    false,
1012
-                    false
1013
-                );
1014
-            },
1015
-            // load_helper
1016
-            'EEH_Parse_Shortcodes'                         => function () {
1017
-                if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
1018
-                    return new EEH_Parse_Shortcodes();
1019
-                }
1020
-                return null;
1021
-            },
1022
-            'EE_Template_Config'                           => function () {
1023
-                return EE_Config::instance()->template_settings;
1024
-            },
1025
-            'EE_Currency_Config'                           => function () {
1026
-                return EE_Config::instance()->currency;
1027
-            },
1028
-            'EE_Registration_Config'                       => function () {
1029
-                return EE_Config::instance()->registration;
1030
-            },
1031
-            'EE_Core_Config'                               => function () {
1032
-                return EE_Config::instance()->core;
1033
-            },
1034
-            'EventEspresso\core\services\loaders\Loader'   => function () {
1035
-                return LoaderFactory::getLoader();
1036
-            },
1037
-            'EE_Network_Config'                            => function () {
1038
-                return EE_Network_Config::instance();
1039
-            },
1040
-            'EE_Config'                                    => function () {
1041
-                return EE_Config::instance();
1042
-            },
1043
-            'EventEspresso\core\domain\Domain'             => function () {
1044
-                return DomainFactory::getEventEspressoCoreDomain();
1045
-            },
1046
-            'EE_Admin_Config'                              => function () {
1047
-                return EE_Config::instance()->admin;
1048
-            },
1049
-            'EE_Organization_Config'                       => function () {
1050
-                return EE_Config::instance()->organization;
1051
-            },
1052
-            'EE_Network_Core_Config'                       => function () {
1053
-                return EE_Network_Config::instance()->core;
1054
-            },
1055
-            'EE_Environment_Config'                        => function () {
1056
-                return EE_Config::instance()->environment;
1057
-            },
1058
-            'EE_Ticket_Selector_Config'                    => function () {
1059
-                return EE_Config::instance()->template_settings->EED_Ticket_Selector;
1060
-            },
1061
-        ];
1062
-    }
1063
-
1064
-
1065
-    /**
1066
-     * can be used for supplying alternate names for classes,
1067
-     * or for connecting interface names to instantiable classes
1068
-     */
1069
-    protected function _register_core_aliases()
1070
-    {
1071
-        $aliases = [
1072
-            'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
1073
-            'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
1074
-            'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
1075
-            'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
1076
-            'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
1077
-            'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
1078
-            'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
1079
-            'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
1080
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
1081
-            'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
1082
-            'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\domain\services\commands\registration\CreateRegistrationCommand',
1083
-            'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\domain\services\commands\registration\CopyRegistrationDetailsCommand',
1084
-            'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\domain\services\commands\registration\CopyRegistrationPaymentsCommand',
1085
-            'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\domain\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
1086
-            'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\domain\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
1087
-            'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\domain\services\commands\ticket\CreateTicketLineItemCommand',
1088
-            'CreateTransactionCommandHandler'                                              => 'EventEspresso\core\domain\services\commands\transaction\CreateTransactionCommandHandler',
1089
-            'CreateAttendeeCommandHandler'                                                 => 'EventEspresso\core\domain\services\commands\attendee\CreateAttendeeCommandHandler',
1090
-            'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
1091
-            'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
1092
-            'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
1093
-            'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
1094
-            'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
1095
-            'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
1096
-            'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
1097
-            'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
1098
-            'CommandFactoryInterface'                                                      => 'EventEspresso\core\services\commands\CommandFactoryInterface',
1099
-            'EventEspresso\core\services\commands\CommandFactoryInterface'                 => 'EventEspresso\core\services\commands\CommandFactory',
1100
-            'EmailValidatorInterface'                                                      => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
1101
-            'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface'  => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
1102
-            'NoticeConverterInterface'                                                     => 'EventEspresso\core\services\notices\NoticeConverterInterface',
1103
-            'EventEspresso\core\services\notices\NoticeConverterInterface'                 => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
1104
-            'NoticesContainerInterface'                                                    => 'EventEspresso\core\services\notices\NoticesContainerInterface',
1105
-            'EventEspresso\core\services\notices\NoticesContainerInterface'                => 'EventEspresso\core\services\notices\NoticesContainer',
1106
-            'EventEspresso\core\services\request\RequestInterface'                         => 'EventEspresso\core\services\request\Request',
1107
-            'EventEspresso\core\services\request\ResponseInterface'                        => 'EventEspresso\core\services\request\Response',
1108
-            'EventEspresso\core\domain\DomainInterface'                                    => 'EventEspresso\core\domain\Domain',
1109
-            'Registration_Processor'                                                       => 'EE_Registration_Processor',
1110
-        ];
1111
-        foreach ($aliases as $alias => $fqn) {
1112
-            if (is_array($fqn)) {
1113
-                foreach ($fqn as $class => $for_class) {
1114
-                    $this->class_cache->addAlias($class, $alias, $for_class);
1115
-                }
1116
-                continue;
1117
-            }
1118
-            $this->class_cache->addAlias($fqn, $alias);
1119
-        }
1120
-        if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1121
-            $this->class_cache->addAlias(
1122
-                'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices',
1123
-                'EventEspresso\core\services\notices\NoticeConverterInterface'
1124
-            );
1125
-        }
1126
-    }
1127
-
1128
-
1129
-    public function debug($for_class = '')
1130
-    {
1131
-        $this->class_cache->debug($for_class);
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
1137
-     * request Primarily used by unit tests.
1138
-     */
1139
-    public function reset()
1140
-    {
1141
-        $this->_register_core_class_loaders();
1142
-        $this->_register_core_dependencies();
1143
-    }
1144
-
1145
-
1146
-    /**
1147
-     * PLZ NOTE: a better name for this method would be is_alias()
1148
-     * because it returns TRUE if the provided fully qualified name IS an alias
1149
-     * WHY?
1150
-     * Because if a class is type hinting for a concretion,
1151
-     * then why would we need to find another class to supply it?
1152
-     * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
1153
-     * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
1154
-     * Don't go looking for some substitute.
1155
-     * Whereas if a class is type hinting for an interface...
1156
-     * then we need to find an actual class to use.
1157
-     * So the interface IS the alias for some other FQN,
1158
-     * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
1159
-     * represents some other class.
1160
-     *
1161
-     * @param string $fqn
1162
-     * @param string $for_class
1163
-     * @return bool
1164
-     * @deprecated 4.9.62.p
1165
-     */
1166
-    public function has_alias($fqn = '', $for_class = '')
1167
-    {
1168
-        return $this->isAlias($fqn, $for_class);
1169
-    }
1170
-
1171
-
1172
-    /**
1173
-     * PLZ NOTE: a better name for this method would be get_fqn_for_alias()
1174
-     * because it returns a FQN for provided alias if one exists, otherwise returns the original $alias
1175
-     * functions recursively, so that multiple aliases can be used to drill down to a FQN
1176
-     *  for example:
1177
-     *      if the following two entries were added to the _aliases array:
1178
-     *          array(
1179
-     *              'interface_alias'           => 'some\namespace\interface'
1180
-     *              'some\namespace\interface'  => 'some\namespace\classname'
1181
-     *          )
1182
-     *      then one could use EE_Registry::instance()->create( 'interface_alias' )
1183
-     *      to load an instance of 'some\namespace\classname'
1184
-     *
1185
-     * @param string $alias
1186
-     * @param string $for_class
1187
-     * @return string
1188
-     * @deprecated 4.9.62.p
1189
-     */
1190
-    public function get_alias($alias = '', $for_class = '')
1191
-    {
1192
-        return $this->getFqnForAlias($alias, $for_class);
1193
-    }
22
+	/**
23
+	 * This means that the requested class dependency is not present in the dependency map
24
+	 */
25
+	const not_registered = 0;
26
+
27
+	/**
28
+	 * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
29
+	 */
30
+	const load_new_object = 1;
31
+
32
+	/**
33
+	 * This instructs class loaders to return a previously instantiated and cached object for the requested class.
34
+	 * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
35
+	 */
36
+	const load_from_cache = 2;
37
+
38
+	/**
39
+	 * When registering a dependency,
40
+	 * this indicates to keep any existing dependencies that already exist,
41
+	 * and simply discard any new dependencies declared in the incoming data
42
+	 */
43
+	const KEEP_EXISTING_DEPENDENCIES = 0;
44
+
45
+	/**
46
+	 * When registering a dependency,
47
+	 * this indicates to overwrite any existing dependencies that already exist using the incoming data
48
+	 */
49
+	const OVERWRITE_DEPENDENCIES = 1;
50
+
51
+
52
+	/**
53
+	 * @type EE_Dependency_Map $_instance
54
+	 */
55
+	protected static $_instance;
56
+
57
+	/**
58
+	 * @var ClassInterfaceCache $class_cache
59
+	 */
60
+	private $class_cache;
61
+
62
+	/**
63
+	 * @type RequestInterface $request
64
+	 */
65
+	protected $request;
66
+
67
+	/**
68
+	 * @type LegacyRequestInterface $legacy_request
69
+	 */
70
+	protected $legacy_request;
71
+
72
+	/**
73
+	 * @type ResponseInterface $response
74
+	 */
75
+	protected $response;
76
+
77
+	/**
78
+	 * @type LoaderInterface $loader
79
+	 */
80
+	protected $loader;
81
+
82
+	/**
83
+	 * @type array $_dependency_map
84
+	 */
85
+	protected $_dependency_map = [];
86
+
87
+	/**
88
+	 * @type array $_class_loaders
89
+	 */
90
+	protected $_class_loaders = [];
91
+
92
+
93
+	/**
94
+	 * EE_Dependency_Map constructor.
95
+	 *
96
+	 * @param ClassInterfaceCache $class_cache
97
+	 */
98
+	protected function __construct(ClassInterfaceCache $class_cache)
99
+	{
100
+		$this->class_cache = $class_cache;
101
+		do_action('EE_Dependency_Map____construct', $this);
102
+	}
103
+
104
+
105
+	/**
106
+	 * @return void
107
+	 */
108
+	public function initialize()
109
+	{
110
+		$this->_register_core_dependencies();
111
+		$this->_register_core_class_loaders();
112
+		$this->_register_core_aliases();
113
+	}
114
+
115
+
116
+	/**
117
+	 * @singleton method used to instantiate class object
118
+	 * @param ClassInterfaceCache|null $class_cache
119
+	 * @return EE_Dependency_Map
120
+	 */
121
+	public static function instance(ClassInterfaceCache $class_cache = null)
122
+	{
123
+		// check if class object is instantiated, and instantiated properly
124
+		if (
125
+			! self::$_instance instanceof EE_Dependency_Map
126
+			&& $class_cache instanceof ClassInterfaceCache
127
+		) {
128
+			self::$_instance = new EE_Dependency_Map($class_cache);
129
+		}
130
+		return self::$_instance;
131
+	}
132
+
133
+
134
+	/**
135
+	 * @param RequestInterface $request
136
+	 */
137
+	public function setRequest(RequestInterface $request)
138
+	{
139
+		$this->request = $request;
140
+	}
141
+
142
+
143
+	/**
144
+	 * @param LegacyRequestInterface $legacy_request
145
+	 */
146
+	public function setLegacyRequest(LegacyRequestInterface $legacy_request)
147
+	{
148
+		$this->legacy_request = $legacy_request;
149
+	}
150
+
151
+
152
+	/**
153
+	 * @param ResponseInterface $response
154
+	 */
155
+	public function setResponse(ResponseInterface $response)
156
+	{
157
+		$this->response = $response;
158
+	}
159
+
160
+
161
+	/**
162
+	 * @param LoaderInterface $loader
163
+	 */
164
+	public function setLoader(LoaderInterface $loader)
165
+	{
166
+		$this->loader = $loader;
167
+	}
168
+
169
+
170
+	/**
171
+	 * @param string $class
172
+	 * @param array  $dependencies
173
+	 * @param int    $overwrite
174
+	 * @return bool
175
+	 */
176
+	public static function register_dependencies(
177
+		$class,
178
+		array $dependencies,
179
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
180
+	) {
181
+		return self::$_instance->registerDependencies($class, $dependencies, $overwrite);
182
+	}
183
+
184
+
185
+	/**
186
+	 * Assigns an array of class names and corresponding load sources (new or cached)
187
+	 * to the class specified by the first parameter.
188
+	 * IMPORTANT !!!
189
+	 * The order of elements in the incoming $dependencies array MUST match
190
+	 * the order of the constructor parameters for the class in question.
191
+	 * This is especially important when overriding any existing dependencies that are registered.
192
+	 * the third parameter controls whether any duplicate dependencies are overwritten or not.
193
+	 *
194
+	 * @param string $class
195
+	 * @param array  $dependencies
196
+	 * @param int    $overwrite
197
+	 * @return bool
198
+	 */
199
+	public function registerDependencies(
200
+		$class,
201
+		array $dependencies,
202
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
203
+	) {
204
+		$class      = trim($class, '\\');
205
+		$registered = false;
206
+		if (empty(self::$_instance->_dependency_map[ $class ])) {
207
+			self::$_instance->_dependency_map[ $class ] = [];
208
+		}
209
+		// we need to make sure that any aliases used when registering a dependency
210
+		// get resolved to the correct class name
211
+		foreach ($dependencies as $dependency => $load_source) {
212
+			$alias = self::$_instance->getFqnForAlias($dependency);
213
+			if (
214
+				$overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
215
+				|| ! isset(self::$_instance->_dependency_map[ $class ][ $alias ])
216
+			) {
217
+				unset($dependencies[ $dependency ]);
218
+				$dependencies[ $alias ] = $load_source;
219
+				$registered             = true;
220
+			}
221
+		}
222
+		// now add our two lists of dependencies together.
223
+		// using Union (+=) favours the arrays in precedence from left to right,
224
+		// so $dependencies is NOT overwritten because it is listed first
225
+		// ie: with A = B + C, entries in B take precedence over duplicate entries in C
226
+		// Union is way faster than array_merge() but should be used with caution...
227
+		// especially with numerically indexed arrays
228
+		$dependencies += self::$_instance->_dependency_map[ $class ];
229
+		// now we need to ensure that the resulting dependencies
230
+		// array only has the entries that are required for the class
231
+		// so first count how many dependencies were originally registered for the class
232
+		$dependency_count = count(self::$_instance->_dependency_map[ $class ]);
233
+		// if that count is non-zero (meaning dependencies were already registered)
234
+		self::$_instance->_dependency_map[ $class ] = $dependency_count
235
+			// then truncate the  final array to match that count
236
+			? array_slice($dependencies, 0, $dependency_count)
237
+			// otherwise just take the incoming array because nothing previously existed
238
+			: $dependencies;
239
+		return $registered;
240
+	}
241
+
242
+
243
+	/**
244
+	 * @param string $class_name
245
+	 * @param string $loader
246
+	 * @param bool   $overwrite
247
+	 * @return bool
248
+	 * @throws DomainException
249
+	 */
250
+	public static function register_class_loader($class_name, $loader = 'load_core', $overwrite = false)
251
+	{
252
+		if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
253
+			throw new DomainException(
254
+				esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
255
+			);
256
+		}
257
+		// check that loader is callable or method starts with "load_" and exists in EE_Registry
258
+		if (
259
+			! is_callable($loader)
260
+			&& (
261
+				strpos($loader, 'load_') !== 0
262
+				|| ! method_exists('EE_Registry', $loader)
263
+			)
264
+		) {
265
+			throw new DomainException(
266
+				sprintf(
267
+					esc_html__(
268
+						'"%1$s" is not a valid loader method on EE_Registry.',
269
+						'event_espresso'
270
+					),
271
+					$loader
272
+				)
273
+			);
274
+		}
275
+		$class_name = self::$_instance->getFqnForAlias($class_name);
276
+		if ($overwrite || ! isset(self::$_instance->_class_loaders[ $class_name ])) {
277
+			self::$_instance->_class_loaders[ $class_name ] = $loader;
278
+			return true;
279
+		}
280
+		return false;
281
+	}
282
+
283
+
284
+	/**
285
+	 * @return array
286
+	 */
287
+	public function dependency_map()
288
+	{
289
+		return $this->_dependency_map;
290
+	}
291
+
292
+
293
+	/**
294
+	 * returns TRUE if dependency map contains a listing for the provided class name
295
+	 *
296
+	 * @param string $class_name
297
+	 * @return boolean
298
+	 */
299
+	public function has($class_name = '')
300
+	{
301
+		// all legacy models have the same dependencies
302
+		if (strpos($class_name, 'EEM_') === 0) {
303
+			$class_name = 'LEGACY_MODELS';
304
+		}
305
+		return isset($this->_dependency_map[ $class_name ]);
306
+	}
307
+
308
+
309
+	/**
310
+	 * returns TRUE if dependency map contains a listing for the provided class name AND dependency
311
+	 *
312
+	 * @param string $class_name
313
+	 * @param string $dependency
314
+	 * @return bool
315
+	 */
316
+	public function has_dependency_for_class($class_name = '', $dependency = '')
317
+	{
318
+		// all legacy models have the same dependencies
319
+		if (strpos($class_name, 'EEM_') === 0) {
320
+			$class_name = 'LEGACY_MODELS';
321
+		}
322
+		$dependency = $this->getFqnForAlias($dependency, $class_name);
323
+		return isset($this->_dependency_map[ $class_name ][ $dependency ]);
324
+	}
325
+
326
+
327
+	/**
328
+	 * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
329
+	 *
330
+	 * @param string $class_name
331
+	 * @param string $dependency
332
+	 * @return int
333
+	 */
334
+	public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
335
+	{
336
+		// all legacy models have the same dependencies
337
+		if (strpos($class_name, 'EEM_') === 0) {
338
+			$class_name = 'LEGACY_MODELS';
339
+		}
340
+		$dependency = $this->getFqnForAlias($dependency);
341
+		return $this->has_dependency_for_class($class_name, $dependency)
342
+			? $this->_dependency_map[ $class_name ][ $dependency ]
343
+			: EE_Dependency_Map::not_registered;
344
+	}
345
+
346
+
347
+	/**
348
+	 * @param string $class_name
349
+	 * @return string | Closure
350
+	 */
351
+	public function class_loader($class_name)
352
+	{
353
+		// all legacy models use load_model()
354
+		if (strpos($class_name, 'EEM_') === 0) {
355
+			return 'load_model';
356
+		}
357
+		// EE_CPT_*_Strategy classes like EE_CPT_Event_Strategy, EE_CPT_Venue_Strategy, etc
358
+		// perform strpos() first to avoid loading regex every time we load a class
359
+		if (
360
+			strpos($class_name, 'EE_CPT_') === 0
361
+			&& preg_match('/^EE_CPT_([a-zA-Z]+)_Strategy$/', $class_name)
362
+		) {
363
+			return 'load_core';
364
+		}
365
+		$class_name = $this->getFqnForAlias($class_name);
366
+		return isset($this->_class_loaders[ $class_name ]) ? $this->_class_loaders[ $class_name ] : '';
367
+	}
368
+
369
+
370
+	/**
371
+	 * @return array
372
+	 */
373
+	public function class_loaders()
374
+	{
375
+		return $this->_class_loaders;
376
+	}
377
+
378
+
379
+	/**
380
+	 * adds an alias for a classname
381
+	 *
382
+	 * @param string $fqcn      the class name that should be used (concrete class to replace interface)
383
+	 * @param string $alias     the class name that would be type hinted for (abstract parent or interface)
384
+	 * @param string $for_class the class that has the dependency (is type hinting for the interface)
385
+	 */
386
+	public function add_alias($fqcn, $alias, $for_class = '')
387
+	{
388
+		$this->class_cache->addAlias($fqcn, $alias, $for_class);
389
+	}
390
+
391
+
392
+	/**
393
+	 * Returns TRUE if the provided fully qualified name IS an alias
394
+	 * WHY?
395
+	 * Because if a class is type hinting for a concretion,
396
+	 * then why would we need to find another class to supply it?
397
+	 * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
398
+	 * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
399
+	 * Don't go looking for some substitute.
400
+	 * Whereas if a class is type hinting for an interface...
401
+	 * then we need to find an actual class to use.
402
+	 * So the interface IS the alias for some other FQN,
403
+	 * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
404
+	 * represents some other class.
405
+	 *
406
+	 * @param string $fqn
407
+	 * @param string $for_class
408
+	 * @return bool
409
+	 */
410
+	public function isAlias($fqn = '', $for_class = '')
411
+	{
412
+		return $this->class_cache->isAlias($fqn, $for_class);
413
+	}
414
+
415
+
416
+	/**
417
+	 * Returns a FQN for provided alias if one exists, otherwise returns the original $alias
418
+	 * functions recursively, so that multiple aliases can be used to drill down to a FQN
419
+	 *  for example:
420
+	 *      if the following two entries were added to the _aliases array:
421
+	 *          array(
422
+	 *              'interface_alias'           => 'some\namespace\interface'
423
+	 *              'some\namespace\interface'  => 'some\namespace\classname'
424
+	 *          )
425
+	 *      then one could use EE_Registry::instance()->create( 'interface_alias' )
426
+	 *      to load an instance of 'some\namespace\classname'
427
+	 *
428
+	 * @param string $alias
429
+	 * @param string $for_class
430
+	 * @return string
431
+	 */
432
+	public function getFqnForAlias($alias = '', $for_class = '')
433
+	{
434
+		return $this->class_cache->getFqnForAlias($alias, $for_class);
435
+	}
436
+
437
+
438
+	/**
439
+	 * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
440
+	 * if one exists, or whether a new object should be generated every time the requested class is loaded.
441
+	 * This is done by using the following class constants:
442
+	 *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
443
+	 *        EE_Dependency_Map::load_new_object - generates a new object every time
444
+	 */
445
+	protected function _register_core_dependencies()
446
+	{
447
+		$this->_dependency_map = [
448
+			'EE_Admin'                                                                                          => [
449
+				'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
450
+			],
451
+			'EE_Request_Handler'                                                                                          => [
452
+				'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
453
+				'EventEspresso\core\services\request\Response'    => EE_Dependency_Map::load_from_cache,
454
+			],
455
+			'EE_System'                                                                                                   => [
456
+				'EE_Registry'                                 => EE_Dependency_Map::load_from_cache,
457
+				'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
458
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
459
+				'EE_Maintenance_Mode'                         => EE_Dependency_Map::load_from_cache,
460
+			],
461
+			'EE_Session'                                                                                                  => [
462
+				'EventEspresso\core\services\cache\TransientCacheStorage'  => EE_Dependency_Map::load_from_cache,
463
+				'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
464
+				'EventEspresso\core\services\request\Request'              => EE_Dependency_Map::load_from_cache,
465
+				'EventEspresso\core\services\session\SessionStartHandler'  => EE_Dependency_Map::load_from_cache,
466
+				'EE_Encryption'                                            => EE_Dependency_Map::load_from_cache,
467
+			],
468
+			'EE_Cart'                                                                                                     => [
469
+				'EE_Session' => EE_Dependency_Map::load_from_cache,
470
+			],
471
+			'EE_Front_Controller'                                                                                         => [
472
+				'EE_Registry'                                     => EE_Dependency_Map::load_from_cache,
473
+				'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
474
+				'EE_Module_Request_Router'                        => EE_Dependency_Map::load_from_cache,
475
+			],
476
+			'EE_Messenger_Collection_Loader'                                                                              => [
477
+				'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
478
+			],
479
+			'EE_Message_Type_Collection_Loader'                                                                           => [
480
+				'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
481
+			],
482
+			'EE_Message_Resource_Manager'                                                                                 => [
483
+				'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
484
+				'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
485
+				'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
486
+			],
487
+			'EE_Message_Factory'                                                                                          => [
488
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
489
+			],
490
+			'EE_messages'                                                                                                 => [
491
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
492
+			],
493
+			'EE_Messages_Generator'                                                                                       => [
494
+				'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
495
+				'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
496
+				'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
497
+				'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
498
+			],
499
+			'EE_Messages_Processor'                                                                                       => [
500
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
501
+			],
502
+			'EE_Messages_Queue'                                                                                           => [
503
+				'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
504
+			],
505
+			'EE_Messages_Template_Defaults'                                                                               => [
506
+				'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
507
+				'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
508
+			],
509
+			'EE_Message_To_Generate_From_Request'                                                                         => [
510
+				'EE_Message_Resource_Manager'                 => EE_Dependency_Map::load_from_cache,
511
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
512
+			],
513
+			'EventEspresso\core\services\commands\CommandBus'                                                             => [
514
+				'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
515
+			],
516
+			'EventEspresso\services\commands\CommandHandler'                                                              => [
517
+				'EE_Registry'         => EE_Dependency_Map::load_from_cache,
518
+				'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
519
+			],
520
+			'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => [
521
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
522
+			],
523
+			'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => [
524
+				'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
525
+				'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
526
+			],
527
+			'EventEspresso\core\services\commands\CommandFactory'                                                         => [
528
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
529
+			],
530
+			'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => [
531
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
532
+			],
533
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => [
534
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
535
+			],
536
+			'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => [
537
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
538
+			],
539
+			'EventEspresso\core\domain\services\commands\registration\CreateRegistrationCommandHandler'                          => [
540
+				'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
541
+			],
542
+			'EventEspresso\core\domain\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => [
543
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
544
+			],
545
+			'EventEspresso\core\domain\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => [
546
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
547
+			],
548
+			'EventEspresso\core\domain\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => [
549
+				'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
550
+			],
551
+			'EventEspresso\core\domain\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => [
552
+				'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
553
+			],
554
+			'EventEspresso\core\domain\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => [
555
+				'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
556
+			],
557
+			'EventEspresso\core\domain\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => [
558
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
559
+			],
560
+			'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => [
561
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
562
+			],
563
+			'EventEspresso\core\domain\services\commands\attendee\CreateAttendeeCommandHandler'                                  => [
564
+				'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
565
+			],
566
+			'EventEspresso\core\services\database\TableManager'                                                           => [
567
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
568
+			],
569
+			'EE_Data_Migration_Class_Base'                                                                                => [
570
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
571
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
572
+			],
573
+			'EE_DMS_Core_4_1_0'                                                                                           => [
574
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
575
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
576
+			],
577
+			'EE_DMS_Core_4_2_0'                                                                                           => [
578
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
579
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
580
+			],
581
+			'EE_DMS_Core_4_3_0'                                                                                           => [
582
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
583
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
584
+			],
585
+			'EE_DMS_Core_4_4_0'                                                                                           => [
586
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
587
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
588
+			],
589
+			'EE_DMS_Core_4_5_0'                                                                                           => [
590
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
591
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
592
+			],
593
+			'EE_DMS_Core_4_6_0'                                                                                           => [
594
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
595
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
596
+			],
597
+			'EE_DMS_Core_4_7_0'                                                                                           => [
598
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
599
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
600
+			],
601
+			'EE_DMS_Core_4_8_0'                                                                                           => [
602
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
603
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
604
+			],
605
+			'EE_DMS_Core_4_9_0'                                                                                           => [
606
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
607
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
608
+			],
609
+			'EE_DMS_Core_4_10_0'                                                                                          => [
610
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
611
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
612
+				'EE_DMS_Core_4_9_0'                                  => EE_Dependency_Map::load_from_cache,
613
+			],
614
+			'EventEspresso\core\services\assets\I18nRegistry'                                                             => [
615
+				'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
616
+			],
617
+			'EventEspresso\core\services\assets\Registry'                                                                 => [
618
+				'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
619
+				'EventEspresso\core\services\assets\I18nRegistry'    => EE_Dependency_Map::load_from_cache,
620
+			],
621
+			'EventEspresso\core\domain\entities\shortcodes\EspressoCancelled'                                             => [
622
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
623
+			],
624
+			'EventEspresso\core\domain\entities\shortcodes\EspressoCheckout'                                              => [
625
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
626
+			],
627
+			'EventEspresso\core\domain\entities\shortcodes\EspressoEventAttendees'                                        => [
628
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
629
+			],
630
+			'EventEspresso\core\domain\entities\shortcodes\EspressoEvents'                                                => [
631
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
632
+			],
633
+			'EventEspresso\core\domain\entities\shortcodes\EspressoThankYou'                                              => [
634
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
635
+			],
636
+			'EventEspresso\core\domain\entities\shortcodes\EspressoTicketSelector'                                        => [
637
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
638
+			],
639
+			'EventEspresso\core\domain\entities\shortcodes\EspressoTxnPage'                                               => [
640
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
641
+			],
642
+			'EventEspresso\core\services\cache\BasicCacheManager'                                                         => [
643
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
644
+			],
645
+			'EventEspresso\core\services\cache\PostRelatedCacheManager'                                                   => [
646
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
647
+			],
648
+			'EventEspresso\core\domain\services\validation\email\EmailValidationService'                                  => [
649
+				'EE_Registration_Config'                     => EE_Dependency_Map::load_from_cache,
650
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
651
+			],
652
+			'EventEspresso\core\domain\values\EmailAddress'                                                               => [
653
+				null,
654
+				'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
655
+			],
656
+			'EventEspresso\core\services\orm\ModelFieldFactory'                                                           => [
657
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
658
+			],
659
+			'LEGACY_MODELS'                                                                                               => [
660
+				null,
661
+				'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
662
+			],
663
+			'EE_Module_Request_Router'                                                                                    => [
664
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
665
+			],
666
+			'EE_Registration_Processor'                                                                                   => [
667
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
668
+			],
669
+			'EventEspresso\core\services\notifications\PersistentAdminNoticeManager'                                      => [
670
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
671
+				'EventEspresso\core\services\request\Request'                         => EE_Dependency_Map::load_from_cache,
672
+			],
673
+			'EventEspresso\core\services\licensing\LicenseService'                                                        => [
674
+				'EventEspresso\core\domain\services\pue\Stats'  => EE_Dependency_Map::load_from_cache,
675
+				'EventEspresso\core\domain\services\pue\Config' => EE_Dependency_Map::load_from_cache,
676
+			],
677
+			'EE_Admin_Transactions_List_Table'                                                                            => [
678
+				null,
679
+				'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
680
+			],
681
+			'EventEspresso\core\domain\services\pue\Stats'                                                                => [
682
+				'EventEspresso\core\domain\services\pue\Config'        => EE_Dependency_Map::load_from_cache,
683
+				'EE_Maintenance_Mode'                                  => EE_Dependency_Map::load_from_cache,
684
+				'EventEspresso\core\domain\services\pue\StatsGatherer' => EE_Dependency_Map::load_from_cache,
685
+			],
686
+			'EventEspresso\core\domain\services\pue\Config'                                                               => [
687
+				'EE_Network_Config' => EE_Dependency_Map::load_from_cache,
688
+				'EE_Config'         => EE_Dependency_Map::load_from_cache,
689
+			],
690
+			'EventEspresso\core\domain\services\pue\StatsGatherer'                                                        => [
691
+				'EEM_Payment_Method' => EE_Dependency_Map::load_from_cache,
692
+				'EEM_Event'          => EE_Dependency_Map::load_from_cache,
693
+				'EEM_Datetime'       => EE_Dependency_Map::load_from_cache,
694
+				'EEM_Ticket'         => EE_Dependency_Map::load_from_cache,
695
+				'EEM_Registration'   => EE_Dependency_Map::load_from_cache,
696
+				'EEM_Transaction'    => EE_Dependency_Map::load_from_cache,
697
+				'EE_Config'          => EE_Dependency_Map::load_from_cache,
698
+			],
699
+			'EventEspresso\core\domain\services\admin\ExitModal'                                                          => [
700
+				'EventEspresso\core\services\assets\Registry' => EE_Dependency_Map::load_from_cache,
701
+			],
702
+			'EventEspresso\core\domain\services\admin\PluginUpsells'                                                      => [
703
+				'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
704
+			],
705
+			'EventEspresso\caffeinated\modules\recaptcha_invisible\InvisibleRecaptcha'                                    => [
706
+				'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
707
+				'EE_Session'             => EE_Dependency_Map::load_from_cache,
708
+			],
709
+			'EventEspresso\caffeinated\modules\recaptcha_invisible\RecaptchaAdminSettings'                                => [
710
+				'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
711
+			],
712
+			'EventEspresso\modules\ticket_selector\DisplayTicketSelector' => [
713
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
714
+				'EE_Ticket_Selector_Config'                   => EE_Dependency_Map::load_from_cache,
715
+			],
716
+			'EventEspresso\modules\ticket_selector\ProcessTicketSelector'                                                 => [
717
+				'EE_Core_Config'                                                          => EE_Dependency_Map::load_from_cache,
718
+				'EventEspresso\core\services\request\Request'                             => EE_Dependency_Map::load_from_cache,
719
+				'EE_Session'                                                              => EE_Dependency_Map::load_from_cache,
720
+				'EEM_Ticket'                                                              => EE_Dependency_Map::load_from_cache,
721
+				'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker' => EE_Dependency_Map::load_from_cache,
722
+			],
723
+			'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker'                                     => [
724
+				'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
725
+			],
726
+			'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'                              => [
727
+				'EE_Core_Config'                             => EE_Dependency_Map::load_from_cache,
728
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
729
+			],
730
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'                                => [
731
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
732
+			],
733
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'                               => [
734
+				'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
735
+			],
736
+			'EE_CPT_Strategy'                                                                                             => [
737
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
738
+				'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
739
+			],
740
+			'EventEspresso\core\services\loaders\ObjectIdentifier'                                                        => [
741
+				'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
742
+			],
743
+			'EventEspresso\core\domain\services\assets\CoreAssetManager'                                                  => [
744
+				'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
745
+				'EE_Currency_Config'                                 => EE_Dependency_Map::load_from_cache,
746
+				'EE_Template_Config'                                 => EE_Dependency_Map::load_from_cache,
747
+				'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
748
+				'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
749
+			],
750
+			'EventEspresso\core\domain\services\admin\privacy\policy\PrivacyPolicy'                                       => [
751
+				'EEM_Payment_Method'                                       => EE_Dependency_Map::load_from_cache,
752
+				'EventEspresso\core\domain\values\session\SessionLifespan' => EE_Dependency_Map::load_from_cache,
753
+			],
754
+			'EventEspresso\core\domain\services\admin\privacy\export\ExportAttendee'                                      => [
755
+				'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
756
+			],
757
+			'EventEspresso\core\domain\services\admin\privacy\export\ExportAttendeeBillingData'                           => [
758
+				'EEM_Attendee'       => EE_Dependency_Map::load_from_cache,
759
+				'EEM_Payment_Method' => EE_Dependency_Map::load_from_cache,
760
+			],
761
+			'EventEspresso\core\domain\services\admin\privacy\export\ExportCheckins'                                      => [
762
+				'EEM_Checkin' => EE_Dependency_Map::load_from_cache,
763
+			],
764
+			'EventEspresso\core\domain\services\admin\privacy\export\ExportRegistration'                                  => [
765
+				'EEM_Registration' => EE_Dependency_Map::load_from_cache,
766
+			],
767
+			'EventEspresso\core\domain\services\admin\privacy\export\ExportTransaction'                                   => [
768
+				'EEM_Transaction' => EE_Dependency_Map::load_from_cache,
769
+			],
770
+			'EventEspresso\core\domain\services\admin\privacy\erasure\EraseAttendeeData'                                  => [
771
+				'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
772
+			],
773
+			'EventEspresso\core\domain\services\admin\privacy\erasure\EraseAnswers'                                       => [
774
+				'EEM_Answer'   => EE_Dependency_Map::load_from_cache,
775
+				'EEM_Question' => EE_Dependency_Map::load_from_cache,
776
+			],
777
+			'EventEspresso\core\CPTs\CptQueryModifier'                                                                    => [
778
+				null,
779
+				null,
780
+				null,
781
+				'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
782
+				'EventEspresso\core\services\request\Request'     => EE_Dependency_Map::load_from_cache,
783
+				'EventEspresso\core\services\loaders\Loader'      => EE_Dependency_Map::load_from_cache,
784
+			],
785
+			'EventEspresso\core\domain\services\admin\privacy\forms\PrivacySettingsFormHandler'                           => [
786
+				'EE_Registry' => EE_Dependency_Map::load_from_cache,
787
+				'EE_Config'   => EE_Dependency_Map::load_from_cache,
788
+			],
789
+			'EventEspresso\core\services\editor\BlockRegistrationManager'                                                 => [
790
+				'EventEspresso\core\services\assets\BlockAssetManagerCollection'         => EE_Dependency_Map::load_from_cache,
791
+				'EventEspresso\core\domain\entities\editor\BlockCollection'              => EE_Dependency_Map::load_from_cache,
792
+				'EventEspresso\core\services\route_match\RouteMatchSpecificationManager' => EE_Dependency_Map::load_from_cache,
793
+				'EventEspresso\core\services\request\Request'                            => EE_Dependency_Map::load_from_cache,
794
+			],
795
+			'EventEspresso\core\domain\entities\editor\CoreBlocksAssetManager'                                            => [
796
+				'EventEspresso\core\domain\Domain'                   => EE_Dependency_Map::load_from_cache,
797
+				'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_from_cache,
798
+				'EventEspresso\core\services\assets\Registry'        => EE_Dependency_Map::load_from_cache,
799
+			],
800
+			'EventEspresso\core\domain\services\blocks\EventAttendeesBlockRenderer'                                       => [
801
+				'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
802
+				'EEM_Attendee'                     => EE_Dependency_Map::load_from_cache,
803
+			],
804
+			'EventEspresso\core\domain\entities\editor\blocks\EventAttendees'                                             => [
805
+				'EventEspresso\core\domain\entities\editor\CoreBlocksAssetManager'      => self::load_from_cache,
806
+				'EventEspresso\core\services\request\Request'                           => EE_Dependency_Map::load_from_cache,
807
+				'EventEspresso\core\domain\services\blocks\EventAttendeesBlockRenderer' => self::load_from_cache,
808
+			],
809
+			'EventEspresso\core\services\route_match\RouteMatchSpecificationDependencyResolver'                           => [
810
+				'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
811
+				'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
812
+				'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
813
+			],
814
+			'EventEspresso\core\services\route_match\RouteMatchSpecificationFactory'                                      => [
815
+				'EventEspresso\core\services\route_match\RouteMatchSpecificationDependencyResolver' => EE_Dependency_Map::load_from_cache,
816
+				'EventEspresso\core\services\loaders\Loader'                                        => EE_Dependency_Map::load_from_cache,
817
+			],
818
+			'EventEspresso\core\services\route_match\RouteMatchSpecificationManager'                                      => [
819
+				'EventEspresso\core\services\route_match\RouteMatchSpecificationCollection' => EE_Dependency_Map::load_from_cache,
820
+				'EventEspresso\core\services\route_match\RouteMatchSpecificationFactory'    => EE_Dependency_Map::load_from_cache,
821
+			],
822
+			'EventEspresso\core\libraries\rest_api\CalculatedModelFields'                                                 => [
823
+				'EventEspresso\core\libraries\rest_api\calculations\CalculatedModelFieldsFactory' => EE_Dependency_Map::load_from_cache,
824
+			],
825
+			'EventEspresso\core\libraries\rest_api\calculations\CalculatedModelFieldsFactory'                             => [
826
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
827
+			],
828
+			'EventEspresso\core\libraries\rest_api\controllers\model\Read'                                                => [
829
+				'EventEspresso\core\libraries\rest_api\CalculatedModelFields' => EE_Dependency_Map::load_from_cache,
830
+			],
831
+			'EventEspresso\core\libraries\rest_api\calculations\Datetime'                                                 => [
832
+				'EEM_Datetime'     => EE_Dependency_Map::load_from_cache,
833
+				'EEM_Registration' => EE_Dependency_Map::load_from_cache,
834
+			],
835
+			'EventEspresso\core\libraries\rest_api\calculations\Event'                                                    => [
836
+				'EEM_Event'        => EE_Dependency_Map::load_from_cache,
837
+				'EEM_Registration' => EE_Dependency_Map::load_from_cache,
838
+			],
839
+			'EventEspresso\core\libraries\rest_api\calculations\Registration'                                             => [
840
+				'EEM_Registration' => EE_Dependency_Map::load_from_cache,
841
+			],
842
+			'EventEspresso\core\services\session\SessionStartHandler'                                                     => [
843
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
844
+			],
845
+			'EE_URL_Validation_Strategy'                                                                                  => [
846
+				null,
847
+				null,
848
+				'EventEspresso\core\services\validators\URLValidator' => EE_Dependency_Map::load_from_cache,
849
+			],
850
+			'EventEspresso\admin_pages\general_settings\OrganizationSettings'                                             => [
851
+				'EE_Registry'                                             => EE_Dependency_Map::load_from_cache,
852
+				'EE_Organization_Config'                                  => EE_Dependency_Map::load_from_cache,
853
+				'EE_Core_Config'                                          => EE_Dependency_Map::load_from_cache,
854
+				'EE_Network_Core_Config'                                  => EE_Dependency_Map::load_from_cache,
855
+				'EventEspresso\core\services\address\CountrySubRegionDao' => EE_Dependency_Map::load_from_cache,
856
+			],
857
+			'EventEspresso\core\services\address\CountrySubRegionDao'                                                     => [
858
+				'EEM_State'                                            => EE_Dependency_Map::load_from_cache,
859
+				'EventEspresso\core\services\validators\JsonValidator' => EE_Dependency_Map::load_from_cache,
860
+			],
861
+			'EventEspresso\core\domain\services\admin\ajax\WordpressHeartbeat'                                            => [
862
+				'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
863
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
864
+			],
865
+			'EventEspresso\core\domain\services\admin\ajax\EventEditorHeartbeat'                                          => [
866
+				'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
867
+				'EE_Environment_Config'            => EE_Dependency_Map::load_from_cache,
868
+			],
869
+			'EventEspresso\core\services\request\files\FilesDataHandler'                                                  => [
870
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
871
+			],
872
+			'EventEspressoBatchRequest\BatchRequestProcessor'                                                             => [
873
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
874
+			],
875
+			'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder'                              => [
876
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
877
+				'EEM_Registration'                            => EE_Dependency_Map::load_from_cache,
878
+				null,
879
+			],
880
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader'          => [
881
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
882
+				'EEM_Attendee'                                => EE_Dependency_Map::load_from_cache,
883
+			],
884
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader'              => [
885
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
886
+				'EEM_Datetime'                                => EE_Dependency_Map::load_from_cache,
887
+			],
888
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader'             => [
889
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
890
+				'EEM_Event'                                   => EE_Dependency_Map::load_from_cache,
891
+			],
892
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader'            => [
893
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
894
+				'EEM_Ticket'                                  => EE_Dependency_Map::load_from_cache,
895
+			],
896
+			'EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion'                                                  => [
897
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
898
+			],
899
+			'EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'                                                  => [
900
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
901
+			],
902
+			'EventEspresso\core\domain\services\admin\events\data\PreviewDeletion'                                        => [
903
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
904
+				'EEM_Event'                                                   => EE_Dependency_Map::load_from_cache,
905
+				'EEM_Datetime'                                                => EE_Dependency_Map::load_from_cache,
906
+				'EEM_Registration'                                            => EE_Dependency_Map::load_from_cache,
907
+			],
908
+			'EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion'                                        => [
909
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
910
+			],
911
+			'EventEspresso\core\services\request\CurrentPage'                                                             => [
912
+				'EE_CPT_Strategy'                             => EE_Dependency_Map::load_from_cache,
913
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
914
+			],
915
+			'EventEspresso\core\services\shortcodes\LegacyShortcodesManager'                                              => [
916
+				'EE_Registry'                                     => EE_Dependency_Map::load_from_cache,
917
+				'EventEspresso\core\services\request\CurrentPage' => EE_Dependency_Map::load_from_cache,
918
+			],
919
+			'EventEspresso\core\services\shortcodes\ShortcodesManager'                                                    => [
920
+				'EventEspresso\core\services\shortcodes\LegacyShortcodesManager' => EE_Dependency_Map::load_from_cache,
921
+				'EventEspresso\core\services\request\CurrentPage'                => EE_Dependency_Map::load_from_cache,
922
+			],
923
+			'EventEspresso\core\services\activation\plugin_prompt\DownloadPluginPromptManager' => [
924
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache
925
+			],
926
+		];
927
+	}
928
+
929
+
930
+	/**
931
+	 * Registers how core classes are loaded.
932
+	 * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
933
+	 *        'EE_Request_Handler' => 'load_core'
934
+	 *        'EE_Messages_Queue'  => 'load_lib'
935
+	 *        'EEH_Debug_Tools'    => 'load_helper'
936
+	 * or, if greater control is required, by providing a custom closure. For example:
937
+	 *        'Some_Class' => function () {
938
+	 *            return new Some_Class();
939
+	 *        },
940
+	 * This is required for instantiating dependencies
941
+	 * where an interface has been type hinted in a class constructor. For example:
942
+	 *        'Required_Interface' => function () {
943
+	 *            return new A_Class_That_Implements_Required_Interface();
944
+	 *        },
945
+	 */
946
+	protected function _register_core_class_loaders()
947
+	{
948
+		$this->_class_loaders = [
949
+			// load_core
950
+			'EE_Dependency_Map'                            => function () {
951
+				return $this;
952
+			},
953
+			'EE_Capabilities'                              => 'load_core',
954
+			'EE_Encryption'                                => 'load_core',
955
+			'EE_Front_Controller'                          => 'load_core',
956
+			'EE_Module_Request_Router'                     => 'load_core',
957
+			'EE_Registry'                                  => 'load_core',
958
+			'EE_Request'                                   => function () {
959
+				return $this->legacy_request;
960
+			},
961
+			'EventEspresso\core\services\request\Request'  => function () {
962
+				return $this->request;
963
+			},
964
+			'EventEspresso\core\services\request\Response' => function () {
965
+				return $this->response;
966
+			},
967
+			'EE_Base'                                      => 'load_core',
968
+			'EE_Request_Handler'                           => 'load_core',
969
+			'EE_Session'                                   => 'load_core',
970
+			'EE_Cron_Tasks'                                => 'load_core',
971
+			'EE_System'                                    => 'load_core',
972
+			'EE_Maintenance_Mode'                          => 'load_core',
973
+			'EE_Register_CPTs'                             => 'load_core',
974
+			'EE_Admin'                                     => 'load_core',
975
+			'EE_CPT_Strategy'                              => 'load_core',
976
+			// load_class
977
+			'EE_Registration_Processor'                    => 'load_class',
978
+			// load_lib
979
+			'EE_Message_Resource_Manager'                  => 'load_lib',
980
+			'EE_Message_Type_Collection'                   => 'load_lib',
981
+			'EE_Message_Type_Collection_Loader'            => 'load_lib',
982
+			'EE_Messenger_Collection'                      => 'load_lib',
983
+			'EE_Messenger_Collection_Loader'               => 'load_lib',
984
+			'EE_Messages_Processor'                        => 'load_lib',
985
+			'EE_Message_Repository'                        => 'load_lib',
986
+			'EE_Messages_Queue'                            => 'load_lib',
987
+			'EE_Messages_Data_Handler_Collection'          => 'load_lib',
988
+			'EE_Message_Template_Group_Collection'         => 'load_lib',
989
+			'EE_Payment_Method_Manager'                    => 'load_lib',
990
+			'EE_DMS_Core_4_1_0'                            => 'load_dms',
991
+			'EE_DMS_Core_4_2_0'                            => 'load_dms',
992
+			'EE_DMS_Core_4_3_0'                            => 'load_dms',
993
+			'EE_DMS_Core_4_5_0'                            => 'load_dms',
994
+			'EE_DMS_Core_4_6_0'                            => 'load_dms',
995
+			'EE_DMS_Core_4_7_0'                            => 'load_dms',
996
+			'EE_DMS_Core_4_8_0'                            => 'load_dms',
997
+			'EE_DMS_Core_4_9_0'                            => 'load_dms',
998
+			'EE_DMS_Core_4_10_0'                           => 'load_dms',
999
+			'EE_Messages_Generator'                        => function () {
1000
+				return EE_Registry::instance()->load_lib(
1001
+					'Messages_Generator',
1002
+					[],
1003
+					false,
1004
+					false
1005
+				);
1006
+			},
1007
+			'EE_Messages_Template_Defaults'                => function ($arguments = []) {
1008
+				return EE_Registry::instance()->load_lib(
1009
+					'Messages_Template_Defaults',
1010
+					$arguments,
1011
+					false,
1012
+					false
1013
+				);
1014
+			},
1015
+			// load_helper
1016
+			'EEH_Parse_Shortcodes'                         => function () {
1017
+				if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
1018
+					return new EEH_Parse_Shortcodes();
1019
+				}
1020
+				return null;
1021
+			},
1022
+			'EE_Template_Config'                           => function () {
1023
+				return EE_Config::instance()->template_settings;
1024
+			},
1025
+			'EE_Currency_Config'                           => function () {
1026
+				return EE_Config::instance()->currency;
1027
+			},
1028
+			'EE_Registration_Config'                       => function () {
1029
+				return EE_Config::instance()->registration;
1030
+			},
1031
+			'EE_Core_Config'                               => function () {
1032
+				return EE_Config::instance()->core;
1033
+			},
1034
+			'EventEspresso\core\services\loaders\Loader'   => function () {
1035
+				return LoaderFactory::getLoader();
1036
+			},
1037
+			'EE_Network_Config'                            => function () {
1038
+				return EE_Network_Config::instance();
1039
+			},
1040
+			'EE_Config'                                    => function () {
1041
+				return EE_Config::instance();
1042
+			},
1043
+			'EventEspresso\core\domain\Domain'             => function () {
1044
+				return DomainFactory::getEventEspressoCoreDomain();
1045
+			},
1046
+			'EE_Admin_Config'                              => function () {
1047
+				return EE_Config::instance()->admin;
1048
+			},
1049
+			'EE_Organization_Config'                       => function () {
1050
+				return EE_Config::instance()->organization;
1051
+			},
1052
+			'EE_Network_Core_Config'                       => function () {
1053
+				return EE_Network_Config::instance()->core;
1054
+			},
1055
+			'EE_Environment_Config'                        => function () {
1056
+				return EE_Config::instance()->environment;
1057
+			},
1058
+			'EE_Ticket_Selector_Config'                    => function () {
1059
+				return EE_Config::instance()->template_settings->EED_Ticket_Selector;
1060
+			},
1061
+		];
1062
+	}
1063
+
1064
+
1065
+	/**
1066
+	 * can be used for supplying alternate names for classes,
1067
+	 * or for connecting interface names to instantiable classes
1068
+	 */
1069
+	protected function _register_core_aliases()
1070
+	{
1071
+		$aliases = [
1072
+			'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
1073
+			'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
1074
+			'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
1075
+			'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
1076
+			'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
1077
+			'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
1078
+			'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
1079
+			'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
1080
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
1081
+			'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
1082
+			'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\domain\services\commands\registration\CreateRegistrationCommand',
1083
+			'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\domain\services\commands\registration\CopyRegistrationDetailsCommand',
1084
+			'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\domain\services\commands\registration\CopyRegistrationPaymentsCommand',
1085
+			'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\domain\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
1086
+			'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\domain\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
1087
+			'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\domain\services\commands\ticket\CreateTicketLineItemCommand',
1088
+			'CreateTransactionCommandHandler'                                              => 'EventEspresso\core\domain\services\commands\transaction\CreateTransactionCommandHandler',
1089
+			'CreateAttendeeCommandHandler'                                                 => 'EventEspresso\core\domain\services\commands\attendee\CreateAttendeeCommandHandler',
1090
+			'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
1091
+			'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
1092
+			'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
1093
+			'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
1094
+			'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
1095
+			'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
1096
+			'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
1097
+			'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
1098
+			'CommandFactoryInterface'                                                      => 'EventEspresso\core\services\commands\CommandFactoryInterface',
1099
+			'EventEspresso\core\services\commands\CommandFactoryInterface'                 => 'EventEspresso\core\services\commands\CommandFactory',
1100
+			'EmailValidatorInterface'                                                      => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
1101
+			'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface'  => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
1102
+			'NoticeConverterInterface'                                                     => 'EventEspresso\core\services\notices\NoticeConverterInterface',
1103
+			'EventEspresso\core\services\notices\NoticeConverterInterface'                 => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
1104
+			'NoticesContainerInterface'                                                    => 'EventEspresso\core\services\notices\NoticesContainerInterface',
1105
+			'EventEspresso\core\services\notices\NoticesContainerInterface'                => 'EventEspresso\core\services\notices\NoticesContainer',
1106
+			'EventEspresso\core\services\request\RequestInterface'                         => 'EventEspresso\core\services\request\Request',
1107
+			'EventEspresso\core\services\request\ResponseInterface'                        => 'EventEspresso\core\services\request\Response',
1108
+			'EventEspresso\core\domain\DomainInterface'                                    => 'EventEspresso\core\domain\Domain',
1109
+			'Registration_Processor'                                                       => 'EE_Registration_Processor',
1110
+		];
1111
+		foreach ($aliases as $alias => $fqn) {
1112
+			if (is_array($fqn)) {
1113
+				foreach ($fqn as $class => $for_class) {
1114
+					$this->class_cache->addAlias($class, $alias, $for_class);
1115
+				}
1116
+				continue;
1117
+			}
1118
+			$this->class_cache->addAlias($fqn, $alias);
1119
+		}
1120
+		if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1121
+			$this->class_cache->addAlias(
1122
+				'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices',
1123
+				'EventEspresso\core\services\notices\NoticeConverterInterface'
1124
+			);
1125
+		}
1126
+	}
1127
+
1128
+
1129
+	public function debug($for_class = '')
1130
+	{
1131
+		$this->class_cache->debug($for_class);
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
1137
+	 * request Primarily used by unit tests.
1138
+	 */
1139
+	public function reset()
1140
+	{
1141
+		$this->_register_core_class_loaders();
1142
+		$this->_register_core_dependencies();
1143
+	}
1144
+
1145
+
1146
+	/**
1147
+	 * PLZ NOTE: a better name for this method would be is_alias()
1148
+	 * because it returns TRUE if the provided fully qualified name IS an alias
1149
+	 * WHY?
1150
+	 * Because if a class is type hinting for a concretion,
1151
+	 * then why would we need to find another class to supply it?
1152
+	 * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
1153
+	 * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
1154
+	 * Don't go looking for some substitute.
1155
+	 * Whereas if a class is type hinting for an interface...
1156
+	 * then we need to find an actual class to use.
1157
+	 * So the interface IS the alias for some other FQN,
1158
+	 * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
1159
+	 * represents some other class.
1160
+	 *
1161
+	 * @param string $fqn
1162
+	 * @param string $for_class
1163
+	 * @return bool
1164
+	 * @deprecated 4.9.62.p
1165
+	 */
1166
+	public function has_alias($fqn = '', $for_class = '')
1167
+	{
1168
+		return $this->isAlias($fqn, $for_class);
1169
+	}
1170
+
1171
+
1172
+	/**
1173
+	 * PLZ NOTE: a better name for this method would be get_fqn_for_alias()
1174
+	 * because it returns a FQN for provided alias if one exists, otherwise returns the original $alias
1175
+	 * functions recursively, so that multiple aliases can be used to drill down to a FQN
1176
+	 *  for example:
1177
+	 *      if the following two entries were added to the _aliases array:
1178
+	 *          array(
1179
+	 *              'interface_alias'           => 'some\namespace\interface'
1180
+	 *              'some\namespace\interface'  => 'some\namespace\classname'
1181
+	 *          )
1182
+	 *      then one could use EE_Registry::instance()->create( 'interface_alias' )
1183
+	 *      to load an instance of 'some\namespace\classname'
1184
+	 *
1185
+	 * @param string $alias
1186
+	 * @param string $for_class
1187
+	 * @return string
1188
+	 * @deprecated 4.9.62.p
1189
+	 */
1190
+	public function get_alias($alias = '', $for_class = '')
1191
+	{
1192
+		return $this->getFqnForAlias($alias, $for_class);
1193
+	}
1194 1194
 }
Please login to merge, or discard this patch.
core/services/activation/plugin_prompt/DownloadPluginPromptManager.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -25,7 +25,7 @@  discard block
 block discarded – undo
25 25
     {
26 26
         $this->cap_checker = $capabilities_checker;
27 27
         $this->loadPluginPrompts();
28
-        if (! empty($this->plugin_prompts)) {
28
+        if ( ! empty($this->plugin_prompts)) {
29 29
             add_action('pre_current_active_plugins', [$this, 'displayPluginPrompts'], 100);
30 30
         }
31 31
     }
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
         }
49 49
         echo "
50 50
         <div class='ee-download-plugin-prompts__grid'>
51
-            " . wp_kses($notifications, AllowedTags::getWithFullTags()) . "
51
+            " . wp_kses($notifications, AllowedTags::getWithFullTags())."
52 52
         </div>";
53 53
     }
54 54
 
@@ -74,7 +74,7 @@  discard block
 block discarded – undo
74 74
         if (
75 75
             class_exists('EED_Core_Rest_Api')
76 76
             && ! (
77
-                is_readable(EE_THIRD_PARTY . 'wp-api-basic-auth/basic-auth.php')
77
+                is_readable(EE_THIRD_PARTY.'wp-api-basic-auth/basic-auth.php')
78 78
                 || class_exists('Jwt_Auth')
79 79
                 || defined('MINIORANGE_API_AUTHENTICATION_VERSION')
80 80
             )
Please login to merge, or discard this patch.
Indentation   +77 added lines, -77 removed lines patch added patch discarded remove patch
@@ -7,93 +7,93 @@  discard block
 block discarded – undo
7 7
 
8 8
 class DownloadPluginPromptManager
9 9
 {
10
-    /**
11
-     * @var CapabilitiesChecker
12
-     */
13
-    private $cap_checker;
10
+	/**
11
+	 * @var CapabilitiesChecker
12
+	 */
13
+	private $cap_checker;
14 14
 
15
-    /**
16
-     * @var DownloadPluginPrompt[]
17
-     */
18
-    private $plugin_prompts = [];
15
+	/**
16
+	 * @var DownloadPluginPrompt[]
17
+	 */
18
+	private $plugin_prompts = [];
19 19
 
20 20
 
21
-    /**
22
-     * @param CapabilitiesChecker $capabilities_checker
23
-     */
24
-    public function __construct(CapabilitiesChecker $capabilities_checker)
25
-    {
26
-        $this->cap_checker = $capabilities_checker;
27
-        $this->loadPluginPrompts();
28
-        if (! empty($this->plugin_prompts)) {
29
-            add_action('pre_current_active_plugins', [$this, 'displayPluginPrompts'], 100);
30
-        }
31
-    }
21
+	/**
22
+	 * @param CapabilitiesChecker $capabilities_checker
23
+	 */
24
+	public function __construct(CapabilitiesChecker $capabilities_checker)
25
+	{
26
+		$this->cap_checker = $capabilities_checker;
27
+		$this->loadPluginPrompts();
28
+		if (! empty($this->plugin_prompts)) {
29
+			add_action('pre_current_active_plugins', [$this, 'displayPluginPrompts'], 100);
30
+		}
31
+	}
32 32
 
33 33
 
34
-    public function loadPluginPrompts()
35
-    {
36
-        $this->wpGraphQlPrompt();
37
-        $this->restApiAuthPrompt();
38
-    }
34
+	public function loadPluginPrompts()
35
+	{
36
+		$this->wpGraphQlPrompt();
37
+		$this->restApiAuthPrompt();
38
+	}
39 39
 
40 40
 
41
-    public function displayPluginPrompts()
42
-    {
43
-        $notifications = '';
44
-        foreach ($this->plugin_prompts as $plugin_prompt) {
45
-            if ($this->cap_checker->processCapCheck($plugin_prompt->getCapCheck())) {
46
-                $notifications .= $plugin_prompt->displayNotification(true);
47
-            }
48
-        }
49
-        echo "
41
+	public function displayPluginPrompts()
42
+	{
43
+		$notifications = '';
44
+		foreach ($this->plugin_prompts as $plugin_prompt) {
45
+			if ($this->cap_checker->processCapCheck($plugin_prompt->getCapCheck())) {
46
+				$notifications .= $plugin_prompt->displayNotification(true);
47
+			}
48
+		}
49
+		echo "
50 50
         <div class='ee-download-plugin-prompts__grid'>
51 51
             " . wp_kses($notifications, AllowedTags::getWithFullTags()) . "
52 52
         </div>";
53
-    }
53
+	}
54 54
 
55 55
 
56
-    private function wpGraphQlPrompt()
57
-    {
58
-        if (class_exists('EventEspresso\core\services\graphql\GraphQLManager') && ! class_exists('WPGraphQL')) {
59
-            $this->plugin_prompts['WPGraphQL'] = new DownloadPluginPrompt(
60
-                'WPGraphQL',
61
-                'https://www.wpgraphql.com/',
62
-                "Event Espresso's new Advanced Editor",
63
-                null,
64
-                null,
65
-                'keen-performance.svg',
66
-                500
67
-            );
68
-        }
69
-    }
56
+	private function wpGraphQlPrompt()
57
+	{
58
+		if (class_exists('EventEspresso\core\services\graphql\GraphQLManager') && ! class_exists('WPGraphQL')) {
59
+			$this->plugin_prompts['WPGraphQL'] = new DownloadPluginPrompt(
60
+				'WPGraphQL',
61
+				'https://www.wpgraphql.com/',
62
+				"Event Espresso's new Advanced Editor",
63
+				null,
64
+				null,
65
+				'keen-performance.svg',
66
+				500
67
+			);
68
+		}
69
+	}
70 70
 
71 71
 
72
-    private function restApiAuthPrompt()
73
-    {
74
-        if (
75
-            class_exists('EED_Core_Rest_Api')
76
-            && ! (
77
-                is_readable(EE_THIRD_PARTY . 'wp-api-basic-auth/basic-auth.php')
78
-                || class_exists('Jwt_Auth')
79
-                || defined('MINIORANGE_API_AUTHENTICATION_VERSION')
80
-            )
81
-        ) {
82
-            $this->plugin_prompts['REST-API-Auth'] = new DownloadPluginPrompt(
83
-                'WP REST API Authentication',
84
-                '',
85
-                'The Event Espresso REST API',
86
-                esc_html__('REST API Authentication!', 'event_espresso'),
87
-                sprintf(
88
-                    /* translators: The Event Espresso REST API requires an Authentication plugin to protect your
72
+	private function restApiAuthPrompt()
73
+	{
74
+		if (
75
+			class_exists('EED_Core_Rest_Api')
76
+			&& ! (
77
+				is_readable(EE_THIRD_PARTY . 'wp-api-basic-auth/basic-auth.php')
78
+				|| class_exists('Jwt_Auth')
79
+				|| defined('MINIORANGE_API_AUTHENTICATION_VERSION')
80
+			)
81
+		) {
82
+			$this->plugin_prompts['REST-API-Auth'] = new DownloadPluginPrompt(
83
+				'WP REST API Authentication',
84
+				'',
85
+				'The Event Espresso REST API',
86
+				esc_html__('REST API Authentication!', 'event_espresso'),
87
+				sprintf(
88
+					/* translators: The Event Espresso REST API requires an Authentication plugin to protect your
89 89
                     site\'s data endpoints. We highly encourage you to secure your site using one of the following:
90 90
                     <list of plugins> */
91
-                    esc_html__(
92
-                        'The Event Espresso REST API requires an Authentication plugin to protect your site\'s data endpoints. We highly encourage you to secure your site using one of the following: %1$s',
93
-                        'event_espresso'
94
-                    ),
95
-                    // 'Plugin Name & Link'
96
-                    "
91
+					esc_html__(
92
+						'The Event Espresso REST API requires an Authentication plugin to protect your site\'s data endpoints. We highly encourage you to secure your site using one of the following: %1$s',
93
+						'event_espresso'
94
+					),
95
+					// 'Plugin Name & Link'
96
+					"
97 97
                     <ul>
98 98
                         <li>
99 99
                             <a href='https://github.com/WP-API/Basic-Auth' target='_blank'>
@@ -111,10 +111,10 @@  discard block
 block discarded – undo
111 111
                             </a>
112 112
                         </li>
113 113
                     </ul>"
114
-                ),
115
-                'password.svg',
116
-                570
117
-            );
118
-        }
119
-    }
114
+				),
115
+				'password.svg',
116
+				570
117
+			);
118
+		}
119
+	}
120 120
 }
Please login to merge, or discard this patch.
modules/core_rest_api/EED_Core_Rest_Api.module.php 2 patches
Indentation   +1381 added lines, -1381 removed lines patch added patch discarded remove patch
@@ -21,1385 +21,1385 @@
 block discarded – undo
21 21
  */
22 22
 class EED_Core_Rest_Api extends EED_Module
23 23
 {
24
-    const ee_api_namespace           = Domain::API_NAMESPACE;
25
-
26
-    const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
27
-
28
-    const saved_routes_option_names  = 'ee_core_routes';
29
-
30
-    /**
31
-     * string used in _links response bodies to make them globally unique.
32
-     *
33
-     * @see http://v2.wp-api.org/extending/linking/
34
-     */
35
-    const ee_api_link_namespace = 'https://api.eventespresso.com/';
36
-
37
-    /**
38
-     * @var CalculatedModelFields
39
-     */
40
-    protected static $_field_calculator;
41
-
42
-
43
-    /**
44
-     * @return EED_Core_Rest_Api|EED_Module
45
-     * @throws EE_Error
46
-     * @throws ReflectionException
47
-     */
48
-    public static function instance()
49
-    {
50
-        self::$_field_calculator =
51
-            LoaderFactory::getLoader()->load('EventEspresso\core\libraries\rest_api\CalculatedModelFields');
52
-        return parent::get_instance(__CLASS__);
53
-    }
54
-
55
-
56
-    /**
57
-     *    set_hooks - for hooking into EE Core, other modules, etc
58
-     *
59
-     * @access    public
60
-     * @return    void
61
-     */
62
-    public static function set_hooks()
63
-    {
64
-        self::set_hooks_both();
65
-    }
66
-
67
-
68
-    /**
69
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
70
-     *
71
-     * @access    public
72
-     * @return    void
73
-     */
74
-    public static function set_hooks_admin()
75
-    {
76
-        self::set_hooks_both();
77
-    }
78
-
79
-
80
-    public static function set_hooks_both()
81
-    {
82
-        add_action('rest_api_init', ['EED_Core_Rest_Api', 'set_hooks_rest_api'], 5);
83
-        add_action('rest_api_init', ['EED_Core_Rest_Api', 'register_routes'], 10);
84
-        add_filter('rest_route_data', ['EED_Core_Rest_Api', 'hide_old_endpoints'], 10, 2);
85
-        add_filter(
86
-            'rest_index',
87
-            ['EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex']
88
-        );
89
-        EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
90
-    }
91
-
92
-
93
-    /**
94
-     * sets up hooks which only need to be included as part of REST API requests;
95
-     * other requests like to the frontend or admin etc don't need them
96
-     *
97
-     * @throws EE_Error
98
-     */
99
-    public static function set_hooks_rest_api()
100
-    {
101
-        // set hooks which account for changes made to the API
102
-        EED_Core_Rest_Api::_set_hooks_for_changes();
103
-    }
104
-
105
-
106
-    /**
107
-     * public wrapper of _set_hooks_for_changes.
108
-     * Loads all the hooks which make requests to old versions of the API
109
-     * appear the same as they always did
110
-     *
111
-     * @throws EE_Error
112
-     */
113
-    public static function set_hooks_for_changes()
114
-    {
115
-        self::_set_hooks_for_changes();
116
-    }
117
-
118
-
119
-    /**
120
-     * Loads all the hooks which make requests to old versions of the API
121
-     * appear the same as they always did
122
-     *
123
-     * @throws EE_Error
124
-     */
125
-    protected static function _set_hooks_for_changes()
126
-    {
127
-        $folder_contents = EEH_File::get_contents_of_folders([EE_LIBRARIES . 'rest_api/changes']);
128
-        foreach ($folder_contents as $classname_in_namespace => $filepath) {
129
-            // ignore the base parent class
130
-            // and legacy named classes
131
-            if (
132
-                $classname_in_namespace === 'ChangesInBase'
133
-                || strpos($classname_in_namespace, 'Changes_In_') === 0
134
-            ) {
135
-                continue;
136
-            }
137
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
138
-            if (class_exists($full_classname)) {
139
-                $instance_of_class = new $full_classname();
140
-                if ($instance_of_class instanceof ChangesInBase) {
141
-                    $instance_of_class->setHooks();
142
-                }
143
-            }
144
-        }
145
-    }
146
-
147
-
148
-    /**
149
-     * Filters the WP routes to add our EE-related ones. This takes a bit of time
150
-     * so we actually prefer to only do it when an EE plugin is activated or upgraded
151
-     *
152
-     * @throws EE_Error
153
-     * @throws ReflectionException
154
-     */
155
-    public static function register_routes()
156
-    {
157
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
158
-            foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
159
-                /**
160
-                 * @var array     $data_for_multiple_endpoints numerically indexed array
161
-                 *                                         but can also contain route options like {
162
-                 * @type array    $schema                      {
163
-                 * @type callable $schema_callback
164
-                 * @type array    $callback_args               arguments that will be passed to the callback, after the
165
-                 * WP_REST_Request of course
166
-                 * }
167
-                 * }
168
-                 */
169
-                // when registering routes, register all the endpoints' data at the same time
170
-                $multiple_endpoint_args = [];
171
-                foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
172
-                    /**
173
-                     * @var array     $data_for_single_endpoint {
174
-                     * @type callable $callback
175
-                     * @type string methods
176
-                     * @type array args
177
-                     * @type array _links
178
-                     * @type array    $callback_args            arguments that will be passed to the callback, after the
179
-                     * WP_REST_Request of course
180
-                     * }
181
-                     */
182
-                    // skip route options
183
-                    if (! is_numeric($endpoint_key)) {
184
-                        continue;
185
-                    }
186
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
187
-                        throw new EE_Error(
188
-                            esc_html__(
189
-                            // @codingStandardsIgnoreStart
190
-                                'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
191
-                                // @codingStandardsIgnoreEnd
192
-                                'event_espresso'
193
-                            )
194
-                        );
195
-                    }
196
-                    $callback             = $data_for_single_endpoint['callback'];
197
-                    $single_endpoint_args = [
198
-                        'methods' => $data_for_single_endpoint['methods'],
199
-                        'args'    => $data_for_single_endpoint['args'] ?? [],
200
-                    ];
201
-                    if (isset($data_for_single_endpoint['_links'])) {
202
-                        $single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
203
-                    }
204
-                    if (isset($data_for_single_endpoint['callback_args'])) {
205
-                        $callback_args                    = $data_for_single_endpoint['callback_args'];
206
-                        $single_endpoint_args['callback'] = function (WP_REST_Request $request) use (
207
-                            $callback,
208
-                            $callback_args
209
-                        ) {
210
-                            array_unshift($callback_args, $request);
211
-                            return call_user_func_array(
212
-                                $callback,
213
-                                $callback_args
214
-                            );
215
-                        };
216
-                    } else {
217
-                        $single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
218
-                    }
219
-                    // As of WordPress 5.5, if a permission_callback is not provided,
220
-                    // the REST API will issue a _doing_it_wrong notice.
221
-                    // Since the EE REST API defers capabilities to the db model system,
222
-                    // we will just use the generic WP callback for public endpoints
223
-                    if (! isset($single_endpoint_args['permission_callback'])) {
224
-                        $single_endpoint_args['permission_callback'] = '__return_true';
225
-                    }
226
-                    $multiple_endpoint_args[] = $single_endpoint_args;
227
-                }
228
-                if (isset($data_for_multiple_endpoints['schema'])) {
229
-                    $schema_route_data                = $data_for_multiple_endpoints['schema'];
230
-                    $schema_callback                  = $schema_route_data['schema_callback'];
231
-                    $callback_args                    = $schema_route_data['callback_args'];
232
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
233
-                        return call_user_func_array(
234
-                            $schema_callback,
235
-                            $callback_args
236
-                        );
237
-                    };
238
-                }
239
-                register_rest_route(
240
-                    $namespace,
241
-                    $relative_route,
242
-                    $multiple_endpoint_args
243
-                );
244
-            }
245
-        }
246
-    }
247
-
248
-
249
-    /**
250
-     * Checks if there was a version change or something that merits invalidating the cached
251
-     * route data. If so, invalidates the cached route data so that it gets refreshed
252
-     * next time the WP API is used
253
-     */
254
-    public static function invalidate_cached_route_data_on_version_change()
255
-    {
256
-        if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
257
-            EED_Core_Rest_Api::invalidate_cached_route_data();
258
-        }
259
-        foreach (EE_Registry::instance()->addons as $addon) {
260
-            if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
261
-                EED_Core_Rest_Api::invalidate_cached_route_data();
262
-            }
263
-        }
264
-    }
265
-
266
-
267
-    /**
268
-     * Removes the cached route data so it will get refreshed next time the WP API is used
269
-     */
270
-    public static function invalidate_cached_route_data()
271
-    {
272
-        // delete the saved EE REST API routes
273
-        foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
274
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
275
-        }
276
-    }
277
-
278
-
279
-    /**
280
-     * Gets the EE route data
281
-     *
282
-     * @return array top-level key is the namespace, next-level key is the route and its value is array{
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     * @type string|array $callback
286
-     * @type string       $methods
287
-     * @type boolean      $hidden_endpoint
288
-     * }
289
-     */
290
-    public static function get_ee_route_data(): array
291
-    {
292
-        $ee_routes = [];
293
-        foreach (self::versions_served() as $version => $hidden_endpoints) {
294
-            $ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
295
-                $version,
296
-                $hidden_endpoints
297
-            );
298
-        }
299
-        return $ee_routes;
300
-    }
301
-
302
-
303
-    /**
304
-     * Gets the EE route data from the wp options if it exists already,
305
-     * otherwise re-generates it and saves it to the option
306
-     *
307
-     * @param string  $version
308
-     * @param boolean $hidden_endpoints
309
-     * @return array
310
-     * @throws EE_Error
311
-     * @throws ReflectionException
312
-     */
313
-    protected static function _get_ee_route_data_for_version(string $version, bool $hidden_endpoints = false): array
314
-    {
315
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
316
-        if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
317
-            $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
318
-        }
319
-        return $ee_routes;
320
-    }
321
-
322
-
323
-    /**
324
-     * Saves the EE REST API route data to a wp option and returns it
325
-     *
326
-     * @param string  $version
327
-     * @param boolean $hidden_endpoints
328
-     * @return mixed|null
329
-     * @throws EE_Error
330
-     * @throws ReflectionException
331
-     */
332
-    protected static function _save_ee_route_data_for_version(string $version, bool $hidden_endpoints = false)
333
-    {
334
-        $instance    = self::instance();
335
-        $routes      = apply_filters(
336
-            'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
337
-            array_replace_recursive(
338
-                $instance->_get_config_route_data_for_version($version, $hidden_endpoints),
339
-                $instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
340
-                $instance->_get_model_route_data_for_version($version, $hidden_endpoints),
341
-                $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
342
-            )
343
-        );
344
-        $option_name = self::saved_routes_option_names . $version;
345
-        if (get_option($option_name)) {
346
-            update_option($option_name, $routes, true);
347
-        } else {
348
-            add_option($option_name, $routes, null, 'no');
349
-        }
350
-        return $routes;
351
-    }
352
-
353
-
354
-    /**
355
-     * Calculates all the EE routes and saves it to a WordPress option so we don't
356
-     * need to calculate it on every request
357
-     *
358
-     * @return void
359
-     * @throws EE_Error
360
-     * @throws ReflectionException
361
-     * @deprecated since version 4.9.1
362
-     */
363
-    public static function save_ee_routes()
364
-    {
365
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
366
-            $instance = self::instance();
367
-            $routes   = apply_filters(
368
-                'EED_Core_Rest_Api__save_ee_routes__routes',
369
-                array_replace_recursive(
370
-                    $instance->_register_config_routes(),
371
-                    $instance->_register_meta_routes(),
372
-                    $instance->_register_model_routes(),
373
-                    $instance->_register_rpc_routes()
374
-                )
375
-            );
376
-            update_option(self::saved_routes_option_names, $routes, true);
377
-        }
378
-    }
379
-
380
-
381
-    /**
382
-     * Gets all the route information relating to EE models
383
-     *
384
-     * @return array @see get_ee_route_data
385
-     * @deprecated since version 4.9.1
386
-     */
387
-    protected function _register_model_routes(): array
388
-    {
389
-        $model_routes = [];
390
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
391
-            $model_routes[ EED_Core_Rest_Api::ee_api_namespace
392
-                           . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
393
-        }
394
-        return $model_routes;
395
-    }
396
-
397
-
398
-    /**
399
-     * Decides whether or not to add write endpoints for this model.
400
-     *
401
-     * Currently, this defaults to exclude all global tables and models
402
-     * which would allow inserting WP core data (we don't want to duplicate
403
-     * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
404
-     *
405
-     * @param EEM_Base $model
406
-     * @return bool
407
-     */
408
-    public static function should_have_write_endpoints(EEM_Base $model): bool
409
-    {
410
-        if ($model->is_wp_core_model()) {
411
-            return false;
412
-        }
413
-        foreach ($model->get_tables() as $table) {
414
-            if ($table->is_global()) {
415
-                return false;
416
-            }
417
-        }
418
-        return true;
419
-    }
420
-
421
-
422
-    /**
423
-     * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
424
-     * in this versioned namespace of EE4
425
-     *
426
-     * @param $version
427
-     * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
428
-     */
429
-    public static function model_names_with_plural_routes($version): array
430
-    {
431
-        $model_version_info = new ModelVersionInfo($version);
432
-        $models_to_register = $model_version_info->modelsForRequestedVersion();
433
-        // let's not bother having endpoints for extra metas
434
-        unset(
435
-            $models_to_register['Extra_Meta'],
436
-            $models_to_register['Extra_Join'],
437
-            $models_to_register['Post_Meta']
438
-        );
439
-        return apply_filters(
440
-            'FHEE__EED_Core_REST_API___register_model_routes',
441
-            $models_to_register
442
-        );
443
-    }
444
-
445
-
446
-    /**
447
-     * Gets the route data for EE models in the specified version
448
-     *
449
-     * @param string  $version
450
-     * @param boolean $hidden_endpoint
451
-     * @return array
452
-     * @throws EE_Error
453
-     * @throws ReflectionException
454
-     */
455
-    protected function _get_model_route_data_for_version(string $version, bool $hidden_endpoint = false): array
456
-    {
457
-        $model_routes       = [];
458
-        $model_version_info = new ModelVersionInfo($version);
459
-        foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
460
-            $model = EE_Registry::instance()->load_model($model_name);
461
-            // if this isn't a valid model then let's skip iterate to the next item in the loop.
462
-            if (! $model instanceof EEM_Base) {
463
-                continue;
464
-            }
465
-            // yes we could just register one route for ALL models, but then they wouldn't show up in the index
466
-            $plural_model_route                    = EED_Core_Rest_Api::get_collection_route($model);
467
-            $singular_model_route                  = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
468
-            $model_routes[ $plural_model_route ]   = [
469
-                [
470
-                    'callback'        => [
471
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
472
-                        'handleRequestGetAll',
473
-                    ],
474
-                    'callback_args'   => [$version, $model_name],
475
-                    'methods'         => WP_REST_Server::READABLE,
476
-                    'hidden_endpoint' => $hidden_endpoint,
477
-                    'args'            => $this->_get_read_query_params($model, $version),
478
-                    '_links'          => [
479
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
480
-                    ],
481
-                ],
482
-                'schema' => [
483
-                    'schema_callback' => [
484
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
485
-                        'handleSchemaRequest',
486
-                    ],
487
-                    'callback_args'   => [$version, $model_name],
488
-                ],
489
-            ];
490
-            $model_routes[ $singular_model_route ] = [
491
-                [
492
-                    'callback'        => [
493
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
494
-                        'handleRequestGetOne',
495
-                    ],
496
-                    'callback_args'   => [$version, $model_name],
497
-                    'methods'         => WP_REST_Server::READABLE,
498
-                    'hidden_endpoint' => $hidden_endpoint,
499
-                    'args'            => $this->_get_response_selection_query_params($model, $version),
500
-                ],
501
-            ];
502
-            if (
503
-                apply_filters(
504
-                    'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
505
-                    EED_Core_Rest_Api::should_have_write_endpoints($model),
506
-                    $model
507
-                )
508
-            ) {
509
-                $model_routes[ $plural_model_route ][] = [
510
-                    'callback'        => [
511
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Write',
512
-                        'handleRequestInsert',
513
-                    ],
514
-                    'callback_args'   => [$version, $model_name],
515
-                    'methods'         => WP_REST_Server::CREATABLE,
516
-                    'hidden_endpoint' => $hidden_endpoint,
517
-                    'args'            => $this->_get_write_params($model_name, $model_version_info, true),
518
-                ];
519
-                $model_routes[ $singular_model_route ] = array_merge(
520
-                    $model_routes[ $singular_model_route ],
521
-                    [
522
-                        [
523
-                            'callback'        => [
524
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
525
-                                'handleRequestUpdate',
526
-                            ],
527
-                            'callback_args'   => [$version, $model_name],
528
-                            'methods'         => WP_REST_Server::EDITABLE,
529
-                            'hidden_endpoint' => $hidden_endpoint,
530
-                            'args'            => $this->_get_write_params($model_name, $model_version_info),
531
-                        ],
532
-                        [
533
-                            'callback'        => [
534
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
535
-                                'handleRequestDelete',
536
-                            ],
537
-                            'callback_args'   => [$version, $model_name],
538
-                            'methods'         => WP_REST_Server::DELETABLE,
539
-                            'hidden_endpoint' => $hidden_endpoint,
540
-                            'args'            => $this->_get_delete_query_params($model, $version),
541
-                        ],
542
-                    ]
543
-                );
544
-            }
545
-            foreach ($model->relation_settings() as $relation_name => $relation_obj) {
546
-                $related_route                  = EED_Core_Rest_Api::get_relation_route_via(
547
-                    $model,
548
-                    '(?P<id>[^\/]+)',
549
-                    $relation_obj
550
-                );
551
-                $model_routes[ $related_route ] = [
552
-                    [
553
-                        'callback'        => [
554
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Read',
555
-                            'handleRequestGetRelated',
556
-                        ],
557
-                        'callback_args'   => [$version, $model_name, $relation_name],
558
-                        'methods'         => WP_REST_Server::READABLE,
559
-                        'hidden_endpoint' => $hidden_endpoint,
560
-                        'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
561
-                    ],
562
-                ];
563
-
564
-                $related_write_route                  = $related_route . '/' . '(?P<related_id>[^\/]+)';
565
-                $model_routes[ $related_write_route ] = [
566
-                    [
567
-                        'callback'        => [
568
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Write',
569
-                            'handleRequestAddRelation',
570
-                        ],
571
-                        'callback_args'   => [$version, $model_name, $relation_name],
572
-                        'methods'         => WP_REST_Server::EDITABLE,
573
-                        'hidden_endpoint' => $hidden_endpoint,
574
-                        'args'            => $this->_get_add_relation_query_params(
575
-                            $model,
576
-                            $relation_obj->get_other_model(),
577
-                            $version
578
-                        ),
579
-                    ],
580
-                    [
581
-                        'callback'        => [
582
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Write',
583
-                            'handleRequestRemoveRelation',
584
-                        ],
585
-                        'callback_args'   => [$version, $model_name, $relation_name],
586
-                        'methods'         => WP_REST_Server::DELETABLE,
587
-                        'hidden_endpoint' => $hidden_endpoint,
588
-                        'args'            => [],
589
-                    ],
590
-                ];
591
-            }
592
-        }
593
-        return $model_routes;
594
-    }
595
-
596
-
597
-    /**
598
-     * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
599
-     * excluding the preceding slash.
600
-     * Eg you pass get_plural_route_to('Event') = 'events'
601
-     *
602
-     * @param EEM_Base $model
603
-     * @return string
604
-     */
605
-    public static function get_collection_route(EEM_Base $model): string
606
-    {
607
-        return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
608
-    }
609
-
610
-
611
-    /**
612
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
613
-     * excluding the preceding slash.
614
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
615
-     *
616
-     * @param EEM_Base   $model eg Event or Venue
617
-     * @param int|string $id
618
-     * @return string
619
-     */
620
-    public static function get_entity_route(EEM_Base $model, $id): string
621
-    {
622
-        return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
623
-    }
624
-
625
-
626
-    /**
627
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
628
-     * excluding the preceding slash.
629
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
630
-     *
631
-     * @param EEM_Base               $model eg Event or Venue
632
-     * @param int|string             $id
633
-     * @param EE_Model_Relation_Base $relation_obj
634
-     * @return string
635
-     */
636
-    public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj): string
637
-    {
638
-        $related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
639
-            $relation_obj->get_other_model()->get_this_model_name(),
640
-            $relation_obj
641
-        );
642
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
643
-    }
644
-
645
-
646
-    /**
647
-     * Adds onto the $relative_route the EE4 REST API versioned namespace.
648
-     * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
649
-     *
650
-     * @param string $relative_route
651
-     * @param string $version
652
-     * @return string
653
-     */
654
-    public static function get_versioned_route_to(string $relative_route, string $version = '4.8.36'): string
655
-    {
656
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
657
-    }
658
-
659
-
660
-    /**
661
-     * Adds all the RPC-style routes (remote procedure call-like routes, ie
662
-     * routes that don't conform to the traditional REST CRUD-style).
663
-     *
664
-     * @deprecated since 4.9.1
665
-     */
666
-    protected function _register_rpc_routes(): array
667
-    {
668
-        $routes = [];
669
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
670
-            $routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
671
-                $version,
672
-                $hidden_endpoint
673
-            );
674
-        }
675
-        return $routes;
676
-    }
677
-
678
-
679
-    /**
680
-     * @param string  $version
681
-     * @param boolean $hidden_endpoint
682
-     * @return array
683
-     */
684
-    protected function _get_rpc_route_data_for_version(string $version, bool $hidden_endpoint = false): array
685
-    {
686
-        $this_versions_routes = [];
687
-        // checkin endpoint
688
-        $this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = [
689
-            [
690
-                'callback'        => [
691
-                    'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
692
-                    'handleRequestToggleCheckin',
693
-                ],
694
-                'methods'         => WP_REST_Server::CREATABLE,
695
-                'hidden_endpoint' => $hidden_endpoint,
696
-                'args'            => [
697
-                    'force' => [
698
-                        'required'    => false,
699
-                        'default'     => false,
700
-                        'description' => esc_html__(
701
-                        // @codingStandardsIgnoreStart
702
-                            'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
703
-                            // @codingStandardsIgnoreEnd
704
-                            'event_espresso'
705
-                        ),
706
-                    ],
707
-                ],
708
-                'callback_args'   => [$version],
709
-            ],
710
-        ];
711
-        return apply_filters(
712
-            'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
713
-            $this_versions_routes,
714
-            $version,
715
-            $hidden_endpoint
716
-        );
717
-    }
718
-
719
-
720
-    /**
721
-     * Gets the query params that can be used when request one or many
722
-     *
723
-     * @param EEM_Base $model
724
-     * @param string   $version
725
-     * @return array
726
-     */
727
-    protected function _get_response_selection_query_params(EEM_Base $model, string $version): array
728
-    {
729
-        $query_params = [
730
-            'include'   => [
731
-                'required' => false,
732
-                'default'  => '*',
733
-                'type'     => 'string',
734
-            ],
735
-            'calculate' => [
736
-                'required'          => false,
737
-                'default'           => '',
738
-                'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
739
-                'type'              => 'string',
740
-                // because we accept a CSV list of the enumerated strings, WP core validation and sanitization
741
-                // freaks out. We'll just validate this argument while handling the request
742
-                'validate_callback' => null,
743
-                'sanitize_callback' => null,
744
-            ],
745
-            'password'  => [
746
-                'required' => false,
747
-                'default'  => '',
748
-                'type'     => 'string',
749
-            ],
750
-        ];
751
-        return apply_filters(
752
-            'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
753
-            $query_params,
754
-            $model,
755
-            $version
756
-        );
757
-    }
758
-
759
-
760
-    /**
761
-     * Gets the parameters acceptable for delete requests
762
-     *
763
-     * @param EEM_Base $model
764
-     * @param string   $version
765
-     * @return array
766
-     */
767
-    protected function _get_delete_query_params(EEM_Base $model, string $version): array
768
-    {
769
-        $params_for_delete          = [
770
-            'allow_blocking' => [
771
-                'required' => false,
772
-                'default'  => true,
773
-                'type'     => 'boolean',
774
-            ],
775
-        ];
776
-        $params_for_delete['force'] = [
777
-            'required' => false,
778
-            'default'  => false,
779
-            'type'     => 'boolean',
780
-        ];
781
-        return apply_filters(
782
-            'FHEE__EED_Core_Rest_Api___get_delete_query_params',
783
-            $params_for_delete,
784
-            $model,
785
-            $version
786
-        );
787
-    }
788
-
789
-
790
-    /**
791
-     * @param EEM_Base $source_model
792
-     * @param EEM_Base $related_model
793
-     * @param string   $version
794
-     * @return array
795
-     * @throws EE_Error
796
-     */
797
-    protected function _get_add_relation_query_params(
798
-        EEM_Base $source_model,
799
-        EEM_Base $related_model,
800
-        string $version
801
-    ): array {
802
-        // if they're related through a HABTM relation, check for any non-FKs
803
-        $all_relation_settings = $source_model->relation_settings();
804
-        $relation_settings     = $all_relation_settings[ $related_model->get_this_model_name() ];
805
-        $params                = [];
806
-        if ($relation_settings instanceof EE_HABTM_Relation && $relation_settings->hasNonKeyFields()) {
807
-            foreach ($relation_settings->getNonKeyFields() as $field) {
808
-                /* @var $field EE_Model_Field_Base */
809
-                $params[ $field->get_name() ] = [
810
-                    'required'          => ! $field->is_nullable(),
811
-                    'default'           => ModelDataTranslator::prepareFieldValueForJson(
812
-                        $field,
813
-                        $field->get_default_value(),
814
-                        $version
815
-                    ),
816
-                    'type'              => $field->getSchemaType(),
817
-                    'validate_callback' => null,
818
-                    'sanitize_callback' => null,
819
-                ];
820
-            }
821
-        }
822
-        return $params;
823
-    }
824
-
825
-
826
-    /**
827
-     * Gets info about reading query params that are acceptable
828
-     *
829
-     * @param EEM_Base $model eg 'Event' or 'Venue'
830
-     * @param string   $version
831
-     * @return array    describing the args acceptable when querying this model
832
-     * @throws EE_Error
833
-     */
834
-    protected function _get_read_query_params(EEM_Base $model, string $version): array
835
-    {
836
-        $default_orderby = [];
837
-        foreach ($model->get_combined_primary_key_fields() as $key_field) {
838
-            $default_orderby[ $key_field->get_name() ] = 'ASC';
839
-        }
840
-        return array_merge(
841
-            $this->_get_response_selection_query_params($model, $version),
842
-            [
843
-                'where'    => [
844
-                    'required'          => false,
845
-                    'default'           => [],
846
-                    'type'              => 'object',
847
-                    // because we accept an almost infinite list of possible where conditions, WP
848
-                    // core validation and sanitization freaks out. We'll just validate this argument
849
-                    // while handling the request
850
-                    'validate_callback' => null,
851
-                    'sanitize_callback' => null,
852
-                ],
853
-                'limit'    => [
854
-                    'required'          => false,
855
-                    'default'           => EED_Core_Rest_Api::get_default_query_limit(),
856
-                    'type'              => [
857
-                        'array',
858
-                        'string',
859
-                        'integer',
860
-                    ],
861
-                    // because we accept a variety of types, WP core validation and sanitization
862
-                    // freaks out. We'll just validate this argument while handling the request
863
-                    'validate_callback' => null,
864
-                    'sanitize_callback' => null,
865
-                ],
866
-                'order_by' => [
867
-                    'required'          => false,
868
-                    'default'           => $default_orderby,
869
-                    'type'              => [
870
-                        'object',
871
-                        'string',
872
-                    ],// because we accept a variety of types, WP core validation and sanitization
873
-                    // freaks out. We'll just validate this argument while handling the request
874
-                    'validate_callback' => null,
875
-                    'sanitize_callback' => null,
876
-                ],
877
-                'group_by' => [
878
-                    'required'          => false,
879
-                    'default'           => null,
880
-                    'type'              => [
881
-                        'object',
882
-                        'string',
883
-                    ],
884
-                    // because we accept  an almost infinite list of possible groupings,
885
-                    // WP core validation and sanitization
886
-                    // freaks out. We'll just validate this argument while handling the request
887
-                    'validate_callback' => null,
888
-                    'sanitize_callback' => null,
889
-                ],
890
-                'having'   => [
891
-                    'required'          => false,
892
-                    'default'           => null,
893
-                    'type'              => 'object',
894
-                    // because we accept an almost infinite list of possible where conditions, WP
895
-                    // core validation and sanitization freaks out. We'll just validate this argument
896
-                    // while handling the request
897
-                    'validate_callback' => null,
898
-                    'sanitize_callback' => null,
899
-                ],
900
-                'caps'     => [
901
-                    'required' => false,
902
-                    'default'  => EEM_Base::caps_read,
903
-                    'type'     => 'string',
904
-                    'enum'     => [
905
-                        EEM_Base::caps_read,
906
-                        EEM_Base::caps_read_admin,
907
-                        EEM_Base::caps_edit,
908
-                        EEM_Base::caps_delete,
909
-                    ],
910
-                ],
911
-            ]
912
-        );
913
-    }
914
-
915
-
916
-    /**
917
-     * Gets parameter information for a model regarding writing data
918
-     *
919
-     * @param string           $model_name
920
-     * @param ModelVersionInfo $model_version_info
921
-     * @param boolean          $create                                       whether this is for request to create (in
922
-     *                                                                       which case we need all required params) or
923
-     *                                                                       just to update (in which case we don't
924
-     *                                                                       need those on every request)
925
-     * @return array
926
-     * @throws EE_Error
927
-     * @throws ReflectionException
928
-     */
929
-    protected function _get_write_params(
930
-        string $model_name,
931
-        ModelVersionInfo $model_version_info,
932
-        bool $create = false
933
-    ): array {
934
-        $model     = EE_Registry::instance()->load_model($model_name);
935
-        $fields    = $model_version_info->fieldsOnModelInThisVersion($model);
936
-        $args_info = [];
937
-        foreach ($fields as $field_name => $field_obj) {
938
-            if ($field_obj->is_auto_increment()) {
939
-                // totally ignore auto increment IDs
940
-                continue;
941
-            }
942
-            $arg_info             = $field_obj->getSchema();
943
-            $required             = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
944
-            $arg_info['required'] = $required;
945
-            // remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
946
-            unset($arg_info['readonly']);
947
-            $schema_properties = $field_obj->getSchemaProperties();
948
-            if (
949
-                isset($schema_properties['raw'])
950
-                && $field_obj->getSchemaType() === 'object'
951
-            ) {
952
-                // if there's a "raw" form of this argument, use those properties instead
953
-                $arg_info = array_replace(
954
-                    $arg_info,
955
-                    $schema_properties['raw']
956
-                );
957
-            }
958
-            $arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
959
-                $field_obj,
960
-                $field_obj->get_default_value(),
961
-                $model_version_info->requestedVersion()
962
-            );
963
-            // we do our own validation and sanitization within the controller
964
-            if (function_exists('rest_validate_value_from_schema')) {
965
-                $sanitize_callback = [
966
-                    'EED_Core_Rest_Api',
967
-                    'default_sanitize_callback',
968
-                ];
969
-            } else {
970
-                $sanitize_callback = null;
971
-            }
972
-            $arg_info['sanitize_callback'] = $sanitize_callback;
973
-            $args_info[ $field_name ]      = $arg_info;
974
-            if ($field_obj instanceof EE_Datetime_Field) {
975
-                $gmt_arg_info                      = $arg_info;
976
-                $gmt_arg_info['description']       = sprintf(
977
-                    esc_html__(
978
-                        '%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
979
-                        'event_espresso'
980
-                    ),
981
-                    $field_obj->get_nicename(),
982
-                    $field_name
983
-                );
984
-                $args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
985
-            }
986
-        }
987
-        return $args_info;
988
-    }
989
-
990
-
991
-    /**
992
-     * Replacement for WP API's 'rest_parse_request_arg'.
993
-     * If the value is blank but not required, don't bother validating it.
994
-     * Also, it uses our email validation instead of WP API's default.
995
-     *
996
-     * @param                 $value
997
-     * @param WP_REST_Request $request
998
-     * @param                 $param
999
-     * @return bool|true|WP_Error
1000
-     * @throws InvalidArgumentException
1001
-     * @throws InvalidInterfaceException
1002
-     * @throws InvalidDataTypeException
1003
-     */
1004
-    public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
1005
-    {
1006
-        $attributes = $request->get_attributes();
1007
-        if (
1008
-            ! isset($attributes['args'][ $param ])
1009
-            || ! is_array($attributes['args'][ $param ])
1010
-        ) {
1011
-            $validation_result = true;
1012
-        } else {
1013
-            $args = $attributes['args'][ $param ];
1014
-            if (
1015
-                (
1016
-                    $value === ''
1017
-                    || $value === null
1018
-                )
1019
-                && (! isset($args['required'])
1020
-                    || $args['required'] === false
1021
-                )
1022
-            ) {
1023
-                // not required and not provided? that's cool
1024
-                $validation_result = true;
1025
-            } elseif (
1026
-                isset($args['format'])
1027
-                && $args['format'] === 'email'
1028
-            ) {
1029
-                $validation_result = true;
1030
-                if (! self::_validate_email($value)) {
1031
-                    $validation_result = new WP_Error(
1032
-                        'rest_invalid_param',
1033
-                        esc_html__(
1034
-                            'The email address is not valid or does not exist.',
1035
-                            'event_espresso'
1036
-                        )
1037
-                    );
1038
-                }
1039
-            } else {
1040
-                $validation_result = rest_validate_value_from_schema($value, $args, $param);
1041
-            }
1042
-        }
1043
-        if (is_wp_error($validation_result)) {
1044
-            return $validation_result;
1045
-        }
1046
-        return rest_sanitize_request_arg($value, $request, $param);
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
1052
-     *
1053
-     * @param $email
1054
-     * @return bool
1055
-     * @throws InvalidArgumentException
1056
-     * @throws InvalidInterfaceException
1057
-     * @throws InvalidDataTypeException
1058
-     */
1059
-    protected static function _validate_email($email): bool
1060
-    {
1061
-        try {
1062
-            EmailAddressFactory::create($email);
1063
-            return true;
1064
-        } catch (EmailValidationException $e) {
1065
-            return false;
1066
-        }
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * Gets routes for the config
1072
-     *
1073
-     * @return array @see _register_model_routes
1074
-     * @deprecated since version 4.9.1
1075
-     */
1076
-    protected function _register_config_routes(): array
1077
-    {
1078
-        $config_routes = [];
1079
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1080
-            $config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
1081
-                $version,
1082
-                $hidden_endpoint
1083
-            );
1084
-        }
1085
-        return $config_routes;
1086
-    }
1087
-
1088
-
1089
-    /**
1090
-     * Gets routes for the config for the specified version
1091
-     *
1092
-     * @param string  $version
1093
-     * @param boolean $hidden_endpoint
1094
-     * @return array
1095
-     */
1096
-    protected function _get_config_route_data_for_version(string $version, bool $hidden_endpoint): array
1097
-    {
1098
-        return [
1099
-            'config'    => [
1100
-                [
1101
-                    'callback'        => [
1102
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1103
-                        'handleRequest',
1104
-                    ],
1105
-                    'methods'         => WP_REST_Server::READABLE,
1106
-                    'hidden_endpoint' => $hidden_endpoint,
1107
-                    'callback_args'   => [$version],
1108
-                ],
1109
-            ],
1110
-            'site_info' => [
1111
-                [
1112
-                    'callback'        => [
1113
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1114
-                        'handleRequestSiteInfo',
1115
-                    ],
1116
-                    'methods'         => WP_REST_Server::READABLE,
1117
-                    'hidden_endpoint' => $hidden_endpoint,
1118
-                    'callback_args'   => [$version],
1119
-                ],
1120
-            ],
1121
-        ];
1122
-    }
1123
-
1124
-
1125
-    /**
1126
-     * Gets the meta info routes
1127
-     *
1128
-     * @return array @see _register_model_routes
1129
-     * @deprecated since version 4.9.1
1130
-     */
1131
-    protected function _register_meta_routes(): array
1132
-    {
1133
-        $meta_routes = [];
1134
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1135
-            $meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1136
-                $version,
1137
-                $hidden_endpoint
1138
-            );
1139
-        }
1140
-        return $meta_routes;
1141
-    }
1142
-
1143
-
1144
-    /**
1145
-     * @param string  $version
1146
-     * @param boolean $hidden_endpoint
1147
-     * @return array
1148
-     */
1149
-    protected function _get_meta_route_data_for_version(string $version, bool $hidden_endpoint = false): array
1150
-    {
1151
-        return [
1152
-            'resources' => [
1153
-                [
1154
-                    'callback'        => [
1155
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1156
-                        'handleRequestModelsMeta',
1157
-                    ],
1158
-                    'methods'         => WP_REST_Server::READABLE,
1159
-                    'hidden_endpoint' => $hidden_endpoint,
1160
-                    'callback_args'   => [$version],
1161
-                ],
1162
-            ],
1163
-        ];
1164
-    }
1165
-
1166
-
1167
-    /**
1168
-     * Tries to hide old 4.6 endpoints from the
1169
-     *
1170
-     * @param array $route_data
1171
-     * @return array
1172
-     * @throws EE_Error
1173
-     * @throws ReflectionException
1174
-     */
1175
-    public static function hide_old_endpoints(array $route_data): array
1176
-    {
1177
-        // allow API clients to override which endpoints get hidden, in case
1178
-        // they want to discover particular endpoints
1179
-        // also, we don't have access to the request so we have to just grab it from the super global
1180
-        $force_show_ee_namespace = ltrim(
1181
-            EED_Core_Rest_Api::getRequest()->getRequestParam('force_show_ee_namespace'),
1182
-            '/'
1183
-        );
1184
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1185
-            foreach ($relative_urls as $resource_name => $endpoints) {
1186
-                foreach ($endpoints as $key => $endpoint) {
1187
-                    // skip schema and other route options
1188
-                    if (! is_numeric($key)) {
1189
-                        continue;
1190
-                    }
1191
-                    // by default, hide "hidden_endpoint"s, unless the request indicates
1192
-                    // to $force_show_ee_namespace, in which case only show that one
1193
-                    // namespace's endpoints (and hide all others)
1194
-                    if (
1195
-                        ($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1196
-                        || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1197
-                    ) {
1198
-                        $full_route = '/' . ltrim($namespace, '/');
1199
-                        $full_route .= '/' . ltrim($resource_name, '/');
1200
-                        unset($route_data[ $full_route ]);
1201
-                    }
1202
-                }
1203
-            }
1204
-        }
1205
-        return $route_data;
1206
-    }
1207
-
1208
-
1209
-    /**
1210
-     * Returns an array describing which versions of core support serving requests for.
1211
-     * Keys are core versions' major and minor version, and values are the
1212
-     * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1213
-     * data by just removing a few models and fields from the responses. However, 4.15 might remove
1214
-     * the answers table entirely, in which case it would be very difficult for
1215
-     * it to serve 4.6-style responses.
1216
-     * Versions of core that are missing from this array are unknowns.
1217
-     * previous ver
1218
-     *
1219
-     * @return array
1220
-     */
1221
-    public static function version_compatibilities(): array
1222
-    {
1223
-        return apply_filters(
1224
-            'FHEE__EED_Core_REST_API__version_compatibilities',
1225
-            [
1226
-                '4.8.29' => '4.8.29',
1227
-                '4.8.33' => '4.8.29',
1228
-                '4.8.34' => '4.8.29',
1229
-                '4.8.36' => '4.8.29',
1230
-            ]
1231
-        );
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Gets the latest API version served. Eg if there
1237
-     * are two versions served of the API, 4.8.29 and 4.8.32, and
1238
-     * we are on core version 4.8.34, it will return the string "4.8.32"
1239
-     *
1240
-     * @return string
1241
-     */
1242
-    public static function latest_rest_api_version(): string
1243
-    {
1244
-        $versions_served      = EED_Core_Rest_Api::versions_served();
1245
-        $versions_served_keys = array_keys($versions_served);
1246
-        return end($versions_served_keys);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1252
-     * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1253
-     * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1254
-     * We also indicate whether or not this version should be put in the index or not
1255
-     *
1256
-     * @return array keys are API version numbers (just major and minor numbers), and values
1257
-     * are whether or not they should be hidden
1258
-     */
1259
-    public static function versions_served(): array
1260
-    {
1261
-        $versions_served           = [];
1262
-        $possibly_served_versions  = EED_Core_Rest_Api::version_compatibilities();
1263
-        $lowest_compatible_version = end($possibly_served_versions);
1264
-        reset($possibly_served_versions);
1265
-        $versions_served_historically = array_keys($possibly_served_versions);
1266
-        $latest_version               = end($versions_served_historically);
1267
-        reset($versions_served_historically);
1268
-        // for each version of core we have ever served:
1269
-        foreach ($versions_served_historically as $key_versioned_endpoint) {
1270
-            // if it's not above the current core version, and it's compatible with the current version of core
1271
-
1272
-            if ($key_versioned_endpoint === $latest_version) {
1273
-                // don't hide the latest version in the index
1274
-                $versions_served[ $key_versioned_endpoint ] = false;
1275
-            } elseif (
1276
-                version_compare($key_versioned_endpoint, $lowest_compatible_version, '>=')
1277
-                && version_compare($key_versioned_endpoint, EED_Core_Rest_Api::core_version(), '<')
1278
-            ) {
1279
-                // include, but hide, previous versions which are still supported
1280
-                $versions_served[ $key_versioned_endpoint ] = true;
1281
-            } elseif (
1282
-                apply_filters(
1283
-                    'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1284
-                    false,
1285
-                    $possibly_served_versions
1286
-                )
1287
-            ) {
1288
-                // if a version is no longer supported, don't include it in index or list of versions served
1289
-                $versions_served[ $key_versioned_endpoint ] = true;
1290
-            }
1291
-        }
1292
-        return $versions_served;
1293
-    }
1294
-
1295
-
1296
-    /**
1297
-     * Gets the major and minor version of EE core's version string
1298
-     *
1299
-     * @return string
1300
-     */
1301
-    public static function core_version(): string
1302
-    {
1303
-        return apply_filters(
1304
-            'FHEE__EED_Core_REST_API__core_version',
1305
-            implode(
1306
-                '.',
1307
-                array_slice(
1308
-                    explode(
1309
-                        '.',
1310
-                        espresso_version()
1311
-                    ),
1312
-                    0,
1313
-                    3
1314
-                )
1315
-            )
1316
-        );
1317
-    }
1318
-
1319
-
1320
-    /**
1321
-     * Gets the default limit that should be used when querying for resources
1322
-     *
1323
-     * @return int
1324
-     */
1325
-    public static function get_default_query_limit(): int
1326
-    {
1327
-        // we actually don't use a const because we want folks to always use
1328
-        // this method, not the const directly
1329
-        return apply_filters(
1330
-            'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1331
-            50
1332
-        );
1333
-    }
1334
-
1335
-
1336
-    /**
1337
-     *
1338
-     * @param string $version api version string (i.e. '4.8.36')
1339
-     * @return array
1340
-     */
1341
-    public static function getCollectionRoutesIndexedByModelName(string $version = ''): array
1342
-    {
1343
-        $version           = empty($version) ? self::latest_rest_api_version() : $version;
1344
-        $model_names       = self::model_names_with_plural_routes($version);
1345
-        $collection_routes = [];
1346
-        foreach ($model_names as $model_name => $model_class_name) {
1347
-            $collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1348
-                                                            . EEH_Inflector::pluralize_and_lower($model_name);
1349
-        }
1350
-        return $collection_routes;
1351
-    }
1352
-
1353
-
1354
-    /**
1355
-     * Returns an array of primary key names indexed by model names.
1356
-     *
1357
-     * @param string $version
1358
-     * @return array
1359
-     */
1360
-    public static function getPrimaryKeyNamesIndexedByModelName(string $version = ''): array
1361
-    {
1362
-        $version           = empty($version) ? self::latest_rest_api_version() : $version;
1363
-        $model_names       = self::model_names_with_plural_routes($version);
1364
-        $primary_key_items = [];
1365
-        foreach ($model_names as $model_name => $model_class_name) {
1366
-            $primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1367
-            foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1368
-                if (count($primary_keys) > 1) {
1369
-                    $primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1370
-                } else {
1371
-                    $primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1372
-                }
1373
-            }
1374
-        }
1375
-        return $primary_key_items;
1376
-    }
1377
-
1378
-
1379
-    /**
1380
-     * Determines the EE REST API debug mode is activated, or not.
1381
-     *
1382
-     * @return bool
1383
-     * @since 4.9.76.p
1384
-     */
1385
-    public static function debugMode(): ?bool
1386
-    {
1387
-        static $debug_mode = null; // could be class prop
1388
-        if ($debug_mode === null) {
1389
-            $debug_mode = defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE;
1390
-        }
1391
-        return $debug_mode;
1392
-    }
1393
-
1394
-
1395
-    /**
1396
-     *    run - initial module setup
1397
-     *
1398
-     * @access    public
1399
-     * @param WP $WP
1400
-     * @return    void
1401
-     */
1402
-    public function run($WP)
1403
-    {
1404
-    }
24
+	const ee_api_namespace           = Domain::API_NAMESPACE;
25
+
26
+	const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
27
+
28
+	const saved_routes_option_names  = 'ee_core_routes';
29
+
30
+	/**
31
+	 * string used in _links response bodies to make them globally unique.
32
+	 *
33
+	 * @see http://v2.wp-api.org/extending/linking/
34
+	 */
35
+	const ee_api_link_namespace = 'https://api.eventespresso.com/';
36
+
37
+	/**
38
+	 * @var CalculatedModelFields
39
+	 */
40
+	protected static $_field_calculator;
41
+
42
+
43
+	/**
44
+	 * @return EED_Core_Rest_Api|EED_Module
45
+	 * @throws EE_Error
46
+	 * @throws ReflectionException
47
+	 */
48
+	public static function instance()
49
+	{
50
+		self::$_field_calculator =
51
+			LoaderFactory::getLoader()->load('EventEspresso\core\libraries\rest_api\CalculatedModelFields');
52
+		return parent::get_instance(__CLASS__);
53
+	}
54
+
55
+
56
+	/**
57
+	 *    set_hooks - for hooking into EE Core, other modules, etc
58
+	 *
59
+	 * @access    public
60
+	 * @return    void
61
+	 */
62
+	public static function set_hooks()
63
+	{
64
+		self::set_hooks_both();
65
+	}
66
+
67
+
68
+	/**
69
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
70
+	 *
71
+	 * @access    public
72
+	 * @return    void
73
+	 */
74
+	public static function set_hooks_admin()
75
+	{
76
+		self::set_hooks_both();
77
+	}
78
+
79
+
80
+	public static function set_hooks_both()
81
+	{
82
+		add_action('rest_api_init', ['EED_Core_Rest_Api', 'set_hooks_rest_api'], 5);
83
+		add_action('rest_api_init', ['EED_Core_Rest_Api', 'register_routes'], 10);
84
+		add_filter('rest_route_data', ['EED_Core_Rest_Api', 'hide_old_endpoints'], 10, 2);
85
+		add_filter(
86
+			'rest_index',
87
+			['EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex']
88
+		);
89
+		EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
90
+	}
91
+
92
+
93
+	/**
94
+	 * sets up hooks which only need to be included as part of REST API requests;
95
+	 * other requests like to the frontend or admin etc don't need them
96
+	 *
97
+	 * @throws EE_Error
98
+	 */
99
+	public static function set_hooks_rest_api()
100
+	{
101
+		// set hooks which account for changes made to the API
102
+		EED_Core_Rest_Api::_set_hooks_for_changes();
103
+	}
104
+
105
+
106
+	/**
107
+	 * public wrapper of _set_hooks_for_changes.
108
+	 * Loads all the hooks which make requests to old versions of the API
109
+	 * appear the same as they always did
110
+	 *
111
+	 * @throws EE_Error
112
+	 */
113
+	public static function set_hooks_for_changes()
114
+	{
115
+		self::_set_hooks_for_changes();
116
+	}
117
+
118
+
119
+	/**
120
+	 * Loads all the hooks which make requests to old versions of the API
121
+	 * appear the same as they always did
122
+	 *
123
+	 * @throws EE_Error
124
+	 */
125
+	protected static function _set_hooks_for_changes()
126
+	{
127
+		$folder_contents = EEH_File::get_contents_of_folders([EE_LIBRARIES . 'rest_api/changes']);
128
+		foreach ($folder_contents as $classname_in_namespace => $filepath) {
129
+			// ignore the base parent class
130
+			// and legacy named classes
131
+			if (
132
+				$classname_in_namespace === 'ChangesInBase'
133
+				|| strpos($classname_in_namespace, 'Changes_In_') === 0
134
+			) {
135
+				continue;
136
+			}
137
+			$full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
138
+			if (class_exists($full_classname)) {
139
+				$instance_of_class = new $full_classname();
140
+				if ($instance_of_class instanceof ChangesInBase) {
141
+					$instance_of_class->setHooks();
142
+				}
143
+			}
144
+		}
145
+	}
146
+
147
+
148
+	/**
149
+	 * Filters the WP routes to add our EE-related ones. This takes a bit of time
150
+	 * so we actually prefer to only do it when an EE plugin is activated or upgraded
151
+	 *
152
+	 * @throws EE_Error
153
+	 * @throws ReflectionException
154
+	 */
155
+	public static function register_routes()
156
+	{
157
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
158
+			foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
159
+				/**
160
+				 * @var array     $data_for_multiple_endpoints numerically indexed array
161
+				 *                                         but can also contain route options like {
162
+				 * @type array    $schema                      {
163
+				 * @type callable $schema_callback
164
+				 * @type array    $callback_args               arguments that will be passed to the callback, after the
165
+				 * WP_REST_Request of course
166
+				 * }
167
+				 * }
168
+				 */
169
+				// when registering routes, register all the endpoints' data at the same time
170
+				$multiple_endpoint_args = [];
171
+				foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
172
+					/**
173
+					 * @var array     $data_for_single_endpoint {
174
+					 * @type callable $callback
175
+					 * @type string methods
176
+					 * @type array args
177
+					 * @type array _links
178
+					 * @type array    $callback_args            arguments that will be passed to the callback, after the
179
+					 * WP_REST_Request of course
180
+					 * }
181
+					 */
182
+					// skip route options
183
+					if (! is_numeric($endpoint_key)) {
184
+						continue;
185
+					}
186
+					if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
187
+						throw new EE_Error(
188
+							esc_html__(
189
+							// @codingStandardsIgnoreStart
190
+								'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
191
+								// @codingStandardsIgnoreEnd
192
+								'event_espresso'
193
+							)
194
+						);
195
+					}
196
+					$callback             = $data_for_single_endpoint['callback'];
197
+					$single_endpoint_args = [
198
+						'methods' => $data_for_single_endpoint['methods'],
199
+						'args'    => $data_for_single_endpoint['args'] ?? [],
200
+					];
201
+					if (isset($data_for_single_endpoint['_links'])) {
202
+						$single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
203
+					}
204
+					if (isset($data_for_single_endpoint['callback_args'])) {
205
+						$callback_args                    = $data_for_single_endpoint['callback_args'];
206
+						$single_endpoint_args['callback'] = function (WP_REST_Request $request) use (
207
+							$callback,
208
+							$callback_args
209
+						) {
210
+							array_unshift($callback_args, $request);
211
+							return call_user_func_array(
212
+								$callback,
213
+								$callback_args
214
+							);
215
+						};
216
+					} else {
217
+						$single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
218
+					}
219
+					// As of WordPress 5.5, if a permission_callback is not provided,
220
+					// the REST API will issue a _doing_it_wrong notice.
221
+					// Since the EE REST API defers capabilities to the db model system,
222
+					// we will just use the generic WP callback for public endpoints
223
+					if (! isset($single_endpoint_args['permission_callback'])) {
224
+						$single_endpoint_args['permission_callback'] = '__return_true';
225
+					}
226
+					$multiple_endpoint_args[] = $single_endpoint_args;
227
+				}
228
+				if (isset($data_for_multiple_endpoints['schema'])) {
229
+					$schema_route_data                = $data_for_multiple_endpoints['schema'];
230
+					$schema_callback                  = $schema_route_data['schema_callback'];
231
+					$callback_args                    = $schema_route_data['callback_args'];
232
+					$multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
233
+						return call_user_func_array(
234
+							$schema_callback,
235
+							$callback_args
236
+						);
237
+					};
238
+				}
239
+				register_rest_route(
240
+					$namespace,
241
+					$relative_route,
242
+					$multiple_endpoint_args
243
+				);
244
+			}
245
+		}
246
+	}
247
+
248
+
249
+	/**
250
+	 * Checks if there was a version change or something that merits invalidating the cached
251
+	 * route data. If so, invalidates the cached route data so that it gets refreshed
252
+	 * next time the WP API is used
253
+	 */
254
+	public static function invalidate_cached_route_data_on_version_change()
255
+	{
256
+		if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
257
+			EED_Core_Rest_Api::invalidate_cached_route_data();
258
+		}
259
+		foreach (EE_Registry::instance()->addons as $addon) {
260
+			if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
261
+				EED_Core_Rest_Api::invalidate_cached_route_data();
262
+			}
263
+		}
264
+	}
265
+
266
+
267
+	/**
268
+	 * Removes the cached route data so it will get refreshed next time the WP API is used
269
+	 */
270
+	public static function invalidate_cached_route_data()
271
+	{
272
+		// delete the saved EE REST API routes
273
+		foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
274
+			delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
275
+		}
276
+	}
277
+
278
+
279
+	/**
280
+	 * Gets the EE route data
281
+	 *
282
+	 * @return array top-level key is the namespace, next-level key is the route and its value is array{
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 * @type string|array $callback
286
+	 * @type string       $methods
287
+	 * @type boolean      $hidden_endpoint
288
+	 * }
289
+	 */
290
+	public static function get_ee_route_data(): array
291
+	{
292
+		$ee_routes = [];
293
+		foreach (self::versions_served() as $version => $hidden_endpoints) {
294
+			$ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
295
+				$version,
296
+				$hidden_endpoints
297
+			);
298
+		}
299
+		return $ee_routes;
300
+	}
301
+
302
+
303
+	/**
304
+	 * Gets the EE route data from the wp options if it exists already,
305
+	 * otherwise re-generates it and saves it to the option
306
+	 *
307
+	 * @param string  $version
308
+	 * @param boolean $hidden_endpoints
309
+	 * @return array
310
+	 * @throws EE_Error
311
+	 * @throws ReflectionException
312
+	 */
313
+	protected static function _get_ee_route_data_for_version(string $version, bool $hidden_endpoints = false): array
314
+	{
315
+		$ee_routes = get_option(self::saved_routes_option_names . $version, null);
316
+		if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
317
+			$ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
318
+		}
319
+		return $ee_routes;
320
+	}
321
+
322
+
323
+	/**
324
+	 * Saves the EE REST API route data to a wp option and returns it
325
+	 *
326
+	 * @param string  $version
327
+	 * @param boolean $hidden_endpoints
328
+	 * @return mixed|null
329
+	 * @throws EE_Error
330
+	 * @throws ReflectionException
331
+	 */
332
+	protected static function _save_ee_route_data_for_version(string $version, bool $hidden_endpoints = false)
333
+	{
334
+		$instance    = self::instance();
335
+		$routes      = apply_filters(
336
+			'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
337
+			array_replace_recursive(
338
+				$instance->_get_config_route_data_for_version($version, $hidden_endpoints),
339
+				$instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
340
+				$instance->_get_model_route_data_for_version($version, $hidden_endpoints),
341
+				$instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
342
+			)
343
+		);
344
+		$option_name = self::saved_routes_option_names . $version;
345
+		if (get_option($option_name)) {
346
+			update_option($option_name, $routes, true);
347
+		} else {
348
+			add_option($option_name, $routes, null, 'no');
349
+		}
350
+		return $routes;
351
+	}
352
+
353
+
354
+	/**
355
+	 * Calculates all the EE routes and saves it to a WordPress option so we don't
356
+	 * need to calculate it on every request
357
+	 *
358
+	 * @return void
359
+	 * @throws EE_Error
360
+	 * @throws ReflectionException
361
+	 * @deprecated since version 4.9.1
362
+	 */
363
+	public static function save_ee_routes()
364
+	{
365
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
366
+			$instance = self::instance();
367
+			$routes   = apply_filters(
368
+				'EED_Core_Rest_Api__save_ee_routes__routes',
369
+				array_replace_recursive(
370
+					$instance->_register_config_routes(),
371
+					$instance->_register_meta_routes(),
372
+					$instance->_register_model_routes(),
373
+					$instance->_register_rpc_routes()
374
+				)
375
+			);
376
+			update_option(self::saved_routes_option_names, $routes, true);
377
+		}
378
+	}
379
+
380
+
381
+	/**
382
+	 * Gets all the route information relating to EE models
383
+	 *
384
+	 * @return array @see get_ee_route_data
385
+	 * @deprecated since version 4.9.1
386
+	 */
387
+	protected function _register_model_routes(): array
388
+	{
389
+		$model_routes = [];
390
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
391
+			$model_routes[ EED_Core_Rest_Api::ee_api_namespace
392
+						   . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
393
+		}
394
+		return $model_routes;
395
+	}
396
+
397
+
398
+	/**
399
+	 * Decides whether or not to add write endpoints for this model.
400
+	 *
401
+	 * Currently, this defaults to exclude all global tables and models
402
+	 * which would allow inserting WP core data (we don't want to duplicate
403
+	 * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
404
+	 *
405
+	 * @param EEM_Base $model
406
+	 * @return bool
407
+	 */
408
+	public static function should_have_write_endpoints(EEM_Base $model): bool
409
+	{
410
+		if ($model->is_wp_core_model()) {
411
+			return false;
412
+		}
413
+		foreach ($model->get_tables() as $table) {
414
+			if ($table->is_global()) {
415
+				return false;
416
+			}
417
+		}
418
+		return true;
419
+	}
420
+
421
+
422
+	/**
423
+	 * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
424
+	 * in this versioned namespace of EE4
425
+	 *
426
+	 * @param $version
427
+	 * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
428
+	 */
429
+	public static function model_names_with_plural_routes($version): array
430
+	{
431
+		$model_version_info = new ModelVersionInfo($version);
432
+		$models_to_register = $model_version_info->modelsForRequestedVersion();
433
+		// let's not bother having endpoints for extra metas
434
+		unset(
435
+			$models_to_register['Extra_Meta'],
436
+			$models_to_register['Extra_Join'],
437
+			$models_to_register['Post_Meta']
438
+		);
439
+		return apply_filters(
440
+			'FHEE__EED_Core_REST_API___register_model_routes',
441
+			$models_to_register
442
+		);
443
+	}
444
+
445
+
446
+	/**
447
+	 * Gets the route data for EE models in the specified version
448
+	 *
449
+	 * @param string  $version
450
+	 * @param boolean $hidden_endpoint
451
+	 * @return array
452
+	 * @throws EE_Error
453
+	 * @throws ReflectionException
454
+	 */
455
+	protected function _get_model_route_data_for_version(string $version, bool $hidden_endpoint = false): array
456
+	{
457
+		$model_routes       = [];
458
+		$model_version_info = new ModelVersionInfo($version);
459
+		foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
460
+			$model = EE_Registry::instance()->load_model($model_name);
461
+			// if this isn't a valid model then let's skip iterate to the next item in the loop.
462
+			if (! $model instanceof EEM_Base) {
463
+				continue;
464
+			}
465
+			// yes we could just register one route for ALL models, but then they wouldn't show up in the index
466
+			$plural_model_route                    = EED_Core_Rest_Api::get_collection_route($model);
467
+			$singular_model_route                  = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
468
+			$model_routes[ $plural_model_route ]   = [
469
+				[
470
+					'callback'        => [
471
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
472
+						'handleRequestGetAll',
473
+					],
474
+					'callback_args'   => [$version, $model_name],
475
+					'methods'         => WP_REST_Server::READABLE,
476
+					'hidden_endpoint' => $hidden_endpoint,
477
+					'args'            => $this->_get_read_query_params($model, $version),
478
+					'_links'          => [
479
+						'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
480
+					],
481
+				],
482
+				'schema' => [
483
+					'schema_callback' => [
484
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
485
+						'handleSchemaRequest',
486
+					],
487
+					'callback_args'   => [$version, $model_name],
488
+				],
489
+			];
490
+			$model_routes[ $singular_model_route ] = [
491
+				[
492
+					'callback'        => [
493
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
494
+						'handleRequestGetOne',
495
+					],
496
+					'callback_args'   => [$version, $model_name],
497
+					'methods'         => WP_REST_Server::READABLE,
498
+					'hidden_endpoint' => $hidden_endpoint,
499
+					'args'            => $this->_get_response_selection_query_params($model, $version),
500
+				],
501
+			];
502
+			if (
503
+				apply_filters(
504
+					'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
505
+					EED_Core_Rest_Api::should_have_write_endpoints($model),
506
+					$model
507
+				)
508
+			) {
509
+				$model_routes[ $plural_model_route ][] = [
510
+					'callback'        => [
511
+						'EventEspresso\core\libraries\rest_api\controllers\model\Write',
512
+						'handleRequestInsert',
513
+					],
514
+					'callback_args'   => [$version, $model_name],
515
+					'methods'         => WP_REST_Server::CREATABLE,
516
+					'hidden_endpoint' => $hidden_endpoint,
517
+					'args'            => $this->_get_write_params($model_name, $model_version_info, true),
518
+				];
519
+				$model_routes[ $singular_model_route ] = array_merge(
520
+					$model_routes[ $singular_model_route ],
521
+					[
522
+						[
523
+							'callback'        => [
524
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
525
+								'handleRequestUpdate',
526
+							],
527
+							'callback_args'   => [$version, $model_name],
528
+							'methods'         => WP_REST_Server::EDITABLE,
529
+							'hidden_endpoint' => $hidden_endpoint,
530
+							'args'            => $this->_get_write_params($model_name, $model_version_info),
531
+						],
532
+						[
533
+							'callback'        => [
534
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
535
+								'handleRequestDelete',
536
+							],
537
+							'callback_args'   => [$version, $model_name],
538
+							'methods'         => WP_REST_Server::DELETABLE,
539
+							'hidden_endpoint' => $hidden_endpoint,
540
+							'args'            => $this->_get_delete_query_params($model, $version),
541
+						],
542
+					]
543
+				);
544
+			}
545
+			foreach ($model->relation_settings() as $relation_name => $relation_obj) {
546
+				$related_route                  = EED_Core_Rest_Api::get_relation_route_via(
547
+					$model,
548
+					'(?P<id>[^\/]+)',
549
+					$relation_obj
550
+				);
551
+				$model_routes[ $related_route ] = [
552
+					[
553
+						'callback'        => [
554
+							'EventEspresso\core\libraries\rest_api\controllers\model\Read',
555
+							'handleRequestGetRelated',
556
+						],
557
+						'callback_args'   => [$version, $model_name, $relation_name],
558
+						'methods'         => WP_REST_Server::READABLE,
559
+						'hidden_endpoint' => $hidden_endpoint,
560
+						'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
561
+					],
562
+				];
563
+
564
+				$related_write_route                  = $related_route . '/' . '(?P<related_id>[^\/]+)';
565
+				$model_routes[ $related_write_route ] = [
566
+					[
567
+						'callback'        => [
568
+							'EventEspresso\core\libraries\rest_api\controllers\model\Write',
569
+							'handleRequestAddRelation',
570
+						],
571
+						'callback_args'   => [$version, $model_name, $relation_name],
572
+						'methods'         => WP_REST_Server::EDITABLE,
573
+						'hidden_endpoint' => $hidden_endpoint,
574
+						'args'            => $this->_get_add_relation_query_params(
575
+							$model,
576
+							$relation_obj->get_other_model(),
577
+							$version
578
+						),
579
+					],
580
+					[
581
+						'callback'        => [
582
+							'EventEspresso\core\libraries\rest_api\controllers\model\Write',
583
+							'handleRequestRemoveRelation',
584
+						],
585
+						'callback_args'   => [$version, $model_name, $relation_name],
586
+						'methods'         => WP_REST_Server::DELETABLE,
587
+						'hidden_endpoint' => $hidden_endpoint,
588
+						'args'            => [],
589
+					],
590
+				];
591
+			}
592
+		}
593
+		return $model_routes;
594
+	}
595
+
596
+
597
+	/**
598
+	 * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
599
+	 * excluding the preceding slash.
600
+	 * Eg you pass get_plural_route_to('Event') = 'events'
601
+	 *
602
+	 * @param EEM_Base $model
603
+	 * @return string
604
+	 */
605
+	public static function get_collection_route(EEM_Base $model): string
606
+	{
607
+		return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
608
+	}
609
+
610
+
611
+	/**
612
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
613
+	 * excluding the preceding slash.
614
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
615
+	 *
616
+	 * @param EEM_Base   $model eg Event or Venue
617
+	 * @param int|string $id
618
+	 * @return string
619
+	 */
620
+	public static function get_entity_route(EEM_Base $model, $id): string
621
+	{
622
+		return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
623
+	}
624
+
625
+
626
+	/**
627
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
628
+	 * excluding the preceding slash.
629
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
630
+	 *
631
+	 * @param EEM_Base               $model eg Event or Venue
632
+	 * @param int|string             $id
633
+	 * @param EE_Model_Relation_Base $relation_obj
634
+	 * @return string
635
+	 */
636
+	public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj): string
637
+	{
638
+		$related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
639
+			$relation_obj->get_other_model()->get_this_model_name(),
640
+			$relation_obj
641
+		);
642
+		return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
643
+	}
644
+
645
+
646
+	/**
647
+	 * Adds onto the $relative_route the EE4 REST API versioned namespace.
648
+	 * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
649
+	 *
650
+	 * @param string $relative_route
651
+	 * @param string $version
652
+	 * @return string
653
+	 */
654
+	public static function get_versioned_route_to(string $relative_route, string $version = '4.8.36'): string
655
+	{
656
+		return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
657
+	}
658
+
659
+
660
+	/**
661
+	 * Adds all the RPC-style routes (remote procedure call-like routes, ie
662
+	 * routes that don't conform to the traditional REST CRUD-style).
663
+	 *
664
+	 * @deprecated since 4.9.1
665
+	 */
666
+	protected function _register_rpc_routes(): array
667
+	{
668
+		$routes = [];
669
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
670
+			$routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
671
+				$version,
672
+				$hidden_endpoint
673
+			);
674
+		}
675
+		return $routes;
676
+	}
677
+
678
+
679
+	/**
680
+	 * @param string  $version
681
+	 * @param boolean $hidden_endpoint
682
+	 * @return array
683
+	 */
684
+	protected function _get_rpc_route_data_for_version(string $version, bool $hidden_endpoint = false): array
685
+	{
686
+		$this_versions_routes = [];
687
+		// checkin endpoint
688
+		$this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = [
689
+			[
690
+				'callback'        => [
691
+					'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
692
+					'handleRequestToggleCheckin',
693
+				],
694
+				'methods'         => WP_REST_Server::CREATABLE,
695
+				'hidden_endpoint' => $hidden_endpoint,
696
+				'args'            => [
697
+					'force' => [
698
+						'required'    => false,
699
+						'default'     => false,
700
+						'description' => esc_html__(
701
+						// @codingStandardsIgnoreStart
702
+							'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
703
+							// @codingStandardsIgnoreEnd
704
+							'event_espresso'
705
+						),
706
+					],
707
+				],
708
+				'callback_args'   => [$version],
709
+			],
710
+		];
711
+		return apply_filters(
712
+			'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
713
+			$this_versions_routes,
714
+			$version,
715
+			$hidden_endpoint
716
+		);
717
+	}
718
+
719
+
720
+	/**
721
+	 * Gets the query params that can be used when request one or many
722
+	 *
723
+	 * @param EEM_Base $model
724
+	 * @param string   $version
725
+	 * @return array
726
+	 */
727
+	protected function _get_response_selection_query_params(EEM_Base $model, string $version): array
728
+	{
729
+		$query_params = [
730
+			'include'   => [
731
+				'required' => false,
732
+				'default'  => '*',
733
+				'type'     => 'string',
734
+			],
735
+			'calculate' => [
736
+				'required'          => false,
737
+				'default'           => '',
738
+				'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
739
+				'type'              => 'string',
740
+				// because we accept a CSV list of the enumerated strings, WP core validation and sanitization
741
+				// freaks out. We'll just validate this argument while handling the request
742
+				'validate_callback' => null,
743
+				'sanitize_callback' => null,
744
+			],
745
+			'password'  => [
746
+				'required' => false,
747
+				'default'  => '',
748
+				'type'     => 'string',
749
+			],
750
+		];
751
+		return apply_filters(
752
+			'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
753
+			$query_params,
754
+			$model,
755
+			$version
756
+		);
757
+	}
758
+
759
+
760
+	/**
761
+	 * Gets the parameters acceptable for delete requests
762
+	 *
763
+	 * @param EEM_Base $model
764
+	 * @param string   $version
765
+	 * @return array
766
+	 */
767
+	protected function _get_delete_query_params(EEM_Base $model, string $version): array
768
+	{
769
+		$params_for_delete          = [
770
+			'allow_blocking' => [
771
+				'required' => false,
772
+				'default'  => true,
773
+				'type'     => 'boolean',
774
+			],
775
+		];
776
+		$params_for_delete['force'] = [
777
+			'required' => false,
778
+			'default'  => false,
779
+			'type'     => 'boolean',
780
+		];
781
+		return apply_filters(
782
+			'FHEE__EED_Core_Rest_Api___get_delete_query_params',
783
+			$params_for_delete,
784
+			$model,
785
+			$version
786
+		);
787
+	}
788
+
789
+
790
+	/**
791
+	 * @param EEM_Base $source_model
792
+	 * @param EEM_Base $related_model
793
+	 * @param string   $version
794
+	 * @return array
795
+	 * @throws EE_Error
796
+	 */
797
+	protected function _get_add_relation_query_params(
798
+		EEM_Base $source_model,
799
+		EEM_Base $related_model,
800
+		string $version
801
+	): array {
802
+		// if they're related through a HABTM relation, check for any non-FKs
803
+		$all_relation_settings = $source_model->relation_settings();
804
+		$relation_settings     = $all_relation_settings[ $related_model->get_this_model_name() ];
805
+		$params                = [];
806
+		if ($relation_settings instanceof EE_HABTM_Relation && $relation_settings->hasNonKeyFields()) {
807
+			foreach ($relation_settings->getNonKeyFields() as $field) {
808
+				/* @var $field EE_Model_Field_Base */
809
+				$params[ $field->get_name() ] = [
810
+					'required'          => ! $field->is_nullable(),
811
+					'default'           => ModelDataTranslator::prepareFieldValueForJson(
812
+						$field,
813
+						$field->get_default_value(),
814
+						$version
815
+					),
816
+					'type'              => $field->getSchemaType(),
817
+					'validate_callback' => null,
818
+					'sanitize_callback' => null,
819
+				];
820
+			}
821
+		}
822
+		return $params;
823
+	}
824
+
825
+
826
+	/**
827
+	 * Gets info about reading query params that are acceptable
828
+	 *
829
+	 * @param EEM_Base $model eg 'Event' or 'Venue'
830
+	 * @param string   $version
831
+	 * @return array    describing the args acceptable when querying this model
832
+	 * @throws EE_Error
833
+	 */
834
+	protected function _get_read_query_params(EEM_Base $model, string $version): array
835
+	{
836
+		$default_orderby = [];
837
+		foreach ($model->get_combined_primary_key_fields() as $key_field) {
838
+			$default_orderby[ $key_field->get_name() ] = 'ASC';
839
+		}
840
+		return array_merge(
841
+			$this->_get_response_selection_query_params($model, $version),
842
+			[
843
+				'where'    => [
844
+					'required'          => false,
845
+					'default'           => [],
846
+					'type'              => 'object',
847
+					// because we accept an almost infinite list of possible where conditions, WP
848
+					// core validation and sanitization freaks out. We'll just validate this argument
849
+					// while handling the request
850
+					'validate_callback' => null,
851
+					'sanitize_callback' => null,
852
+				],
853
+				'limit'    => [
854
+					'required'          => false,
855
+					'default'           => EED_Core_Rest_Api::get_default_query_limit(),
856
+					'type'              => [
857
+						'array',
858
+						'string',
859
+						'integer',
860
+					],
861
+					// because we accept a variety of types, WP core validation and sanitization
862
+					// freaks out. We'll just validate this argument while handling the request
863
+					'validate_callback' => null,
864
+					'sanitize_callback' => null,
865
+				],
866
+				'order_by' => [
867
+					'required'          => false,
868
+					'default'           => $default_orderby,
869
+					'type'              => [
870
+						'object',
871
+						'string',
872
+					],// because we accept a variety of types, WP core validation and sanitization
873
+					// freaks out. We'll just validate this argument while handling the request
874
+					'validate_callback' => null,
875
+					'sanitize_callback' => null,
876
+				],
877
+				'group_by' => [
878
+					'required'          => false,
879
+					'default'           => null,
880
+					'type'              => [
881
+						'object',
882
+						'string',
883
+					],
884
+					// because we accept  an almost infinite list of possible groupings,
885
+					// WP core validation and sanitization
886
+					// freaks out. We'll just validate this argument while handling the request
887
+					'validate_callback' => null,
888
+					'sanitize_callback' => null,
889
+				],
890
+				'having'   => [
891
+					'required'          => false,
892
+					'default'           => null,
893
+					'type'              => 'object',
894
+					// because we accept an almost infinite list of possible where conditions, WP
895
+					// core validation and sanitization freaks out. We'll just validate this argument
896
+					// while handling the request
897
+					'validate_callback' => null,
898
+					'sanitize_callback' => null,
899
+				],
900
+				'caps'     => [
901
+					'required' => false,
902
+					'default'  => EEM_Base::caps_read,
903
+					'type'     => 'string',
904
+					'enum'     => [
905
+						EEM_Base::caps_read,
906
+						EEM_Base::caps_read_admin,
907
+						EEM_Base::caps_edit,
908
+						EEM_Base::caps_delete,
909
+					],
910
+				],
911
+			]
912
+		);
913
+	}
914
+
915
+
916
+	/**
917
+	 * Gets parameter information for a model regarding writing data
918
+	 *
919
+	 * @param string           $model_name
920
+	 * @param ModelVersionInfo $model_version_info
921
+	 * @param boolean          $create                                       whether this is for request to create (in
922
+	 *                                                                       which case we need all required params) or
923
+	 *                                                                       just to update (in which case we don't
924
+	 *                                                                       need those on every request)
925
+	 * @return array
926
+	 * @throws EE_Error
927
+	 * @throws ReflectionException
928
+	 */
929
+	protected function _get_write_params(
930
+		string $model_name,
931
+		ModelVersionInfo $model_version_info,
932
+		bool $create = false
933
+	): array {
934
+		$model     = EE_Registry::instance()->load_model($model_name);
935
+		$fields    = $model_version_info->fieldsOnModelInThisVersion($model);
936
+		$args_info = [];
937
+		foreach ($fields as $field_name => $field_obj) {
938
+			if ($field_obj->is_auto_increment()) {
939
+				// totally ignore auto increment IDs
940
+				continue;
941
+			}
942
+			$arg_info             = $field_obj->getSchema();
943
+			$required             = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
944
+			$arg_info['required'] = $required;
945
+			// remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
946
+			unset($arg_info['readonly']);
947
+			$schema_properties = $field_obj->getSchemaProperties();
948
+			if (
949
+				isset($schema_properties['raw'])
950
+				&& $field_obj->getSchemaType() === 'object'
951
+			) {
952
+				// if there's a "raw" form of this argument, use those properties instead
953
+				$arg_info = array_replace(
954
+					$arg_info,
955
+					$schema_properties['raw']
956
+				);
957
+			}
958
+			$arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
959
+				$field_obj,
960
+				$field_obj->get_default_value(),
961
+				$model_version_info->requestedVersion()
962
+			);
963
+			// we do our own validation and sanitization within the controller
964
+			if (function_exists('rest_validate_value_from_schema')) {
965
+				$sanitize_callback = [
966
+					'EED_Core_Rest_Api',
967
+					'default_sanitize_callback',
968
+				];
969
+			} else {
970
+				$sanitize_callback = null;
971
+			}
972
+			$arg_info['sanitize_callback'] = $sanitize_callback;
973
+			$args_info[ $field_name ]      = $arg_info;
974
+			if ($field_obj instanceof EE_Datetime_Field) {
975
+				$gmt_arg_info                      = $arg_info;
976
+				$gmt_arg_info['description']       = sprintf(
977
+					esc_html__(
978
+						'%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
979
+						'event_espresso'
980
+					),
981
+					$field_obj->get_nicename(),
982
+					$field_name
983
+				);
984
+				$args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
985
+			}
986
+		}
987
+		return $args_info;
988
+	}
989
+
990
+
991
+	/**
992
+	 * Replacement for WP API's 'rest_parse_request_arg'.
993
+	 * If the value is blank but not required, don't bother validating it.
994
+	 * Also, it uses our email validation instead of WP API's default.
995
+	 *
996
+	 * @param                 $value
997
+	 * @param WP_REST_Request $request
998
+	 * @param                 $param
999
+	 * @return bool|true|WP_Error
1000
+	 * @throws InvalidArgumentException
1001
+	 * @throws InvalidInterfaceException
1002
+	 * @throws InvalidDataTypeException
1003
+	 */
1004
+	public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
1005
+	{
1006
+		$attributes = $request->get_attributes();
1007
+		if (
1008
+			! isset($attributes['args'][ $param ])
1009
+			|| ! is_array($attributes['args'][ $param ])
1010
+		) {
1011
+			$validation_result = true;
1012
+		} else {
1013
+			$args = $attributes['args'][ $param ];
1014
+			if (
1015
+				(
1016
+					$value === ''
1017
+					|| $value === null
1018
+				)
1019
+				&& (! isset($args['required'])
1020
+					|| $args['required'] === false
1021
+				)
1022
+			) {
1023
+				// not required and not provided? that's cool
1024
+				$validation_result = true;
1025
+			} elseif (
1026
+				isset($args['format'])
1027
+				&& $args['format'] === 'email'
1028
+			) {
1029
+				$validation_result = true;
1030
+				if (! self::_validate_email($value)) {
1031
+					$validation_result = new WP_Error(
1032
+						'rest_invalid_param',
1033
+						esc_html__(
1034
+							'The email address is not valid or does not exist.',
1035
+							'event_espresso'
1036
+						)
1037
+					);
1038
+				}
1039
+			} else {
1040
+				$validation_result = rest_validate_value_from_schema($value, $args, $param);
1041
+			}
1042
+		}
1043
+		if (is_wp_error($validation_result)) {
1044
+			return $validation_result;
1045
+		}
1046
+		return rest_sanitize_request_arg($value, $request, $param);
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
1052
+	 *
1053
+	 * @param $email
1054
+	 * @return bool
1055
+	 * @throws InvalidArgumentException
1056
+	 * @throws InvalidInterfaceException
1057
+	 * @throws InvalidDataTypeException
1058
+	 */
1059
+	protected static function _validate_email($email): bool
1060
+	{
1061
+		try {
1062
+			EmailAddressFactory::create($email);
1063
+			return true;
1064
+		} catch (EmailValidationException $e) {
1065
+			return false;
1066
+		}
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * Gets routes for the config
1072
+	 *
1073
+	 * @return array @see _register_model_routes
1074
+	 * @deprecated since version 4.9.1
1075
+	 */
1076
+	protected function _register_config_routes(): array
1077
+	{
1078
+		$config_routes = [];
1079
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1080
+			$config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
1081
+				$version,
1082
+				$hidden_endpoint
1083
+			);
1084
+		}
1085
+		return $config_routes;
1086
+	}
1087
+
1088
+
1089
+	/**
1090
+	 * Gets routes for the config for the specified version
1091
+	 *
1092
+	 * @param string  $version
1093
+	 * @param boolean $hidden_endpoint
1094
+	 * @return array
1095
+	 */
1096
+	protected function _get_config_route_data_for_version(string $version, bool $hidden_endpoint): array
1097
+	{
1098
+		return [
1099
+			'config'    => [
1100
+				[
1101
+					'callback'        => [
1102
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1103
+						'handleRequest',
1104
+					],
1105
+					'methods'         => WP_REST_Server::READABLE,
1106
+					'hidden_endpoint' => $hidden_endpoint,
1107
+					'callback_args'   => [$version],
1108
+				],
1109
+			],
1110
+			'site_info' => [
1111
+				[
1112
+					'callback'        => [
1113
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1114
+						'handleRequestSiteInfo',
1115
+					],
1116
+					'methods'         => WP_REST_Server::READABLE,
1117
+					'hidden_endpoint' => $hidden_endpoint,
1118
+					'callback_args'   => [$version],
1119
+				],
1120
+			],
1121
+		];
1122
+	}
1123
+
1124
+
1125
+	/**
1126
+	 * Gets the meta info routes
1127
+	 *
1128
+	 * @return array @see _register_model_routes
1129
+	 * @deprecated since version 4.9.1
1130
+	 */
1131
+	protected function _register_meta_routes(): array
1132
+	{
1133
+		$meta_routes = [];
1134
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1135
+			$meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1136
+				$version,
1137
+				$hidden_endpoint
1138
+			);
1139
+		}
1140
+		return $meta_routes;
1141
+	}
1142
+
1143
+
1144
+	/**
1145
+	 * @param string  $version
1146
+	 * @param boolean $hidden_endpoint
1147
+	 * @return array
1148
+	 */
1149
+	protected function _get_meta_route_data_for_version(string $version, bool $hidden_endpoint = false): array
1150
+	{
1151
+		return [
1152
+			'resources' => [
1153
+				[
1154
+					'callback'        => [
1155
+						'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1156
+						'handleRequestModelsMeta',
1157
+					],
1158
+					'methods'         => WP_REST_Server::READABLE,
1159
+					'hidden_endpoint' => $hidden_endpoint,
1160
+					'callback_args'   => [$version],
1161
+				],
1162
+			],
1163
+		];
1164
+	}
1165
+
1166
+
1167
+	/**
1168
+	 * Tries to hide old 4.6 endpoints from the
1169
+	 *
1170
+	 * @param array $route_data
1171
+	 * @return array
1172
+	 * @throws EE_Error
1173
+	 * @throws ReflectionException
1174
+	 */
1175
+	public static function hide_old_endpoints(array $route_data): array
1176
+	{
1177
+		// allow API clients to override which endpoints get hidden, in case
1178
+		// they want to discover particular endpoints
1179
+		// also, we don't have access to the request so we have to just grab it from the super global
1180
+		$force_show_ee_namespace = ltrim(
1181
+			EED_Core_Rest_Api::getRequest()->getRequestParam('force_show_ee_namespace'),
1182
+			'/'
1183
+		);
1184
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1185
+			foreach ($relative_urls as $resource_name => $endpoints) {
1186
+				foreach ($endpoints as $key => $endpoint) {
1187
+					// skip schema and other route options
1188
+					if (! is_numeric($key)) {
1189
+						continue;
1190
+					}
1191
+					// by default, hide "hidden_endpoint"s, unless the request indicates
1192
+					// to $force_show_ee_namespace, in which case only show that one
1193
+					// namespace's endpoints (and hide all others)
1194
+					if (
1195
+						($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1196
+						|| ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1197
+					) {
1198
+						$full_route = '/' . ltrim($namespace, '/');
1199
+						$full_route .= '/' . ltrim($resource_name, '/');
1200
+						unset($route_data[ $full_route ]);
1201
+					}
1202
+				}
1203
+			}
1204
+		}
1205
+		return $route_data;
1206
+	}
1207
+
1208
+
1209
+	/**
1210
+	 * Returns an array describing which versions of core support serving requests for.
1211
+	 * Keys are core versions' major and minor version, and values are the
1212
+	 * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1213
+	 * data by just removing a few models and fields from the responses. However, 4.15 might remove
1214
+	 * the answers table entirely, in which case it would be very difficult for
1215
+	 * it to serve 4.6-style responses.
1216
+	 * Versions of core that are missing from this array are unknowns.
1217
+	 * previous ver
1218
+	 *
1219
+	 * @return array
1220
+	 */
1221
+	public static function version_compatibilities(): array
1222
+	{
1223
+		return apply_filters(
1224
+			'FHEE__EED_Core_REST_API__version_compatibilities',
1225
+			[
1226
+				'4.8.29' => '4.8.29',
1227
+				'4.8.33' => '4.8.29',
1228
+				'4.8.34' => '4.8.29',
1229
+				'4.8.36' => '4.8.29',
1230
+			]
1231
+		);
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Gets the latest API version served. Eg if there
1237
+	 * are two versions served of the API, 4.8.29 and 4.8.32, and
1238
+	 * we are on core version 4.8.34, it will return the string "4.8.32"
1239
+	 *
1240
+	 * @return string
1241
+	 */
1242
+	public static function latest_rest_api_version(): string
1243
+	{
1244
+		$versions_served      = EED_Core_Rest_Api::versions_served();
1245
+		$versions_served_keys = array_keys($versions_served);
1246
+		return end($versions_served_keys);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1252
+	 * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1253
+	 * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1254
+	 * We also indicate whether or not this version should be put in the index or not
1255
+	 *
1256
+	 * @return array keys are API version numbers (just major and minor numbers), and values
1257
+	 * are whether or not they should be hidden
1258
+	 */
1259
+	public static function versions_served(): array
1260
+	{
1261
+		$versions_served           = [];
1262
+		$possibly_served_versions  = EED_Core_Rest_Api::version_compatibilities();
1263
+		$lowest_compatible_version = end($possibly_served_versions);
1264
+		reset($possibly_served_versions);
1265
+		$versions_served_historically = array_keys($possibly_served_versions);
1266
+		$latest_version               = end($versions_served_historically);
1267
+		reset($versions_served_historically);
1268
+		// for each version of core we have ever served:
1269
+		foreach ($versions_served_historically as $key_versioned_endpoint) {
1270
+			// if it's not above the current core version, and it's compatible with the current version of core
1271
+
1272
+			if ($key_versioned_endpoint === $latest_version) {
1273
+				// don't hide the latest version in the index
1274
+				$versions_served[ $key_versioned_endpoint ] = false;
1275
+			} elseif (
1276
+				version_compare($key_versioned_endpoint, $lowest_compatible_version, '>=')
1277
+				&& version_compare($key_versioned_endpoint, EED_Core_Rest_Api::core_version(), '<')
1278
+			) {
1279
+				// include, but hide, previous versions which are still supported
1280
+				$versions_served[ $key_versioned_endpoint ] = true;
1281
+			} elseif (
1282
+				apply_filters(
1283
+					'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1284
+					false,
1285
+					$possibly_served_versions
1286
+				)
1287
+			) {
1288
+				// if a version is no longer supported, don't include it in index or list of versions served
1289
+				$versions_served[ $key_versioned_endpoint ] = true;
1290
+			}
1291
+		}
1292
+		return $versions_served;
1293
+	}
1294
+
1295
+
1296
+	/**
1297
+	 * Gets the major and minor version of EE core's version string
1298
+	 *
1299
+	 * @return string
1300
+	 */
1301
+	public static function core_version(): string
1302
+	{
1303
+		return apply_filters(
1304
+			'FHEE__EED_Core_REST_API__core_version',
1305
+			implode(
1306
+				'.',
1307
+				array_slice(
1308
+					explode(
1309
+						'.',
1310
+						espresso_version()
1311
+					),
1312
+					0,
1313
+					3
1314
+				)
1315
+			)
1316
+		);
1317
+	}
1318
+
1319
+
1320
+	/**
1321
+	 * Gets the default limit that should be used when querying for resources
1322
+	 *
1323
+	 * @return int
1324
+	 */
1325
+	public static function get_default_query_limit(): int
1326
+	{
1327
+		// we actually don't use a const because we want folks to always use
1328
+		// this method, not the const directly
1329
+		return apply_filters(
1330
+			'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1331
+			50
1332
+		);
1333
+	}
1334
+
1335
+
1336
+	/**
1337
+	 *
1338
+	 * @param string $version api version string (i.e. '4.8.36')
1339
+	 * @return array
1340
+	 */
1341
+	public static function getCollectionRoutesIndexedByModelName(string $version = ''): array
1342
+	{
1343
+		$version           = empty($version) ? self::latest_rest_api_version() : $version;
1344
+		$model_names       = self::model_names_with_plural_routes($version);
1345
+		$collection_routes = [];
1346
+		foreach ($model_names as $model_name => $model_class_name) {
1347
+			$collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1348
+															. EEH_Inflector::pluralize_and_lower($model_name);
1349
+		}
1350
+		return $collection_routes;
1351
+	}
1352
+
1353
+
1354
+	/**
1355
+	 * Returns an array of primary key names indexed by model names.
1356
+	 *
1357
+	 * @param string $version
1358
+	 * @return array
1359
+	 */
1360
+	public static function getPrimaryKeyNamesIndexedByModelName(string $version = ''): array
1361
+	{
1362
+		$version           = empty($version) ? self::latest_rest_api_version() : $version;
1363
+		$model_names       = self::model_names_with_plural_routes($version);
1364
+		$primary_key_items = [];
1365
+		foreach ($model_names as $model_name => $model_class_name) {
1366
+			$primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1367
+			foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1368
+				if (count($primary_keys) > 1) {
1369
+					$primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1370
+				} else {
1371
+					$primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1372
+				}
1373
+			}
1374
+		}
1375
+		return $primary_key_items;
1376
+	}
1377
+
1378
+
1379
+	/**
1380
+	 * Determines the EE REST API debug mode is activated, or not.
1381
+	 *
1382
+	 * @return bool
1383
+	 * @since 4.9.76.p
1384
+	 */
1385
+	public static function debugMode(): ?bool
1386
+	{
1387
+		static $debug_mode = null; // could be class prop
1388
+		if ($debug_mode === null) {
1389
+			$debug_mode = defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE;
1390
+		}
1391
+		return $debug_mode;
1392
+	}
1393
+
1394
+
1395
+	/**
1396
+	 *    run - initial module setup
1397
+	 *
1398
+	 * @access    public
1399
+	 * @param WP $WP
1400
+	 * @return    void
1401
+	 */
1402
+	public function run($WP)
1403
+	{
1404
+	}
1405 1405
 }
Please login to merge, or discard this patch.
Spacing   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
      */
125 125
     protected static function _set_hooks_for_changes()
126 126
     {
127
-        $folder_contents = EEH_File::get_contents_of_folders([EE_LIBRARIES . 'rest_api/changes']);
127
+        $folder_contents = EEH_File::get_contents_of_folders([EE_LIBRARIES.'rest_api/changes']);
128 128
         foreach ($folder_contents as $classname_in_namespace => $filepath) {
129 129
             // ignore the base parent class
130 130
             // and legacy named classes
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
             ) {
135 135
                 continue;
136 136
             }
137
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
137
+            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\'.$classname_in_namespace;
138 138
             if (class_exists($full_classname)) {
139 139
                 $instance_of_class = new $full_classname();
140 140
                 if ($instance_of_class instanceof ChangesInBase) {
@@ -180,10 +180,10 @@  discard block
 block discarded – undo
180 180
                      * }
181 181
                      */
182 182
                     // skip route options
183
-                    if (! is_numeric($endpoint_key)) {
183
+                    if ( ! is_numeric($endpoint_key)) {
184 184
                         continue;
185 185
                     }
186
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
186
+                    if ( ! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
187 187
                         throw new EE_Error(
188 188
                             esc_html__(
189 189
                             // @codingStandardsIgnoreStart
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
                     }
204 204
                     if (isset($data_for_single_endpoint['callback_args'])) {
205 205
                         $callback_args                    = $data_for_single_endpoint['callback_args'];
206
-                        $single_endpoint_args['callback'] = function (WP_REST_Request $request) use (
206
+                        $single_endpoint_args['callback'] = function(WP_REST_Request $request) use (
207 207
                             $callback,
208 208
                             $callback_args
209 209
                         ) {
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
                     // the REST API will issue a _doing_it_wrong notice.
221 221
                     // Since the EE REST API defers capabilities to the db model system,
222 222
                     // we will just use the generic WP callback for public endpoints
223
-                    if (! isset($single_endpoint_args['permission_callback'])) {
223
+                    if ( ! isset($single_endpoint_args['permission_callback'])) {
224 224
                         $single_endpoint_args['permission_callback'] = '__return_true';
225 225
                     }
226 226
                     $multiple_endpoint_args[] = $single_endpoint_args;
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
                     $schema_route_data                = $data_for_multiple_endpoints['schema'];
230 230
                     $schema_callback                  = $schema_route_data['schema_callback'];
231 231
                     $callback_args                    = $schema_route_data['callback_args'];
232
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
232
+                    $multiple_endpoint_args['schema'] = function() use ($schema_callback, $callback_args) {
233 233
                         return call_user_func_array(
234 234
                             $schema_callback,
235 235
                             $callback_args
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
     {
272 272
         // delete the saved EE REST API routes
273 273
         foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
274
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
274
+            delete_option(EED_Core_Rest_Api::saved_routes_option_names.$version);
275 275
         }
276 276
     }
277 277
 
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
     {
292 292
         $ee_routes = [];
293 293
         foreach (self::versions_served() as $version => $hidden_endpoints) {
294
-            $ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
294
+            $ee_routes[self::ee_api_namespace.$version] = self::_get_ee_route_data_for_version(
295 295
                 $version,
296 296
                 $hidden_endpoints
297 297
             );
@@ -312,8 +312,8 @@  discard block
 block discarded – undo
312 312
      */
313 313
     protected static function _get_ee_route_data_for_version(string $version, bool $hidden_endpoints = false): array
314 314
     {
315
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
316
-        if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
315
+        $ee_routes = get_option(self::saved_routes_option_names.$version, null);
316
+        if ( ! $ee_routes || EED_Core_Rest_Api::debugMode()) {
317 317
             $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
318 318
         }
319 319
         return $ee_routes;
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
                 $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
342 342
             )
343 343
         );
344
-        $option_name = self::saved_routes_option_names . $version;
344
+        $option_name = self::saved_routes_option_names.$version;
345 345
         if (get_option($option_name)) {
346 346
             update_option($option_name, $routes, true);
347 347
         } else {
@@ -388,8 +388,8 @@  discard block
 block discarded – undo
388 388
     {
389 389
         $model_routes = [];
390 390
         foreach (self::versions_served() as $version => $hidden_endpoint) {
391
-            $model_routes[ EED_Core_Rest_Api::ee_api_namespace
392
-                           . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
391
+            $model_routes[EED_Core_Rest_Api::ee_api_namespace
392
+                           . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
393 393
         }
394 394
         return $model_routes;
395 395
     }
@@ -459,13 +459,13 @@  discard block
 block discarded – undo
459 459
         foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
460 460
             $model = EE_Registry::instance()->load_model($model_name);
461 461
             // if this isn't a valid model then let's skip iterate to the next item in the loop.
462
-            if (! $model instanceof EEM_Base) {
462
+            if ( ! $model instanceof EEM_Base) {
463 463
                 continue;
464 464
             }
465 465
             // yes we could just register one route for ALL models, but then they wouldn't show up in the index
466 466
             $plural_model_route                    = EED_Core_Rest_Api::get_collection_route($model);
467 467
             $singular_model_route                  = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
468
-            $model_routes[ $plural_model_route ]   = [
468
+            $model_routes[$plural_model_route]   = [
469 469
                 [
470 470
                     'callback'        => [
471 471
                         'EventEspresso\core\libraries\rest_api\controllers\model\Read',
@@ -476,7 +476,7 @@  discard block
 block discarded – undo
476 476
                     'hidden_endpoint' => $hidden_endpoint,
477 477
                     'args'            => $this->_get_read_query_params($model, $version),
478 478
                     '_links'          => [
479
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
479
+                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace.$version.$singular_model_route),
480 480
                     ],
481 481
                 ],
482 482
                 'schema' => [
@@ -487,7 +487,7 @@  discard block
 block discarded – undo
487 487
                     'callback_args'   => [$version, $model_name],
488 488
                 ],
489 489
             ];
490
-            $model_routes[ $singular_model_route ] = [
490
+            $model_routes[$singular_model_route] = [
491 491
                 [
492 492
                     'callback'        => [
493 493
                         'EventEspresso\core\libraries\rest_api\controllers\model\Read',
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
                     $model
507 507
                 )
508 508
             ) {
509
-                $model_routes[ $plural_model_route ][] = [
509
+                $model_routes[$plural_model_route][] = [
510 510
                     'callback'        => [
511 511
                         'EventEspresso\core\libraries\rest_api\controllers\model\Write',
512 512
                         'handleRequestInsert',
@@ -516,8 +516,8 @@  discard block
 block discarded – undo
516 516
                     'hidden_endpoint' => $hidden_endpoint,
517 517
                     'args'            => $this->_get_write_params($model_name, $model_version_info, true),
518 518
                 ];
519
-                $model_routes[ $singular_model_route ] = array_merge(
520
-                    $model_routes[ $singular_model_route ],
519
+                $model_routes[$singular_model_route] = array_merge(
520
+                    $model_routes[$singular_model_route],
521 521
                     [
522 522
                         [
523 523
                             'callback'        => [
@@ -543,12 +543,12 @@  discard block
 block discarded – undo
543 543
                 );
544 544
             }
545 545
             foreach ($model->relation_settings() as $relation_name => $relation_obj) {
546
-                $related_route                  = EED_Core_Rest_Api::get_relation_route_via(
546
+                $related_route = EED_Core_Rest_Api::get_relation_route_via(
547 547
                     $model,
548 548
                     '(?P<id>[^\/]+)',
549 549
                     $relation_obj
550 550
                 );
551
-                $model_routes[ $related_route ] = [
551
+                $model_routes[$related_route] = [
552 552
                     [
553 553
                         'callback'        => [
554 554
                             'EventEspresso\core\libraries\rest_api\controllers\model\Read',
@@ -561,8 +561,8 @@  discard block
 block discarded – undo
561 561
                     ],
562 562
                 ];
563 563
 
564
-                $related_write_route                  = $related_route . '/' . '(?P<related_id>[^\/]+)';
565
-                $model_routes[ $related_write_route ] = [
564
+                $related_write_route                  = $related_route.'/'.'(?P<related_id>[^\/]+)';
565
+                $model_routes[$related_write_route] = [
566 566
                     [
567 567
                         'callback'        => [
568 568
                             'EventEspresso\core\libraries\rest_api\controllers\model\Write',
@@ -619,7 +619,7 @@  discard block
 block discarded – undo
619 619
      */
620 620
     public static function get_entity_route(EEM_Base $model, $id): string
621 621
     {
622
-        return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
622
+        return EED_Core_Rest_Api::get_collection_route($model).'/'.$id;
623 623
     }
624 624
 
625 625
 
@@ -639,7 +639,7 @@  discard block
 block discarded – undo
639 639
             $relation_obj->get_other_model()->get_this_model_name(),
640 640
             $relation_obj
641 641
         );
642
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
642
+        return EED_Core_Rest_Api::get_entity_route($model, $id).'/'.$related_model_name_endpoint_part;
643 643
     }
644 644
 
645 645
 
@@ -653,7 +653,7 @@  discard block
 block discarded – undo
653 653
      */
654 654
     public static function get_versioned_route_to(string $relative_route, string $version = '4.8.36'): string
655 655
     {
656
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
656
+        return '/'.EED_Core_Rest_Api::ee_api_namespace.$version.'/'.$relative_route;
657 657
     }
658 658
 
659 659
 
@@ -667,7 +667,7 @@  discard block
 block discarded – undo
667 667
     {
668 668
         $routes = [];
669 669
         foreach (self::versions_served() as $version => $hidden_endpoint) {
670
-            $routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
670
+            $routes[self::ee_api_namespace.$version] = $this->_get_rpc_route_data_for_version(
671 671
                 $version,
672 672
                 $hidden_endpoint
673 673
             );
@@ -766,7 +766,7 @@  discard block
 block discarded – undo
766 766
      */
767 767
     protected function _get_delete_query_params(EEM_Base $model, string $version): array
768 768
     {
769
-        $params_for_delete          = [
769
+        $params_for_delete = [
770 770
             'allow_blocking' => [
771 771
                 'required' => false,
772 772
                 'default'  => true,
@@ -801,12 +801,12 @@  discard block
 block discarded – undo
801 801
     ): array {
802 802
         // if they're related through a HABTM relation, check for any non-FKs
803 803
         $all_relation_settings = $source_model->relation_settings();
804
-        $relation_settings     = $all_relation_settings[ $related_model->get_this_model_name() ];
804
+        $relation_settings     = $all_relation_settings[$related_model->get_this_model_name()];
805 805
         $params                = [];
806 806
         if ($relation_settings instanceof EE_HABTM_Relation && $relation_settings->hasNonKeyFields()) {
807 807
             foreach ($relation_settings->getNonKeyFields() as $field) {
808 808
                 /* @var $field EE_Model_Field_Base */
809
-                $params[ $field->get_name() ] = [
809
+                $params[$field->get_name()] = [
810 810
                     'required'          => ! $field->is_nullable(),
811 811
                     'default'           => ModelDataTranslator::prepareFieldValueForJson(
812 812
                         $field,
@@ -835,7 +835,7 @@  discard block
 block discarded – undo
835 835
     {
836 836
         $default_orderby = [];
837 837
         foreach ($model->get_combined_primary_key_fields() as $key_field) {
838
-            $default_orderby[ $key_field->get_name() ] = 'ASC';
838
+            $default_orderby[$key_field->get_name()] = 'ASC';
839 839
         }
840 840
         return array_merge(
841 841
             $this->_get_response_selection_query_params($model, $version),
@@ -869,7 +869,7 @@  discard block
 block discarded – undo
869 869
                     'type'              => [
870 870
                         'object',
871 871
                         'string',
872
-                    ],// because we accept a variety of types, WP core validation and sanitization
872
+                    ], // because we accept a variety of types, WP core validation and sanitization
873 873
                     // freaks out. We'll just validate this argument while handling the request
874 874
                     'validate_callback' => null,
875 875
                     'sanitize_callback' => null,
@@ -970,7 +970,7 @@  discard block
 block discarded – undo
970 970
                 $sanitize_callback = null;
971 971
             }
972 972
             $arg_info['sanitize_callback'] = $sanitize_callback;
973
-            $args_info[ $field_name ]      = $arg_info;
973
+            $args_info[$field_name]      = $arg_info;
974 974
             if ($field_obj instanceof EE_Datetime_Field) {
975 975
                 $gmt_arg_info                      = $arg_info;
976 976
                 $gmt_arg_info['description']       = sprintf(
@@ -981,7 +981,7 @@  discard block
 block discarded – undo
981 981
                     $field_obj->get_nicename(),
982 982
                     $field_name
983 983
                 );
984
-                $args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
984
+                $args_info[$field_name.'_gmt'] = $gmt_arg_info;
985 985
             }
986 986
         }
987 987
         return $args_info;
@@ -1005,18 +1005,18 @@  discard block
 block discarded – undo
1005 1005
     {
1006 1006
         $attributes = $request->get_attributes();
1007 1007
         if (
1008
-            ! isset($attributes['args'][ $param ])
1009
-            || ! is_array($attributes['args'][ $param ])
1008
+            ! isset($attributes['args'][$param])
1009
+            || ! is_array($attributes['args'][$param])
1010 1010
         ) {
1011 1011
             $validation_result = true;
1012 1012
         } else {
1013
-            $args = $attributes['args'][ $param ];
1013
+            $args = $attributes['args'][$param];
1014 1014
             if (
1015 1015
                 (
1016 1016
                     $value === ''
1017 1017
                     || $value === null
1018 1018
                 )
1019
-                && (! isset($args['required'])
1019
+                && ( ! isset($args['required'])
1020 1020
                     || $args['required'] === false
1021 1021
                 )
1022 1022
             ) {
@@ -1027,7 +1027,7 @@  discard block
 block discarded – undo
1027 1027
                 && $args['format'] === 'email'
1028 1028
             ) {
1029 1029
                 $validation_result = true;
1030
-                if (! self::_validate_email($value)) {
1030
+                if ( ! self::_validate_email($value)) {
1031 1031
                     $validation_result = new WP_Error(
1032 1032
                         'rest_invalid_param',
1033 1033
                         esc_html__(
@@ -1077,7 +1077,7 @@  discard block
 block discarded – undo
1077 1077
     {
1078 1078
         $config_routes = [];
1079 1079
         foreach (self::versions_served() as $version => $hidden_endpoint) {
1080
-            $config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
1080
+            $config_routes[self::ee_api_namespace.$version] = $this->_get_config_route_data_for_version(
1081 1081
                 $version,
1082 1082
                 $hidden_endpoint
1083 1083
             );
@@ -1132,7 +1132,7 @@  discard block
 block discarded – undo
1132 1132
     {
1133 1133
         $meta_routes = [];
1134 1134
         foreach (self::versions_served() as $version => $hidden_endpoint) {
1135
-            $meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1135
+            $meta_routes[self::ee_api_namespace.$version] = $this->_get_meta_route_data_for_version(
1136 1136
                 $version,
1137 1137
                 $hidden_endpoint
1138 1138
             );
@@ -1185,7 +1185,7 @@  discard block
 block discarded – undo
1185 1185
             foreach ($relative_urls as $resource_name => $endpoints) {
1186 1186
                 foreach ($endpoints as $key => $endpoint) {
1187 1187
                     // skip schema and other route options
1188
-                    if (! is_numeric($key)) {
1188
+                    if ( ! is_numeric($key)) {
1189 1189
                         continue;
1190 1190
                     }
1191 1191
                     // by default, hide "hidden_endpoint"s, unless the request indicates
@@ -1195,9 +1195,9 @@  discard block
 block discarded – undo
1195 1195
                         ($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1196 1196
                         || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1197 1197
                     ) {
1198
-                        $full_route = '/' . ltrim($namespace, '/');
1199
-                        $full_route .= '/' . ltrim($resource_name, '/');
1200
-                        unset($route_data[ $full_route ]);
1198
+                        $full_route = '/'.ltrim($namespace, '/');
1199
+                        $full_route .= '/'.ltrim($resource_name, '/');
1200
+                        unset($route_data[$full_route]);
1201 1201
                     }
1202 1202
                 }
1203 1203
             }
@@ -1271,13 +1271,13 @@  discard block
 block discarded – undo
1271 1271
 
1272 1272
             if ($key_versioned_endpoint === $latest_version) {
1273 1273
                 // don't hide the latest version in the index
1274
-                $versions_served[ $key_versioned_endpoint ] = false;
1274
+                $versions_served[$key_versioned_endpoint] = false;
1275 1275
             } elseif (
1276 1276
                 version_compare($key_versioned_endpoint, $lowest_compatible_version, '>=')
1277 1277
                 && version_compare($key_versioned_endpoint, EED_Core_Rest_Api::core_version(), '<')
1278 1278
             ) {
1279 1279
                 // include, but hide, previous versions which are still supported
1280
-                $versions_served[ $key_versioned_endpoint ] = true;
1280
+                $versions_served[$key_versioned_endpoint] = true;
1281 1281
             } elseif (
1282 1282
                 apply_filters(
1283 1283
                     'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
@@ -1286,7 +1286,7 @@  discard block
 block discarded – undo
1286 1286
                 )
1287 1287
             ) {
1288 1288
                 // if a version is no longer supported, don't include it in index or list of versions served
1289
-                $versions_served[ $key_versioned_endpoint ] = true;
1289
+                $versions_served[$key_versioned_endpoint] = true;
1290 1290
             }
1291 1291
         }
1292 1292
         return $versions_served;
@@ -1344,7 +1344,7 @@  discard block
 block discarded – undo
1344 1344
         $model_names       = self::model_names_with_plural_routes($version);
1345 1345
         $collection_routes = [];
1346 1346
         foreach ($model_names as $model_name => $model_class_name) {
1347
-            $collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1347
+            $collection_routes[strtolower($model_name)] = '/'.self::ee_api_namespace.$version.'/'
1348 1348
                                                             . EEH_Inflector::pluralize_and_lower($model_name);
1349 1349
         }
1350 1350
         return $collection_routes;
@@ -1366,9 +1366,9 @@  discard block
 block discarded – undo
1366 1366
             $primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1367 1367
             foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1368 1368
                 if (count($primary_keys) > 1) {
1369
-                    $primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1369
+                    $primary_key_items[strtolower($model_name)][] = $primary_key_name;
1370 1370
                 } else {
1371
-                    $primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1371
+                    $primary_key_items[strtolower($model_name)] = $primary_key_name;
1372 1372
                 }
1373 1373
             }
1374 1374
         }
Please login to merge, or discard this patch.
core/libraries/rest_api/ModelDataTranslator.php 2 patches
Indentation   +663 added lines, -663 removed lines patch added patch discarded remove patch
@@ -38,667 +38,667 @@
 block discarded – undo
38 38
  */
39 39
 class ModelDataTranslator
40 40
 {
41
-    /**
42
-     * We used to use -1 for infinity in the rest api, but that's ambiguous for
43
-     * fields that COULD contain -1; so we use null
44
-     */
45
-    const EE_INF_IN_REST = null;
46
-
47
-
48
-    /**
49
-     * Prepares a possible array of input values from JSON for use by the models
50
-     *
51
-     * @param EE_Model_Field_Base $field_obj
52
-     * @param mixed               $original_value_maybe_array
53
-     * @param string              $requested_version
54
-     * @param string              $timezone_string treat values as being in this timezone
55
-     * @return mixed
56
-     * @throws RestException
57
-     * @throws EE_Error
58
-     */
59
-    public static function prepareFieldValuesFromJson(
60
-        EE_Model_Field_Base $field_obj,
61
-        $original_value_maybe_array,
62
-        string $requested_version,
63
-        string $timezone_string = 'UTC'
64
-    ) {
65
-        if (
66
-            is_array($original_value_maybe_array)
67
-            && ! $field_obj instanceof EE_Serialized_Text_Field
68
-        ) {
69
-            $new_value_maybe_array = [];
70
-            foreach ($original_value_maybe_array as $array_key => $array_item) {
71
-                $new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
72
-                    $field_obj,
73
-                    $array_item,
74
-                    $requested_version,
75
-                    $timezone_string
76
-                );
77
-            }
78
-        } else {
79
-            $new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
80
-                $field_obj,
81
-                $original_value_maybe_array,
82
-                $requested_version,
83
-                $timezone_string
84
-            );
85
-        }
86
-        return $new_value_maybe_array;
87
-    }
88
-
89
-
90
-    /**
91
-     * Prepares an array of field values FOR use in JSON/REST API
92
-     *
93
-     * @param EE_Model_Field_Base $field_obj
94
-     * @param mixed               $original_value_maybe_array
95
-     * @param string              $request_version (eg 4.8.36)
96
-     * @return mixed
97
-     * @throws EE_Error
98
-     * @throws EE_Error
99
-     */
100
-    public static function prepareFieldValuesForJson(
101
-        EE_Model_Field_Base $field_obj,
102
-        $original_value_maybe_array,
103
-        string $request_version
104
-    ) {
105
-        if (is_array($original_value_maybe_array)) {
106
-            $new_value = [];
107
-            foreach ($original_value_maybe_array as $key => $value) {
108
-                $new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
109
-                    $field_obj,
110
-                    $value,
111
-                    $request_version
112
-                );
113
-            }
114
-        } else {
115
-            $new_value = ModelDataTranslator::prepareFieldValueForJson(
116
-                $field_obj,
117
-                $original_value_maybe_array,
118
-                $request_version
119
-            );
120
-        }
121
-        return $new_value;
122
-    }
123
-
124
-
125
-    /**
126
-     * Prepares incoming data from the json or request parameters for the models'
127
-     * "$query_params".
128
-     *
129
-     * @param EE_Model_Field_Base $field_obj
130
-     * @param mixed               $original_value
131
-     * @param string              $requested_version
132
-     * @param string              $timezone_string treat values as being in this timezone
133
-     * @return mixed
134
-     * @throws RestException
135
-     * @throws DomainException
136
-     * @throws EE_Error
137
-     */
138
-    public static function prepareFieldValueFromJson(
139
-        EE_Model_Field_Base $field_obj,
140
-        $original_value,
141
-        string $requested_version,
142
-        string $timezone_string = 'UTC'
143
-    ) {
144
-        // check if they accidentally submitted an error value. If so throw an exception
145
-        if (
146
-            is_array($original_value)
147
-            && isset($original_value['error_code'], $original_value['error_message'])
148
-        ) {
149
-            throw new RestException(
150
-                'rest_submitted_error_value',
151
-                sprintf(
152
-                    esc_html__(
153
-                        'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
154
-                        'event_espresso'
155
-                    ),
156
-                    $field_obj->get_name()
157
-                ),
158
-                [
159
-                    'status' => 400,
160
-                ]
161
-            );
162
-        }
163
-        // double-check for serialized PHP. We never accept serialized PHP. No way Jose.
164
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
165
-        $timezone_string =
166
-            $timezone_string !== ''
167
-                ? $timezone_string
168
-                : get_option('timezone_string', '');
169
-        // walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
170
-        // way Jose.
171
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
172
-        if (
173
-            $field_obj instanceof EE_Infinite_Integer_Field
174
-            && in_array($original_value, [null, ''], true)
175
-        ) {
176
-            $new_value = EE_INF;
177
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
178
-            $new_value = rest_parse_date(
179
-                self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
180
-            );
181
-            if ($new_value === false) {
182
-                throw new RestException(
183
-                    'invalid_format_for_timestamp',
184
-                    sprintf(
185
-                        esc_html__(
186
-                            'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
187
-                            'event_espresso'
188
-                        ),
189
-                        'RFC3339',
190
-                        'ISO8601',
191
-                        $original_value
192
-                    ),
193
-                    [
194
-                        'status' => 400,
195
-                    ]
196
-                );
197
-            }
198
-        } elseif ($field_obj instanceof EE_Boolean_Field) {
199
-            // Interpreted the strings "false", "true", "on", "off" appropriately.
200
-            $new_value = filter_var($original_value, FILTER_VALIDATE_BOOLEAN);
201
-        } else {
202
-            $new_value = $original_value;
203
-        }
204
-        return $new_value;
205
-    }
206
-
207
-
208
-    /**
209
-     * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
210
-     * information via details obtained from the host site.
211
-     *
212
-     * @param string            $original_timestamp
213
-     * @param EE_Datetime_Field $datetime_field
214
-     * @param                   $timezone_string
215
-     * @return string
216
-     * @throws DomainException
217
-     */
218
-    private static function getTimestampWithTimezoneOffset(
219
-        string $original_timestamp,
220
-        EE_Datetime_Field $datetime_field,
221
-        $timezone_string
222
-    ): string {
223
-        // already have timezone information?
224
-        if (preg_match('/Z|([+-])(\d{2}:\d{2})/', $original_timestamp)) {
225
-            // yes, we're ignoring the timezone.
226
-            return $original_timestamp;
227
-        }
228
-        // need to append timezone
229
-        [$offset_sign, $offset_secs] = self::parseTimezoneOffset(
230
-            $datetime_field->get_timezone_offset(
231
-                new DateTimeZone($timezone_string),
232
-                $original_timestamp
233
-            )
234
-        );
235
-        $offset_string =
236
-            str_pad(
237
-                floor($offset_secs / HOUR_IN_SECONDS),
238
-                2,
239
-                '0',
240
-                STR_PAD_LEFT
241
-            )
242
-            . ':'
243
-            . str_pad(
244
-                ($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
245
-                2,
246
-                '0',
247
-                STR_PAD_LEFT
248
-            );
249
-        return $original_timestamp . $offset_sign . $offset_string;
250
-    }
251
-
252
-
253
-    /**
254
-     * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
255
-     * think that can happen). If $data is an array, will recurse into its keys and values
256
-     *
257
-     * @param mixed $data
258
-     * @return void
259
-     * @throws RestException
260
-     */
261
-    public static function throwExceptionIfContainsSerializedData($data)
262
-    {
263
-        if (is_array($data)) {
264
-            foreach ($data as $key => $value) {
265
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
266
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
267
-            }
268
-        } else {
269
-            if (is_serialized($data) || is_object($data)) {
270
-                throw new RestException(
271
-                    'serialized_data_submission_prohibited',
272
-                    esc_html__(
273
-                    // @codingStandardsIgnoreStart
274
-                        'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
275
-                        // @codingStandardsIgnoreEnd
276
-                        'event_espresso'
277
-                    )
278
-                );
279
-            }
280
-        }
281
-    }
282
-
283
-
284
-    /**
285
-     * determines what's going on with them timezone strings
286
-     *
287
-     * @param int|string $timezone_offset
288
-     * @return array
289
-     */
290
-    private static function parseTimezoneOffset($timezone_offset): array
291
-    {
292
-        $first_char = substr((string) $timezone_offset, 0, 1);
293
-        if ($first_char === '+' || $first_char === '-') {
294
-            $offset_sign = $first_char;
295
-            $offset_secs = substr((string) $timezone_offset, 1);
296
-        } else {
297
-            $offset_sign = '+';
298
-            $offset_secs = $timezone_offset;
299
-        }
300
-        return [$offset_sign, $offset_secs];
301
-    }
302
-
303
-
304
-    /**
305
-     * Prepares a field's value for display in the API
306
-     *
307
-     * @param EE_Model_Field_Base $field_obj
308
-     * @param mixed               $original_value
309
-     * @param string              $requested_version
310
-     * @return mixed
311
-     * @throws EE_Error
312
-     * @throws EE_Error
313
-     */
314
-    public static function prepareFieldValueForJson(
315
-        EE_Model_Field_Base $field_obj,
316
-        $original_value,
317
-        string $requested_version
318
-    ) {
319
-        if ($original_value === EE_INF) {
320
-            $new_value = ModelDataTranslator::EE_INF_IN_REST;
321
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
322
-            if (is_string($original_value)) {
323
-                // did they submit a string of a unix timestamp?
324
-                if (is_numeric($original_value)) {
325
-                    $datetime_obj = new DateTime();
326
-                    $datetime_obj->setTimestamp((int) $original_value);
327
-                } else {
328
-                    // first, check if its a MySQL timestamp in GMT
329
-                    $datetime_obj = DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
330
-                }
331
-                if (! $datetime_obj instanceof DateTime) {
332
-                    // so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
333
-                    $datetime_obj = $field_obj->prepare_for_set($original_value);
334
-                }
335
-                $original_value = $datetime_obj;
336
-            }
337
-            if ($original_value instanceof DateTime) {
338
-                $new_value = $original_value->format('Y-m-d H:i:s');
339
-            } elseif (is_int($original_value) || is_float($original_value)) {
340
-                $new_value = date('Y-m-d H:i:s', $original_value);
341
-            } elseif ($original_value === null || $original_value === '') {
342
-                $new_value = null;
343
-            } else {
344
-                // so it's not a datetime object, unix timestamp (as string or int),
345
-                // MySQL timestamp, or even a string in the field object's format. So no idea what it is
346
-                throw new EE_Error(
347
-                    sprintf(
348
-                        esc_html__(
349
-                        // @codingStandardsIgnoreStart
350
-                            'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
351
-                            // @codingStandardsIgnoreEnd
352
-                            'event_espresso'
353
-                        ),
354
-                        $original_value,
355
-                        $field_obj->get_name(),
356
-                        $field_obj->get_model_name(),
357
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
358
-                    )
359
-                );
360
-            }
361
-            if ($new_value !== null) {
362
-                // phpcs:disable PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
363
-                $new_value = mysql_to_rfc3339($new_value);
364
-                // phpcs:enable
365
-            }
366
-        } else {
367
-            $new_value = $original_value;
368
-        }
369
-        // are we about to send an object? just don't. We have no good way to represent it in JSON.
370
-        // can't just check using is_object() because that missed PHP incomplete objects
371
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
372
-            $new_value = [
373
-                'error_code'    => 'php_object_not_return',
374
-                'error_message' => esc_html__(
375
-                    'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
376
-                    'event_espresso'
377
-                ),
378
-            ];
379
-        }
380
-        return apply_filters(
381
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
382
-            $new_value,
383
-            $field_obj,
384
-            $original_value,
385
-            $requested_version
386
-        );
387
-    }
388
-
389
-
390
-    /**
391
-     * Prepares condition-query-parameters (like what's in where and having) from
392
-     * the format expected in the API to use in the models
393
-     *
394
-     * @param array    $inputted_query_params_of_this_type
395
-     * @param EEM_Base $model
396
-     * @param string   $requested_version
397
-     * @param boolean  $writing whether this data will be written to the DB, or if we're just building a query.
398
-     *                          If we're writing to the DB, we don't expect any operators, or any logic query
399
-     *                          parameters, and we also won't accept serialized data unless the current user has
400
-     *                          unfiltered_html.
401
-     * @return array
402
-     * @throws DomainException
403
-     * @throws EE_Error
404
-     * @throws RestException
405
-     * @throws InvalidDataTypeException
406
-     * @throws InvalidInterfaceException
407
-     * @throws InvalidArgumentException
408
-     */
409
-    public static function prepareConditionsQueryParamsForModels(
410
-        array $inputted_query_params_of_this_type,
411
-        EEM_Base $model,
412
-        string $requested_version,
413
-        bool $writing = false
414
-    ): array {
415
-        $query_param_for_models = [];
416
-        $context                = new RestIncomingQueryParamContext($model, $requested_version, $writing);
417
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
418
-            $query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
419
-            if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
420
-                $translated_value = $query_param_meta->determineConditionsQueryParameterValue();
421
-                if (
422
-                    (isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ])
423
-                     && $query_param_meta->isGmtField())
424
-                    || $translated_value === null
425
-                ) {
426
-                    // they have already provided a non-gmt field, ignore the gmt one. That's what WP core
427
-                    // currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
428
-                    // OR we couldn't create a translated value from their input
429
-                    continue;
430
-                }
431
-                $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
432
-            } else {
433
-                $nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
434
-                if ($nested_query_params) {
435
-                    $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
436
-                }
437
-            }
438
-        }
439
-        return $query_param_for_models;
440
-    }
441
-
442
-
443
-    /**
444
-     * Mostly checks if the last 4 characters are "_gmt", indicating its a
445
-     * gmt date field name
446
-     *
447
-     * @param string $field_name
448
-     * @return boolean
449
-     */
450
-    public static function isGmtDateFieldName(string $field_name): bool
451
-    {
452
-        $field_name = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name);
453
-        return substr($field_name, -4, 4) === '_gmt';
454
-    }
455
-
456
-
457
-    /**
458
-     * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
459
-     *
460
-     * @param string $field_name
461
-     * @return string
462
-     */
463
-    public static function removeGmtFromFieldName(string $field_name): string
464
-    {
465
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
466
-            return $field_name;
467
-        }
468
-        $query_param_sans_stars              =
469
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
470
-                $field_name
471
-            );
472
-        $query_param_sans_gmt_and_sans_stars = substr(
473
-            $query_param_sans_stars,
474
-            0,
475
-            strrpos(
476
-                $field_name,
477
-                '_gmt'
478
-            )
479
-        );
480
-        return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
481
-    }
482
-
483
-
484
-    /**
485
-     * Takes a field name from the REST API and prepares it for the model querying
486
-     *
487
-     * @param string $field_name
488
-     * @return string
489
-     */
490
-    public static function prepareFieldNameFromJson(string $field_name): string
491
-    {
492
-        if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
493
-            return ModelDataTranslator::removeGmtFromFieldName($field_name);
494
-        }
495
-        return $field_name;
496
-    }
497
-
498
-
499
-    /**
500
-     * Takes array of field names from REST API and prepares for models
501
-     *
502
-     * @param array $field_names
503
-     * @return array of field names (possibly include model prefixes)
504
-     */
505
-    public static function prepareFieldNamesFromJson(array $field_names): array
506
-    {
507
-        $new_array = [];
508
-        foreach ($field_names as $key => $field_name) {
509
-            $new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
510
-        }
511
-        return $new_array;
512
-    }
513
-
514
-
515
-    /**
516
-     * Takes array where array keys are field names (possibly with model path prefixes)
517
-     * from the REST API and prepares them for model querying
518
-     *
519
-     * @param array $field_names_as_keys
520
-     * @return array
521
-     */
522
-    public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys): array
523
-    {
524
-        $new_array = [];
525
-        foreach ($field_names_as_keys as $field_name => $value) {
526
-            $new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
527
-        }
528
-        return $new_array;
529
-    }
530
-
531
-
532
-    /**
533
-     * Prepares an array of model query params for use in the REST API
534
-     *
535
-     * @param array       $model_query_params
536
-     * @param EEM_Base    $model
537
-     * @param string|null $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
538
-     *                                       REST API
539
-     * @return array which can be passed into the EE4 REST API when querying a model resource
540
-     * @throws EE_Error
541
-     * @throws ReflectionException
542
-     */
543
-    public static function prepareQueryParamsForRestApi(
544
-        array $model_query_params,
545
-        EEM_Base $model,
546
-        ?string $requested_version = null
547
-    ): array {
548
-        if ($requested_version === null) {
549
-            $requested_version = EED_Core_Rest_Api::latest_rest_api_version();
550
-        }
551
-        $rest_query_params = $model_query_params;
552
-        if (isset($model_query_params[0])) {
553
-            $rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
554
-                $model_query_params[0],
555
-                $model,
556
-                $requested_version
557
-            );
558
-            unset($rest_query_params[0]);
559
-        }
560
-        if (isset($model_query_params['having'])) {
561
-            $rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
562
-                $model_query_params['having'],
563
-                $model,
564
-                $requested_version
565
-            );
566
-        }
567
-        return apply_filters(
568
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
569
-            $rest_query_params,
570
-            $model_query_params,
571
-            $model,
572
-            $requested_version
573
-        );
574
-    }
575
-
576
-
577
-    /**
578
-     * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
579
-     *
580
-     * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
581
-     * @param EEM_Base $model
582
-     * @param string   $requested_version                   eg "4.8.36"
583
-     * @return array ready for use in the rest api query params
584
-     * @throws EE_Error
585
-     * @throws RestException if somehow a PHP object were in the query params' values,*@throws
586
-     * @throws ReflectionException
587
-     *                                                      ReflectionException
588
-     *                                                      (which would be really unusual)
589
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
590
-     */
591
-    public static function prepareConditionsQueryParamsForRestApi(
592
-        array $inputted_query_params_of_this_type,
593
-        EEM_Base $model,
594
-        string $requested_version
595
-    ): array {
596
-        $query_param_for_models = [];
597
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
598
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
599
-                ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
600
-                $model
601
-            );
602
-            if ($field instanceof EE_Model_Field_Base) {
603
-                // did they specify an operator?
604
-                if (is_array($query_param_value)) {
605
-                    $op               = $query_param_value[0];
606
-                    $translated_value = [$op];
607
-                    if (isset($query_param_value[1])) {
608
-                        $value               = $query_param_value[1];
609
-                        $translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
610
-                            $field,
611
-                            $value,
612
-                            $requested_version
613
-                        );
614
-                    }
615
-                } else {
616
-                    $translated_value = ModelDataTranslator::prepareFieldValueForJson(
617
-                        $field,
618
-                        $query_param_value,
619
-                        $requested_version
620
-                    );
621
-                }
622
-                $query_param_for_models[ $query_param_key ] = $translated_value;
623
-            } else {
624
-                // so it's not for a field, assume it's a logic query param key
625
-                $query_param_for_models[ $query_param_key ] =
626
-                    ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
627
-                        $query_param_value,
628
-                        $model,
629
-                        $requested_version
630
-                    );
631
-            }
632
-        }
633
-        return $query_param_for_models;
634
-    }
635
-
636
-
637
-    /**
638
-     * @param $condition_query_param_key
639
-     * @return string
640
-     */
641
-    public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key): string
642
-    {
643
-        $pos_of_star = strpos($condition_query_param_key, '*');
644
-        if ($pos_of_star === false) {
645
-            return $condition_query_param_key;
646
-        }
647
-        return substr($condition_query_param_key, 0, $pos_of_star);
648
-    }
649
-
650
-
651
-    /**
652
-     * Takes the input parameter and finds the model field that it indicates.
653
-     *
654
-     * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
655
-     * @param EEM_Base $model
656
-     * @return EE_Model_Field_Base
657
-     * @throws EE_Error
658
-     * @throws ReflectionException
659
-     */
660
-    public static function deduceFieldFromQueryParam(string $query_param_name, EEM_Base $model): ?EE_Model_Field_Base
661
-    {
662
-        // ok, now proceed with deducing which part is the model's name, and which is the field's name
663
-        // which will help us find the database table and column
664
-        $query_param_parts = explode('.', $query_param_name);
665
-        if (empty($query_param_parts)) {
666
-            throw new EE_Error(
667
-                sprintf(
668
-                    esc_html__(
669
-                        '_extract_column_name is empty when trying to extract column and table name from %s',
670
-                        'event_espresso'
671
-                    ),
672
-                    $query_param_name
673
-                )
674
-            );
675
-        }
676
-        $number_of_parts       = count($query_param_parts);
677
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
678
-        $field_name            = $last_query_param_part;
679
-        if ($number_of_parts !== 1) {
680
-            // the last part is the column name, and there are only 2parts. therefore...
681
-            $model = EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
682
-        }
683
-        try {
684
-            return $model->field_settings_for($field_name, false);
685
-        } catch (EE_Error $e) {
686
-            return null;
687
-        }
688
-    }
689
-
690
-
691
-    /**
692
-     * Returns true if $data can be easily represented in JSON.
693
-     * Basically, objects and resources can't be represented in JSON easily.
694
-     *
695
-     * @param mixed $data
696
-     * @return bool
697
-     */
698
-    protected static function isRepresentableInJson($data): bool
699
-    {
700
-        return is_scalar($data)
701
-               || is_array($data)
702
-               || is_null($data);
703
-    }
41
+	/**
42
+	 * We used to use -1 for infinity in the rest api, but that's ambiguous for
43
+	 * fields that COULD contain -1; so we use null
44
+	 */
45
+	const EE_INF_IN_REST = null;
46
+
47
+
48
+	/**
49
+	 * Prepares a possible array of input values from JSON for use by the models
50
+	 *
51
+	 * @param EE_Model_Field_Base $field_obj
52
+	 * @param mixed               $original_value_maybe_array
53
+	 * @param string              $requested_version
54
+	 * @param string              $timezone_string treat values as being in this timezone
55
+	 * @return mixed
56
+	 * @throws RestException
57
+	 * @throws EE_Error
58
+	 */
59
+	public static function prepareFieldValuesFromJson(
60
+		EE_Model_Field_Base $field_obj,
61
+		$original_value_maybe_array,
62
+		string $requested_version,
63
+		string $timezone_string = 'UTC'
64
+	) {
65
+		if (
66
+			is_array($original_value_maybe_array)
67
+			&& ! $field_obj instanceof EE_Serialized_Text_Field
68
+		) {
69
+			$new_value_maybe_array = [];
70
+			foreach ($original_value_maybe_array as $array_key => $array_item) {
71
+				$new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
72
+					$field_obj,
73
+					$array_item,
74
+					$requested_version,
75
+					$timezone_string
76
+				);
77
+			}
78
+		} else {
79
+			$new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
80
+				$field_obj,
81
+				$original_value_maybe_array,
82
+				$requested_version,
83
+				$timezone_string
84
+			);
85
+		}
86
+		return $new_value_maybe_array;
87
+	}
88
+
89
+
90
+	/**
91
+	 * Prepares an array of field values FOR use in JSON/REST API
92
+	 *
93
+	 * @param EE_Model_Field_Base $field_obj
94
+	 * @param mixed               $original_value_maybe_array
95
+	 * @param string              $request_version (eg 4.8.36)
96
+	 * @return mixed
97
+	 * @throws EE_Error
98
+	 * @throws EE_Error
99
+	 */
100
+	public static function prepareFieldValuesForJson(
101
+		EE_Model_Field_Base $field_obj,
102
+		$original_value_maybe_array,
103
+		string $request_version
104
+	) {
105
+		if (is_array($original_value_maybe_array)) {
106
+			$new_value = [];
107
+			foreach ($original_value_maybe_array as $key => $value) {
108
+				$new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
109
+					$field_obj,
110
+					$value,
111
+					$request_version
112
+				);
113
+			}
114
+		} else {
115
+			$new_value = ModelDataTranslator::prepareFieldValueForJson(
116
+				$field_obj,
117
+				$original_value_maybe_array,
118
+				$request_version
119
+			);
120
+		}
121
+		return $new_value;
122
+	}
123
+
124
+
125
+	/**
126
+	 * Prepares incoming data from the json or request parameters for the models'
127
+	 * "$query_params".
128
+	 *
129
+	 * @param EE_Model_Field_Base $field_obj
130
+	 * @param mixed               $original_value
131
+	 * @param string              $requested_version
132
+	 * @param string              $timezone_string treat values as being in this timezone
133
+	 * @return mixed
134
+	 * @throws RestException
135
+	 * @throws DomainException
136
+	 * @throws EE_Error
137
+	 */
138
+	public static function prepareFieldValueFromJson(
139
+		EE_Model_Field_Base $field_obj,
140
+		$original_value,
141
+		string $requested_version,
142
+		string $timezone_string = 'UTC'
143
+	) {
144
+		// check if they accidentally submitted an error value. If so throw an exception
145
+		if (
146
+			is_array($original_value)
147
+			&& isset($original_value['error_code'], $original_value['error_message'])
148
+		) {
149
+			throw new RestException(
150
+				'rest_submitted_error_value',
151
+				sprintf(
152
+					esc_html__(
153
+						'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
154
+						'event_espresso'
155
+					),
156
+					$field_obj->get_name()
157
+				),
158
+				[
159
+					'status' => 400,
160
+				]
161
+			);
162
+		}
163
+		// double-check for serialized PHP. We never accept serialized PHP. No way Jose.
164
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
165
+		$timezone_string =
166
+			$timezone_string !== ''
167
+				? $timezone_string
168
+				: get_option('timezone_string', '');
169
+		// walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
170
+		// way Jose.
171
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
172
+		if (
173
+			$field_obj instanceof EE_Infinite_Integer_Field
174
+			&& in_array($original_value, [null, ''], true)
175
+		) {
176
+			$new_value = EE_INF;
177
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
178
+			$new_value = rest_parse_date(
179
+				self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
180
+			);
181
+			if ($new_value === false) {
182
+				throw new RestException(
183
+					'invalid_format_for_timestamp',
184
+					sprintf(
185
+						esc_html__(
186
+							'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
187
+							'event_espresso'
188
+						),
189
+						'RFC3339',
190
+						'ISO8601',
191
+						$original_value
192
+					),
193
+					[
194
+						'status' => 400,
195
+					]
196
+				);
197
+			}
198
+		} elseif ($field_obj instanceof EE_Boolean_Field) {
199
+			// Interpreted the strings "false", "true", "on", "off" appropriately.
200
+			$new_value = filter_var($original_value, FILTER_VALIDATE_BOOLEAN);
201
+		} else {
202
+			$new_value = $original_value;
203
+		}
204
+		return $new_value;
205
+	}
206
+
207
+
208
+	/**
209
+	 * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
210
+	 * information via details obtained from the host site.
211
+	 *
212
+	 * @param string            $original_timestamp
213
+	 * @param EE_Datetime_Field $datetime_field
214
+	 * @param                   $timezone_string
215
+	 * @return string
216
+	 * @throws DomainException
217
+	 */
218
+	private static function getTimestampWithTimezoneOffset(
219
+		string $original_timestamp,
220
+		EE_Datetime_Field $datetime_field,
221
+		$timezone_string
222
+	): string {
223
+		// already have timezone information?
224
+		if (preg_match('/Z|([+-])(\d{2}:\d{2})/', $original_timestamp)) {
225
+			// yes, we're ignoring the timezone.
226
+			return $original_timestamp;
227
+		}
228
+		// need to append timezone
229
+		[$offset_sign, $offset_secs] = self::parseTimezoneOffset(
230
+			$datetime_field->get_timezone_offset(
231
+				new DateTimeZone($timezone_string),
232
+				$original_timestamp
233
+			)
234
+		);
235
+		$offset_string =
236
+			str_pad(
237
+				floor($offset_secs / HOUR_IN_SECONDS),
238
+				2,
239
+				'0',
240
+				STR_PAD_LEFT
241
+			)
242
+			. ':'
243
+			. str_pad(
244
+				($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
245
+				2,
246
+				'0',
247
+				STR_PAD_LEFT
248
+			);
249
+		return $original_timestamp . $offset_sign . $offset_string;
250
+	}
251
+
252
+
253
+	/**
254
+	 * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
255
+	 * think that can happen). If $data is an array, will recurse into its keys and values
256
+	 *
257
+	 * @param mixed $data
258
+	 * @return void
259
+	 * @throws RestException
260
+	 */
261
+	public static function throwExceptionIfContainsSerializedData($data)
262
+	{
263
+		if (is_array($data)) {
264
+			foreach ($data as $key => $value) {
265
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
266
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
267
+			}
268
+		} else {
269
+			if (is_serialized($data) || is_object($data)) {
270
+				throw new RestException(
271
+					'serialized_data_submission_prohibited',
272
+					esc_html__(
273
+					// @codingStandardsIgnoreStart
274
+						'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
275
+						// @codingStandardsIgnoreEnd
276
+						'event_espresso'
277
+					)
278
+				);
279
+			}
280
+		}
281
+	}
282
+
283
+
284
+	/**
285
+	 * determines what's going on with them timezone strings
286
+	 *
287
+	 * @param int|string $timezone_offset
288
+	 * @return array
289
+	 */
290
+	private static function parseTimezoneOffset($timezone_offset): array
291
+	{
292
+		$first_char = substr((string) $timezone_offset, 0, 1);
293
+		if ($first_char === '+' || $first_char === '-') {
294
+			$offset_sign = $first_char;
295
+			$offset_secs = substr((string) $timezone_offset, 1);
296
+		} else {
297
+			$offset_sign = '+';
298
+			$offset_secs = $timezone_offset;
299
+		}
300
+		return [$offset_sign, $offset_secs];
301
+	}
302
+
303
+
304
+	/**
305
+	 * Prepares a field's value for display in the API
306
+	 *
307
+	 * @param EE_Model_Field_Base $field_obj
308
+	 * @param mixed               $original_value
309
+	 * @param string              $requested_version
310
+	 * @return mixed
311
+	 * @throws EE_Error
312
+	 * @throws EE_Error
313
+	 */
314
+	public static function prepareFieldValueForJson(
315
+		EE_Model_Field_Base $field_obj,
316
+		$original_value,
317
+		string $requested_version
318
+	) {
319
+		if ($original_value === EE_INF) {
320
+			$new_value = ModelDataTranslator::EE_INF_IN_REST;
321
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
322
+			if (is_string($original_value)) {
323
+				// did they submit a string of a unix timestamp?
324
+				if (is_numeric($original_value)) {
325
+					$datetime_obj = new DateTime();
326
+					$datetime_obj->setTimestamp((int) $original_value);
327
+				} else {
328
+					// first, check if its a MySQL timestamp in GMT
329
+					$datetime_obj = DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
330
+				}
331
+				if (! $datetime_obj instanceof DateTime) {
332
+					// so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
333
+					$datetime_obj = $field_obj->prepare_for_set($original_value);
334
+				}
335
+				$original_value = $datetime_obj;
336
+			}
337
+			if ($original_value instanceof DateTime) {
338
+				$new_value = $original_value->format('Y-m-d H:i:s');
339
+			} elseif (is_int($original_value) || is_float($original_value)) {
340
+				$new_value = date('Y-m-d H:i:s', $original_value);
341
+			} elseif ($original_value === null || $original_value === '') {
342
+				$new_value = null;
343
+			} else {
344
+				// so it's not a datetime object, unix timestamp (as string or int),
345
+				// MySQL timestamp, or even a string in the field object's format. So no idea what it is
346
+				throw new EE_Error(
347
+					sprintf(
348
+						esc_html__(
349
+						// @codingStandardsIgnoreStart
350
+							'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
351
+							// @codingStandardsIgnoreEnd
352
+							'event_espresso'
353
+						),
354
+						$original_value,
355
+						$field_obj->get_name(),
356
+						$field_obj->get_model_name(),
357
+						$field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
358
+					)
359
+				);
360
+			}
361
+			if ($new_value !== null) {
362
+				// phpcs:disable PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
363
+				$new_value = mysql_to_rfc3339($new_value);
364
+				// phpcs:enable
365
+			}
366
+		} else {
367
+			$new_value = $original_value;
368
+		}
369
+		// are we about to send an object? just don't. We have no good way to represent it in JSON.
370
+		// can't just check using is_object() because that missed PHP incomplete objects
371
+		if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
372
+			$new_value = [
373
+				'error_code'    => 'php_object_not_return',
374
+				'error_message' => esc_html__(
375
+					'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
376
+					'event_espresso'
377
+				),
378
+			];
379
+		}
380
+		return apply_filters(
381
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
382
+			$new_value,
383
+			$field_obj,
384
+			$original_value,
385
+			$requested_version
386
+		);
387
+	}
388
+
389
+
390
+	/**
391
+	 * Prepares condition-query-parameters (like what's in where and having) from
392
+	 * the format expected in the API to use in the models
393
+	 *
394
+	 * @param array    $inputted_query_params_of_this_type
395
+	 * @param EEM_Base $model
396
+	 * @param string   $requested_version
397
+	 * @param boolean  $writing whether this data will be written to the DB, or if we're just building a query.
398
+	 *                          If we're writing to the DB, we don't expect any operators, or any logic query
399
+	 *                          parameters, and we also won't accept serialized data unless the current user has
400
+	 *                          unfiltered_html.
401
+	 * @return array
402
+	 * @throws DomainException
403
+	 * @throws EE_Error
404
+	 * @throws RestException
405
+	 * @throws InvalidDataTypeException
406
+	 * @throws InvalidInterfaceException
407
+	 * @throws InvalidArgumentException
408
+	 */
409
+	public static function prepareConditionsQueryParamsForModels(
410
+		array $inputted_query_params_of_this_type,
411
+		EEM_Base $model,
412
+		string $requested_version,
413
+		bool $writing = false
414
+	): array {
415
+		$query_param_for_models = [];
416
+		$context                = new RestIncomingQueryParamContext($model, $requested_version, $writing);
417
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
418
+			$query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
419
+			if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
420
+				$translated_value = $query_param_meta->determineConditionsQueryParameterValue();
421
+				if (
422
+					(isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ])
423
+					 && $query_param_meta->isGmtField())
424
+					|| $translated_value === null
425
+				) {
426
+					// they have already provided a non-gmt field, ignore the gmt one. That's what WP core
427
+					// currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
428
+					// OR we couldn't create a translated value from their input
429
+					continue;
430
+				}
431
+				$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
432
+			} else {
433
+				$nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
434
+				if ($nested_query_params) {
435
+					$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
436
+				}
437
+			}
438
+		}
439
+		return $query_param_for_models;
440
+	}
441
+
442
+
443
+	/**
444
+	 * Mostly checks if the last 4 characters are "_gmt", indicating its a
445
+	 * gmt date field name
446
+	 *
447
+	 * @param string $field_name
448
+	 * @return boolean
449
+	 */
450
+	public static function isGmtDateFieldName(string $field_name): bool
451
+	{
452
+		$field_name = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name);
453
+		return substr($field_name, -4, 4) === '_gmt';
454
+	}
455
+
456
+
457
+	/**
458
+	 * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
459
+	 *
460
+	 * @param string $field_name
461
+	 * @return string
462
+	 */
463
+	public static function removeGmtFromFieldName(string $field_name): string
464
+	{
465
+		if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
466
+			return $field_name;
467
+		}
468
+		$query_param_sans_stars              =
469
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
470
+				$field_name
471
+			);
472
+		$query_param_sans_gmt_and_sans_stars = substr(
473
+			$query_param_sans_stars,
474
+			0,
475
+			strrpos(
476
+				$field_name,
477
+				'_gmt'
478
+			)
479
+		);
480
+		return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
481
+	}
482
+
483
+
484
+	/**
485
+	 * Takes a field name from the REST API and prepares it for the model querying
486
+	 *
487
+	 * @param string $field_name
488
+	 * @return string
489
+	 */
490
+	public static function prepareFieldNameFromJson(string $field_name): string
491
+	{
492
+		if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
493
+			return ModelDataTranslator::removeGmtFromFieldName($field_name);
494
+		}
495
+		return $field_name;
496
+	}
497
+
498
+
499
+	/**
500
+	 * Takes array of field names from REST API and prepares for models
501
+	 *
502
+	 * @param array $field_names
503
+	 * @return array of field names (possibly include model prefixes)
504
+	 */
505
+	public static function prepareFieldNamesFromJson(array $field_names): array
506
+	{
507
+		$new_array = [];
508
+		foreach ($field_names as $key => $field_name) {
509
+			$new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
510
+		}
511
+		return $new_array;
512
+	}
513
+
514
+
515
+	/**
516
+	 * Takes array where array keys are field names (possibly with model path prefixes)
517
+	 * from the REST API and prepares them for model querying
518
+	 *
519
+	 * @param array $field_names_as_keys
520
+	 * @return array
521
+	 */
522
+	public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys): array
523
+	{
524
+		$new_array = [];
525
+		foreach ($field_names_as_keys as $field_name => $value) {
526
+			$new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
527
+		}
528
+		return $new_array;
529
+	}
530
+
531
+
532
+	/**
533
+	 * Prepares an array of model query params for use in the REST API
534
+	 *
535
+	 * @param array       $model_query_params
536
+	 * @param EEM_Base    $model
537
+	 * @param string|null $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
538
+	 *                                       REST API
539
+	 * @return array which can be passed into the EE4 REST API when querying a model resource
540
+	 * @throws EE_Error
541
+	 * @throws ReflectionException
542
+	 */
543
+	public static function prepareQueryParamsForRestApi(
544
+		array $model_query_params,
545
+		EEM_Base $model,
546
+		?string $requested_version = null
547
+	): array {
548
+		if ($requested_version === null) {
549
+			$requested_version = EED_Core_Rest_Api::latest_rest_api_version();
550
+		}
551
+		$rest_query_params = $model_query_params;
552
+		if (isset($model_query_params[0])) {
553
+			$rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
554
+				$model_query_params[0],
555
+				$model,
556
+				$requested_version
557
+			);
558
+			unset($rest_query_params[0]);
559
+		}
560
+		if (isset($model_query_params['having'])) {
561
+			$rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
562
+				$model_query_params['having'],
563
+				$model,
564
+				$requested_version
565
+			);
566
+		}
567
+		return apply_filters(
568
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
569
+			$rest_query_params,
570
+			$model_query_params,
571
+			$model,
572
+			$requested_version
573
+		);
574
+	}
575
+
576
+
577
+	/**
578
+	 * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
579
+	 *
580
+	 * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
581
+	 * @param EEM_Base $model
582
+	 * @param string   $requested_version                   eg "4.8.36"
583
+	 * @return array ready for use in the rest api query params
584
+	 * @throws EE_Error
585
+	 * @throws RestException if somehow a PHP object were in the query params' values,*@throws
586
+	 * @throws ReflectionException
587
+	 *                                                      ReflectionException
588
+	 *                                                      (which would be really unusual)
589
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
590
+	 */
591
+	public static function prepareConditionsQueryParamsForRestApi(
592
+		array $inputted_query_params_of_this_type,
593
+		EEM_Base $model,
594
+		string $requested_version
595
+	): array {
596
+		$query_param_for_models = [];
597
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
598
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
599
+				ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
600
+				$model
601
+			);
602
+			if ($field instanceof EE_Model_Field_Base) {
603
+				// did they specify an operator?
604
+				if (is_array($query_param_value)) {
605
+					$op               = $query_param_value[0];
606
+					$translated_value = [$op];
607
+					if (isset($query_param_value[1])) {
608
+						$value               = $query_param_value[1];
609
+						$translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
610
+							$field,
611
+							$value,
612
+							$requested_version
613
+						);
614
+					}
615
+				} else {
616
+					$translated_value = ModelDataTranslator::prepareFieldValueForJson(
617
+						$field,
618
+						$query_param_value,
619
+						$requested_version
620
+					);
621
+				}
622
+				$query_param_for_models[ $query_param_key ] = $translated_value;
623
+			} else {
624
+				// so it's not for a field, assume it's a logic query param key
625
+				$query_param_for_models[ $query_param_key ] =
626
+					ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
627
+						$query_param_value,
628
+						$model,
629
+						$requested_version
630
+					);
631
+			}
632
+		}
633
+		return $query_param_for_models;
634
+	}
635
+
636
+
637
+	/**
638
+	 * @param $condition_query_param_key
639
+	 * @return string
640
+	 */
641
+	public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key): string
642
+	{
643
+		$pos_of_star = strpos($condition_query_param_key, '*');
644
+		if ($pos_of_star === false) {
645
+			return $condition_query_param_key;
646
+		}
647
+		return substr($condition_query_param_key, 0, $pos_of_star);
648
+	}
649
+
650
+
651
+	/**
652
+	 * Takes the input parameter and finds the model field that it indicates.
653
+	 *
654
+	 * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
655
+	 * @param EEM_Base $model
656
+	 * @return EE_Model_Field_Base
657
+	 * @throws EE_Error
658
+	 * @throws ReflectionException
659
+	 */
660
+	public static function deduceFieldFromQueryParam(string $query_param_name, EEM_Base $model): ?EE_Model_Field_Base
661
+	{
662
+		// ok, now proceed with deducing which part is the model's name, and which is the field's name
663
+		// which will help us find the database table and column
664
+		$query_param_parts = explode('.', $query_param_name);
665
+		if (empty($query_param_parts)) {
666
+			throw new EE_Error(
667
+				sprintf(
668
+					esc_html__(
669
+						'_extract_column_name is empty when trying to extract column and table name from %s',
670
+						'event_espresso'
671
+					),
672
+					$query_param_name
673
+				)
674
+			);
675
+		}
676
+		$number_of_parts       = count($query_param_parts);
677
+		$last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
678
+		$field_name            = $last_query_param_part;
679
+		if ($number_of_parts !== 1) {
680
+			// the last part is the column name, and there are only 2parts. therefore...
681
+			$model = EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
682
+		}
683
+		try {
684
+			return $model->field_settings_for($field_name, false);
685
+		} catch (EE_Error $e) {
686
+			return null;
687
+		}
688
+	}
689
+
690
+
691
+	/**
692
+	 * Returns true if $data can be easily represented in JSON.
693
+	 * Basically, objects and resources can't be represented in JSON easily.
694
+	 *
695
+	 * @param mixed $data
696
+	 * @return bool
697
+	 */
698
+	protected static function isRepresentableInJson($data): bool
699
+	{
700
+		return is_scalar($data)
701
+			   || is_array($data)
702
+			   || is_null($data);
703
+	}
704 704
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -68,7 +68,7 @@  discard block
 block discarded – undo
68 68
         ) {
69 69
             $new_value_maybe_array = [];
70 70
             foreach ($original_value_maybe_array as $array_key => $array_item) {
71
-                $new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
71
+                $new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
72 72
                     $field_obj,
73 73
                     $array_item,
74 74
                     $requested_version,
@@ -105,7 +105,7 @@  discard block
 block discarded – undo
105 105
         if (is_array($original_value_maybe_array)) {
106 106
             $new_value = [];
107 107
             foreach ($original_value_maybe_array as $key => $value) {
108
-                $new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
108
+                $new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson(
109 109
                     $field_obj,
110 110
                     $value,
111 111
                     $request_version
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
                 '0',
247 247
                 STR_PAD_LEFT
248 248
             );
249
-        return $original_timestamp . $offset_sign . $offset_string;
249
+        return $original_timestamp.$offset_sign.$offset_string;
250 250
     }
251 251
 
252 252
 
@@ -328,7 +328,7 @@  discard block
 block discarded – undo
328 328
                     // first, check if its a MySQL timestamp in GMT
329 329
                     $datetime_obj = DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
330 330
                 }
331
-                if (! $datetime_obj instanceof DateTime) {
331
+                if ( ! $datetime_obj instanceof DateTime) {
332 332
                     // so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
333 333
                     $datetime_obj = $field_obj->prepare_for_set($original_value);
334 334
                 }
@@ -354,7 +354,7 @@  discard block
 block discarded – undo
354 354
                         $original_value,
355 355
                         $field_obj->get_name(),
356 356
                         $field_obj->get_model_name(),
357
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
357
+                        $field_obj->get_time_format().' '.$field_obj->get_time_format()
358 358
                     )
359 359
                 );
360 360
             }
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
         }
369 369
         // are we about to send an object? just don't. We have no good way to represent it in JSON.
370 370
         // can't just check using is_object() because that missed PHP incomplete objects
371
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
371
+        if ( ! ModelDataTranslator::isRepresentableInJson($new_value)) {
372 372
             $new_value = [
373 373
                 'error_code'    => 'php_object_not_return',
374 374
                 'error_message' => esc_html__(
@@ -419,7 +419,7 @@  discard block
 block discarded – undo
419 419
             if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
420 420
                 $translated_value = $query_param_meta->determineConditionsQueryParameterValue();
421 421
                 if (
422
-                    (isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ])
422
+                    (isset($query_param_for_models[$query_param_meta->getQueryParamKey()])
423 423
                      && $query_param_meta->isGmtField())
424 424
                     || $translated_value === null
425 425
                 ) {
@@ -428,11 +428,11 @@  discard block
 block discarded – undo
428 428
                     // OR we couldn't create a translated value from their input
429 429
                     continue;
430 430
                 }
431
-                $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
431
+                $query_param_for_models[$query_param_meta->getQueryParamKey()] = $translated_value;
432 432
             } else {
433 433
                 $nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
434 434
                 if ($nested_query_params) {
435
-                    $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
435
+                    $query_param_for_models[$query_param_meta->getQueryParamKey()] = $nested_query_params;
436 436
                 }
437 437
             }
438 438
         }
@@ -462,10 +462,10 @@  discard block
 block discarded – undo
462 462
      */
463 463
     public static function removeGmtFromFieldName(string $field_name): string
464 464
     {
465
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
465
+        if ( ! ModelDataTranslator::isGmtDateFieldName($field_name)) {
466 466
             return $field_name;
467 467
         }
468
-        $query_param_sans_stars              =
468
+        $query_param_sans_stars =
469 469
             ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
470 470
                 $field_name
471 471
             );
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
     {
507 507
         $new_array = [];
508 508
         foreach ($field_names as $key => $field_name) {
509
-            $new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
509
+            $new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
510 510
         }
511 511
         return $new_array;
512 512
     }
@@ -523,7 +523,7 @@  discard block
 block discarded – undo
523 523
     {
524 524
         $new_array = [];
525 525
         foreach ($field_names_as_keys as $field_name => $value) {
526
-            $new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
526
+            $new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
527 527
         }
528 528
         return $new_array;
529 529
     }
@@ -619,10 +619,10 @@  discard block
 block discarded – undo
619 619
                         $requested_version
620 620
                     );
621 621
                 }
622
-                $query_param_for_models[ $query_param_key ] = $translated_value;
622
+                $query_param_for_models[$query_param_key] = $translated_value;
623 623
             } else {
624 624
                 // so it's not for a field, assume it's a logic query param key
625
-                $query_param_for_models[ $query_param_key ] =
625
+                $query_param_for_models[$query_param_key] =
626 626
                     ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
627 627
                         $query_param_value,
628 628
                         $model,
@@ -674,11 +674,11 @@  discard block
 block discarded – undo
674 674
             );
675 675
         }
676 676
         $number_of_parts       = count($query_param_parts);
677
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
677
+        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
678 678
         $field_name            = $last_query_param_part;
679 679
         if ($number_of_parts !== 1) {
680 680
             // the last part is the column name, and there are only 2parts. therefore...
681
-            $model = EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
681
+            $model = EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
682 682
         }
683 683
         try {
684 684
             return $model->field_settings_for($field_name, false);
Please login to merge, or discard this patch.
core/libraries/rest_api/RestException.php 1 patch
Indentation   +54 added lines, -54 removed lines patch added patch discarded remove patch
@@ -17,65 +17,65 @@
 block discarded – undo
17 17
  */
18 18
 class RestException extends EE_Error
19 19
 {
20
-    /**
21
-     * @var array
22
-     */
23
-    protected $wp_error_data = [];
20
+	/**
21
+	 * @var array
22
+	 */
23
+	protected $wp_error_data = [];
24 24
 
25
-    /**
26
-     * @var string
27
-     */
28
-    protected $wp_error_code = '';
25
+	/**
26
+	 * @var string
27
+	 */
28
+	protected $wp_error_code = '';
29 29
 
30 30
 
31
-    /**
32
-     * @param string         $string_code
33
-     * @param string         $message
34
-     * @param array          $wp_error_data
35
-     * @param Exception|null $previous
36
-     */
37
-    public function __construct(
38
-        string $string_code,
39
-        string $message,
40
-        array $wp_error_data = [],
41
-        Exception $previous = null
42
-    ) {
43
-        if (
44
-            is_array($wp_error_data)
45
-            && isset($wp_error_data['status'])
46
-        ) {
47
-            $http_status_number = $wp_error_data['status'];
48
-        } else {
49
-            $http_status_number = 500;
50
-        }
51
-        parent::__construct(
52
-            $message,
53
-            $http_status_number,
54
-            $previous
55
-        );
56
-        $this->wp_error_data = $wp_error_data;
57
-        $this->wp_error_code = $string_code;
58
-    }
31
+	/**
32
+	 * @param string         $string_code
33
+	 * @param string         $message
34
+	 * @param array          $wp_error_data
35
+	 * @param Exception|null $previous
36
+	 */
37
+	public function __construct(
38
+		string $string_code,
39
+		string $message,
40
+		array $wp_error_data = [],
41
+		Exception $previous = null
42
+	) {
43
+		if (
44
+			is_array($wp_error_data)
45
+			&& isset($wp_error_data['status'])
46
+		) {
47
+			$http_status_number = $wp_error_data['status'];
48
+		} else {
49
+			$http_status_number = 500;
50
+		}
51
+		parent::__construct(
52
+			$message,
53
+			$http_status_number,
54
+			$previous
55
+		);
56
+		$this->wp_error_data = $wp_error_data;
57
+		$this->wp_error_code = $string_code;
58
+	}
59 59
 
60 60
 
61
-    /**
62
-     * Array of data that may have been set during the constructor, intended for WP_Error's data
63
-     *
64
-     * @return array
65
-     */
66
-    public function getData(): array
67
-    {
68
-        return $this->wp_error_data;
69
-    }
61
+	/**
62
+	 * Array of data that may have been set during the constructor, intended for WP_Error's data
63
+	 *
64
+	 * @return array
65
+	 */
66
+	public function getData(): array
67
+	{
68
+		return $this->wp_error_data;
69
+	}
70 70
 
71 71
 
72
-    /**
73
-     * Gets the error string
74
-     *
75
-     * @return string
76
-     */
77
-    public function getStringCode(): string
78
-    {
79
-        return $this->wp_error_code;
80
-    }
72
+	/**
73
+	 * Gets the error string
74
+	 *
75
+	 * @return string
76
+	 */
77
+	public function getStringCode(): string
78
+	{
79
+		return $this->wp_error_code;
80
+	}
81 81
 }
Please login to merge, or discard this patch.
core/libraries/rest_api/ModelVersionInfo.php 2 patches
Indentation   +441 added lines, -441 removed lines patch added patch discarded remove patch
@@ -25,445 +25,445 @@
 block discarded – undo
25 25
  */
26 26
 class ModelVersionInfo
27 27
 {
28
-    /**
29
-     * Constant used in the $_model_changes array to indicate that a model
30
-     * was completely new in this version
31
-     */
32
-    const MODEL_ADDED = 'model_added_in_this_version';
33
-
34
-    /**
35
-     * Top-level keys are versions (major and minor version numbers, eg "4.6")
36
-     * next-level keys are model names (eg "Event") that underwent some change in that version
37
-     * and the value is either Model_Version_Info::model_added (indicating the model is completely NEW in this version),
38
-     * or it's an array where the values are model field names,
39
-     * or API resource properties (ie, non-model fields that appear in REST API results)
40
-     * If a version is missing then we don't know anything about what changes it introduced from the previous version
41
-     *
42
-     * @var array
43
-     */
44
-    protected $model_changes = [];
45
-
46
-    /**
47
-     * top-level keys are version numbers,
48
-     * next-level keys are model CLASSNAMES (even parent classnames),
49
-     * and next-level keys are extra resource properties to attach to those models' resources,
50
-     * and next-level key-value pairs, where the keys are:
51
-     * 'raw', 'type', 'nullable', 'table_alias', 'table_column',  'always_available'
52
-     *
53
-     * @var array
54
-     */
55
-    protected $resource_changes = [];
56
-
57
-    /**
58
-     * @var string indicating what version of the API was requested
59
-     * (eg although core might be at version 4.8.11, they may have sent a request
60
-     * for 4.6)
61
-     */
62
-    protected $requested_version = null;
63
-
64
-    /**
65
-     * Keys are model names, values are their classnames.
66
-     * We cache this so we only need to calculate this once per request
67
-     *
68
-     * @var array
69
-     */
70
-    protected $cached_models_for_requested_version = null;
71
-
72
-    /**
73
-     * @var array
74
-     */
75
-    protected $cached_model_changes_between_requested_version_and_current = null;
76
-
77
-    /**
78
-     * @var array
79
-     */
80
-    protected $cached_resource_changes_between_requested_version_and_current = null;
81
-
82
-    /**
83
-     * 2d array where top-level keys are model names, 2nd-level keys are field names
84
-     * and values are the actual field objects
85
-     *
86
-     * @var array
87
-     */
88
-    protected $cached_fields_on_models = [];
89
-
90
-
91
-    /**
92
-     * Model_Version_Info constructor.
93
-     *
94
-     * @param string $requested_version
95
-     */
96
-    public function __construct(string $requested_version)
97
-    {
98
-        $this->requested_version = $requested_version;
99
-        $this->model_changes     = [
100
-            '4.8.29' => [
101
-                // first version where the REST API is in EE core, so no need
102
-                // to specify how its different from the previous
103
-            ],
104
-        ];
105
-        // setup data for "extra" fields added onto resources which don't actually exist on models
106
-        $this->resource_changes = apply_filters(
107
-            'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
108
-            []
109
-        );
110
-        $defaults               = [
111
-            'raw'              => false,
112
-            'type'             => 'N/A',
113
-            'nullable'         => true,
114
-            'table_alias'      => 'N/A',
115
-            'table_column'     => 'N/A',
116
-            'always_available' => true,
117
-        ];
118
-        foreach ($this->resource_changes as $model_classnames) {
119
-            foreach ($model_classnames as $model_classname => $extra_fields) {
120
-                foreach ($extra_fields as $field_name => $field_data) {
121
-                    $this->resource_changes[ $model_classname ][ $field_name ]['name'] = $field_name;
122
-                    foreach ($defaults as $attribute => $default_value) {
123
-                        if (! isset($this->resource_changes[ $model_classname ][ $field_name ][ $attribute ])) {
124
-                            $this->resource_changes[ $model_classname ][ $field_name ][ $attribute ] = $default_value;
125
-                        }
126
-                    }
127
-                }
128
-            }
129
-        }
130
-    }
131
-
132
-
133
-    /**
134
-     * Returns a slice of Model_Version_Info::model_changes()'s array
135
-     * indicating exactly what changes happened between the current core version,
136
-     * and the version requested
137
-     *
138
-     * @return array
139
-     */
140
-    public function modelChangesBetweenRequestedVersionAndCurrent(): array
141
-    {
142
-        if ($this->cached_model_changes_between_requested_version_and_current === null) {
143
-            $model_changes = [];
144
-            foreach ($this->modelChanges() as $version => $models_changed_in_version) {
145
-                if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
146
-                    $model_changes[ $version ] = $models_changed_in_version;
147
-                }
148
-            }
149
-            $this->cached_model_changes_between_requested_version_and_current = $model_changes;
150
-        }
151
-        return $this->cached_model_changes_between_requested_version_and_current;
152
-    }
153
-
154
-
155
-    /**
156
-     * Returns a slice of Model_Version_Info::model_changes()'s array
157
-     * indicating exactly what changes happened between the current core version,
158
-     * and the version requested
159
-     *
160
-     * @return array
161
-     */
162
-    public function resourceChangesBetweenRequestedVersionAndCurrent(): array
163
-    {
164
-        if ($this->cached_resource_changes_between_requested_version_and_current === null) {
165
-            $resource_changes = [];
166
-            foreach ($this->resourceChanges() as $version => $model_classnames) {
167
-                if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
168
-                    $resource_changes[ $version ] = $model_classnames;
169
-                }
170
-            }
171
-            $this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
172
-        }
173
-        return $this->cached_resource_changes_between_requested_version_and_current;
174
-    }
175
-
176
-
177
-    /**
178
-     * If a request was sent to 'wp-json/ee/v4.7/events' this would be '4.7'
179
-     *
180
-     * @return string like '4.6'
181
-     */
182
-    public function requestedVersion(): ?string
183
-    {
184
-        return $this->requested_version;
185
-    }
186
-
187
-
188
-    /**
189
-     * Returns an array describing how the models have changed in each version of core
190
-     * that supports the API (starting at 4.6)
191
-     * Top-level keys are versions (major and minor version numbers, eg "4.6")
192
-     * next-level keys are model names (eg "Event") that underwent some change in that version
193
-     * and the value is either NULL (indicating the model is completely NEW in this version),
194
-     * or it's an array where fields are value names.
195
-     * If a version is missing then we don't know anything about what changes it introduced from the previous version
196
-     *
197
-     * @return array
198
-     */
199
-    public function modelChanges(): array
200
-    {
201
-        return $this->model_changes;
202
-    }
203
-
204
-
205
-    /**
206
-     * Takes into account the requested version, and the current version, and
207
-     * what changed between the two, and tries to return.
208
-     * Analogous to EE_Registry::instance()->non_abstract_db_models
209
-     *
210
-     * @return array keys are model names, values are their classname
211
-     */
212
-    public function modelsForRequestedVersion(): array
213
-    {
214
-        if ($this->cached_models_for_requested_version === null) {
215
-            $all_models_in_current_version = EE_Registry::instance()->non_abstract_db_models;
216
-            foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
217
-                foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
218
-                    if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
219
-                        unset($all_models_in_current_version[ $model_name ]);
220
-                    }
221
-                }
222
-            }
223
-            $this->cached_models_for_requested_version = apply_filters(
224
-                'FHEE__EventEspresso_core_libraries_rest_api__models_for_requested_version',
225
-                $all_models_in_current_version,
226
-                $this
227
-            );
228
-        }
229
-        return $this->cached_models_for_requested_version;
230
-    }
231
-
232
-
233
-    /**
234
-     * Determines if this is a valid model name in the requested version.
235
-     * Similar to EE_Registry::instance()->is_model_name(), but takes the requested
236
-     * version's models into account
237
-     *
238
-     * @param string $model_name eg 'Event'
239
-     * @return boolean
240
-     */
241
-    public function isModelNameInThisVersion(string $model_name): bool
242
-    {
243
-        $model_names = $this->modelsForRequestedVersion();
244
-        if (isset($model_names[ $model_name ])) {
245
-            return true;
246
-        } else {
247
-            return false;
248
-        }
249
-    }
250
-
251
-
252
-    /**
253
-     * Wrapper for EE_Registry::instance()->load_model(), but takes the requested
254
-     * version's models into account
255
-     *
256
-     * @param string $model_name
257
-     * @return EEM_Base
258
-     * @throws EE_Error
259
-     * @throws ReflectionException
260
-     */
261
-    public function loadModel(string $model_name): EEM_Base
262
-    {
263
-        if ($this->isModelNameInThisVersion($model_name)) {
264
-            return EE_Registry::instance()->load_model($model_name);
265
-        } else {
266
-            throw new EE_Error(
267
-                sprintf(
268
-                    esc_html__(
269
-                        'Cannot load model "%1$s" because it does not exist in version %2$s of Event Espresso',
270
-                        'event_espresso'
271
-                    ),
272
-                    $model_name,
273
-                    $this->requestedVersion()
274
-                )
275
-            );
276
-        }
277
-    }
278
-
279
-
280
-    /**
281
-     * Gets all the fields that should exist on this model right now
282
-     *
283
-     * @param EEM_Base $model
284
-     * @return array|EE_Model_Field_Base[]
285
-     */
286
-    public function fieldsOnModelInThisVersion(EEM_Base $model): array
287
-    {
288
-        if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
289
-            // get all model changes between the requested version and current core version
290
-            $changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
291
-            // fetch all fields currently on this model
292
-            $current_fields = $model->field_settings();
293
-            // remove all fields that have been added since
294
-            foreach ($changes as $changes_in_version) {
295
-                if (
296
-                    isset($changes_in_version[ $model->get_this_model_name() ])
297
-                    && $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
298
-                ) {
299
-                    $current_fields = array_diff_key(
300
-                        $current_fields,
301
-                        array_flip($changes_in_version[ $model->get_this_model_name() ])
302
-                    );
303
-                }
304
-            }
305
-            $this->cached_fields_on_models = $current_fields;
306
-        }
307
-        return $this->cached_fields_on_models;
308
-    }
309
-
310
-
311
-    /**
312
-     * Determines if $object is of one of the classes of $classes. Similar to
313
-     * in_array(), except this checks if $object is a subclass of the classnames provided
314
-     * in $classnames
315
-     *
316
-     * @param object $object
317
-     * @param array  $classnames
318
-     * @return boolean
319
-     */
320
-    public function isSubclassOfOne($object, array $classnames): bool
321
-    {
322
-        foreach ($classnames as $classname) {
323
-            if (is_a($object, $classname)) {
324
-                return true;
325
-            }
326
-        }
327
-        return false;
328
-    }
329
-
330
-
331
-    /**
332
-     * Returns the list of model field classes that that the API basically ignores
333
-     *
334
-     * @return array
335
-     */
336
-    public function fieldsIgnored(): array
337
-    {
338
-        return apply_filters(
339
-            'FHEE__Controller_Model_Read_fields_ignored',
340
-            []
341
-        );
342
-    }
343
-
344
-
345
-    /**
346
-     * If this field one that should be ignored by the API?
347
-     *
348
-     * @param EE_Model_Field_Base
349
-     * @return boolean
350
-     */
351
-    public function fieldIsIgnored($field_obj): bool
352
-    {
353
-        return $this->isSubclassOfOne($field_obj, $this->fieldsIgnored());
354
-    }
355
-
356
-
357
-    /**
358
-     * Returns the list of model field classes that have a "raw" and non-raw formats.
359
-     * Normally the "raw" versions are only accessible to those who can edit them.
360
-     *
361
-     * @return array an array of EE_Model_Field_Base child classnames
362
-     */
363
-    public function fieldsThatHaveRenderedFormat(): array
364
-    {
365
-        return apply_filters(
366
-            'FHEE__Controller_Model_Read__fields_raw',
367
-            ['EE_Post_Content_Field', 'EE_Full_HTML_Field']
368
-        );
369
-    }
370
-
371
-
372
-    /**
373
-     * If this field one that has a raw format
374
-     *
375
-     * @param EE_Model_Field_Base
376
-     * @return boolean
377
-     */
378
-    public function fieldHasRenderedFormat($field_obj): bool
379
-    {
380
-        return $this->isSubclassOfOne($field_obj, $this->fieldsThatHaveRenderedFormat());
381
-    }
382
-
383
-
384
-    /**
385
-     * Returns the list of model field classes that have a "_pretty" and non-pretty versions.
386
-     * The pretty version of the field is NOT query-able or editable, but requires no extra permissions
387
-     * to view
388
-     *
389
-     * @return array an array of EE_Model_Field_Base child classnames
390
-     */
391
-    public function fieldsThatHavePrettyFormat(): array
392
-    {
393
-        return apply_filters(
394
-            'FHEE__Controller_Model_Read__fields_pretty',
395
-            ['EE_Enum_Integer_Field', 'EE_Enum_Text_Field', 'EE_Money_Field']
396
-        );
397
-    }
398
-
399
-
400
-    /**
401
-     * If this field one that has a pretty equivalent
402
-     *
403
-     * @param EE_Model_Field_Base
404
-     * @return boolean
405
-     */
406
-    public function fieldHasPrettyFormat($field_obj): bool
407
-    {
408
-        return $this->isSubclassOfOne($field_obj, $this->fieldsThatHavePrettyFormat());
409
-    }
410
-
411
-
412
-    /**
413
-     * Returns an array describing what extra API resource properties have been added through the versions
414
-     *
415
-     * @return array
416
-     * @see _extra_resource_properties_for_models()
417
-     */
418
-    public function resourceChanges(): array
419
-    {
420
-        return $this->resource_changes;
421
-    }
422
-
423
-
424
-    /**
425
-     * Returns an array where keys are extra resource properties in this version of the API,
426
-     * and values are key-value pairs describing the new properties.
427
-     *
428
-     * @param EEM_Base $model
429
-     * @return array
430
-     * @see Model_Version::_resource_changes
431
-     *
432
-     */
433
-    public function extraResourcePropertiesForModel(EEM_Base $model): array
434
-    {
435
-        $extra_properties = [];
436
-        foreach ($this->resourceChangesBetweenRequestedVersionAndCurrent() as $model_classnames) {
437
-            foreach ($model_classnames as $model_classname => $properties_added_in_this_version) {
438
-                if (is_subclass_of($model, $model_classname)) {
439
-                    $extra_properties = array_merge($extra_properties, $properties_added_in_this_version);
440
-                }
441
-            }
442
-        }
443
-        return $extra_properties;
444
-    }
445
-
446
-
447
-    /**
448
-     * Gets all the related models for the specified model. It's good to use this
449
-     * in case this model didn't exist for this version or something
450
-     *
451
-     * @param EEM_Base $model
452
-     * @return EE_Model_Relation_Base[]
453
-     */
454
-    public function relationSettings(EEM_Base $model): array
455
-    {
456
-        $relations = [];
457
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
458
-            if ($this->isModelNameInThisVersion($relation_name)) {
459
-                $relations[ $relation_name ] = $relation_obj;
460
-            }
461
-        }
462
-        // filter the results, but use the old filter name
463
-        return apply_filters(
464
-            'FHEE__Read__create_entity_from_wpdb_result__related_models_to_include',
465
-            $relations,
466
-            $model
467
-        );
468
-    }
28
+	/**
29
+	 * Constant used in the $_model_changes array to indicate that a model
30
+	 * was completely new in this version
31
+	 */
32
+	const MODEL_ADDED = 'model_added_in_this_version';
33
+
34
+	/**
35
+	 * Top-level keys are versions (major and minor version numbers, eg "4.6")
36
+	 * next-level keys are model names (eg "Event") that underwent some change in that version
37
+	 * and the value is either Model_Version_Info::model_added (indicating the model is completely NEW in this version),
38
+	 * or it's an array where the values are model field names,
39
+	 * or API resource properties (ie, non-model fields that appear in REST API results)
40
+	 * If a version is missing then we don't know anything about what changes it introduced from the previous version
41
+	 *
42
+	 * @var array
43
+	 */
44
+	protected $model_changes = [];
45
+
46
+	/**
47
+	 * top-level keys are version numbers,
48
+	 * next-level keys are model CLASSNAMES (even parent classnames),
49
+	 * and next-level keys are extra resource properties to attach to those models' resources,
50
+	 * and next-level key-value pairs, where the keys are:
51
+	 * 'raw', 'type', 'nullable', 'table_alias', 'table_column',  'always_available'
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $resource_changes = [];
56
+
57
+	/**
58
+	 * @var string indicating what version of the API was requested
59
+	 * (eg although core might be at version 4.8.11, they may have sent a request
60
+	 * for 4.6)
61
+	 */
62
+	protected $requested_version = null;
63
+
64
+	/**
65
+	 * Keys are model names, values are their classnames.
66
+	 * We cache this so we only need to calculate this once per request
67
+	 *
68
+	 * @var array
69
+	 */
70
+	protected $cached_models_for_requested_version = null;
71
+
72
+	/**
73
+	 * @var array
74
+	 */
75
+	protected $cached_model_changes_between_requested_version_and_current = null;
76
+
77
+	/**
78
+	 * @var array
79
+	 */
80
+	protected $cached_resource_changes_between_requested_version_and_current = null;
81
+
82
+	/**
83
+	 * 2d array where top-level keys are model names, 2nd-level keys are field names
84
+	 * and values are the actual field objects
85
+	 *
86
+	 * @var array
87
+	 */
88
+	protected $cached_fields_on_models = [];
89
+
90
+
91
+	/**
92
+	 * Model_Version_Info constructor.
93
+	 *
94
+	 * @param string $requested_version
95
+	 */
96
+	public function __construct(string $requested_version)
97
+	{
98
+		$this->requested_version = $requested_version;
99
+		$this->model_changes     = [
100
+			'4.8.29' => [
101
+				// first version where the REST API is in EE core, so no need
102
+				// to specify how its different from the previous
103
+			],
104
+		];
105
+		// setup data for "extra" fields added onto resources which don't actually exist on models
106
+		$this->resource_changes = apply_filters(
107
+			'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
108
+			[]
109
+		);
110
+		$defaults               = [
111
+			'raw'              => false,
112
+			'type'             => 'N/A',
113
+			'nullable'         => true,
114
+			'table_alias'      => 'N/A',
115
+			'table_column'     => 'N/A',
116
+			'always_available' => true,
117
+		];
118
+		foreach ($this->resource_changes as $model_classnames) {
119
+			foreach ($model_classnames as $model_classname => $extra_fields) {
120
+				foreach ($extra_fields as $field_name => $field_data) {
121
+					$this->resource_changes[ $model_classname ][ $field_name ]['name'] = $field_name;
122
+					foreach ($defaults as $attribute => $default_value) {
123
+						if (! isset($this->resource_changes[ $model_classname ][ $field_name ][ $attribute ])) {
124
+							$this->resource_changes[ $model_classname ][ $field_name ][ $attribute ] = $default_value;
125
+						}
126
+					}
127
+				}
128
+			}
129
+		}
130
+	}
131
+
132
+
133
+	/**
134
+	 * Returns a slice of Model_Version_Info::model_changes()'s array
135
+	 * indicating exactly what changes happened between the current core version,
136
+	 * and the version requested
137
+	 *
138
+	 * @return array
139
+	 */
140
+	public function modelChangesBetweenRequestedVersionAndCurrent(): array
141
+	{
142
+		if ($this->cached_model_changes_between_requested_version_and_current === null) {
143
+			$model_changes = [];
144
+			foreach ($this->modelChanges() as $version => $models_changed_in_version) {
145
+				if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
146
+					$model_changes[ $version ] = $models_changed_in_version;
147
+				}
148
+			}
149
+			$this->cached_model_changes_between_requested_version_and_current = $model_changes;
150
+		}
151
+		return $this->cached_model_changes_between_requested_version_and_current;
152
+	}
153
+
154
+
155
+	/**
156
+	 * Returns a slice of Model_Version_Info::model_changes()'s array
157
+	 * indicating exactly what changes happened between the current core version,
158
+	 * and the version requested
159
+	 *
160
+	 * @return array
161
+	 */
162
+	public function resourceChangesBetweenRequestedVersionAndCurrent(): array
163
+	{
164
+		if ($this->cached_resource_changes_between_requested_version_and_current === null) {
165
+			$resource_changes = [];
166
+			foreach ($this->resourceChanges() as $version => $model_classnames) {
167
+				if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
168
+					$resource_changes[ $version ] = $model_classnames;
169
+				}
170
+			}
171
+			$this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
172
+		}
173
+		return $this->cached_resource_changes_between_requested_version_and_current;
174
+	}
175
+
176
+
177
+	/**
178
+	 * If a request was sent to 'wp-json/ee/v4.7/events' this would be '4.7'
179
+	 *
180
+	 * @return string like '4.6'
181
+	 */
182
+	public function requestedVersion(): ?string
183
+	{
184
+		return $this->requested_version;
185
+	}
186
+
187
+
188
+	/**
189
+	 * Returns an array describing how the models have changed in each version of core
190
+	 * that supports the API (starting at 4.6)
191
+	 * Top-level keys are versions (major and minor version numbers, eg "4.6")
192
+	 * next-level keys are model names (eg "Event") that underwent some change in that version
193
+	 * and the value is either NULL (indicating the model is completely NEW in this version),
194
+	 * or it's an array where fields are value names.
195
+	 * If a version is missing then we don't know anything about what changes it introduced from the previous version
196
+	 *
197
+	 * @return array
198
+	 */
199
+	public function modelChanges(): array
200
+	{
201
+		return $this->model_changes;
202
+	}
203
+
204
+
205
+	/**
206
+	 * Takes into account the requested version, and the current version, and
207
+	 * what changed between the two, and tries to return.
208
+	 * Analogous to EE_Registry::instance()->non_abstract_db_models
209
+	 *
210
+	 * @return array keys are model names, values are their classname
211
+	 */
212
+	public function modelsForRequestedVersion(): array
213
+	{
214
+		if ($this->cached_models_for_requested_version === null) {
215
+			$all_models_in_current_version = EE_Registry::instance()->non_abstract_db_models;
216
+			foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
217
+				foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
218
+					if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
219
+						unset($all_models_in_current_version[ $model_name ]);
220
+					}
221
+				}
222
+			}
223
+			$this->cached_models_for_requested_version = apply_filters(
224
+				'FHEE__EventEspresso_core_libraries_rest_api__models_for_requested_version',
225
+				$all_models_in_current_version,
226
+				$this
227
+			);
228
+		}
229
+		return $this->cached_models_for_requested_version;
230
+	}
231
+
232
+
233
+	/**
234
+	 * Determines if this is a valid model name in the requested version.
235
+	 * Similar to EE_Registry::instance()->is_model_name(), but takes the requested
236
+	 * version's models into account
237
+	 *
238
+	 * @param string $model_name eg 'Event'
239
+	 * @return boolean
240
+	 */
241
+	public function isModelNameInThisVersion(string $model_name): bool
242
+	{
243
+		$model_names = $this->modelsForRequestedVersion();
244
+		if (isset($model_names[ $model_name ])) {
245
+			return true;
246
+		} else {
247
+			return false;
248
+		}
249
+	}
250
+
251
+
252
+	/**
253
+	 * Wrapper for EE_Registry::instance()->load_model(), but takes the requested
254
+	 * version's models into account
255
+	 *
256
+	 * @param string $model_name
257
+	 * @return EEM_Base
258
+	 * @throws EE_Error
259
+	 * @throws ReflectionException
260
+	 */
261
+	public function loadModel(string $model_name): EEM_Base
262
+	{
263
+		if ($this->isModelNameInThisVersion($model_name)) {
264
+			return EE_Registry::instance()->load_model($model_name);
265
+		} else {
266
+			throw new EE_Error(
267
+				sprintf(
268
+					esc_html__(
269
+						'Cannot load model "%1$s" because it does not exist in version %2$s of Event Espresso',
270
+						'event_espresso'
271
+					),
272
+					$model_name,
273
+					$this->requestedVersion()
274
+				)
275
+			);
276
+		}
277
+	}
278
+
279
+
280
+	/**
281
+	 * Gets all the fields that should exist on this model right now
282
+	 *
283
+	 * @param EEM_Base $model
284
+	 * @return array|EE_Model_Field_Base[]
285
+	 */
286
+	public function fieldsOnModelInThisVersion(EEM_Base $model): array
287
+	{
288
+		if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
289
+			// get all model changes between the requested version and current core version
290
+			$changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
291
+			// fetch all fields currently on this model
292
+			$current_fields = $model->field_settings();
293
+			// remove all fields that have been added since
294
+			foreach ($changes as $changes_in_version) {
295
+				if (
296
+					isset($changes_in_version[ $model->get_this_model_name() ])
297
+					&& $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
298
+				) {
299
+					$current_fields = array_diff_key(
300
+						$current_fields,
301
+						array_flip($changes_in_version[ $model->get_this_model_name() ])
302
+					);
303
+				}
304
+			}
305
+			$this->cached_fields_on_models = $current_fields;
306
+		}
307
+		return $this->cached_fields_on_models;
308
+	}
309
+
310
+
311
+	/**
312
+	 * Determines if $object is of one of the classes of $classes. Similar to
313
+	 * in_array(), except this checks if $object is a subclass of the classnames provided
314
+	 * in $classnames
315
+	 *
316
+	 * @param object $object
317
+	 * @param array  $classnames
318
+	 * @return boolean
319
+	 */
320
+	public function isSubclassOfOne($object, array $classnames): bool
321
+	{
322
+		foreach ($classnames as $classname) {
323
+			if (is_a($object, $classname)) {
324
+				return true;
325
+			}
326
+		}
327
+		return false;
328
+	}
329
+
330
+
331
+	/**
332
+	 * Returns the list of model field classes that that the API basically ignores
333
+	 *
334
+	 * @return array
335
+	 */
336
+	public function fieldsIgnored(): array
337
+	{
338
+		return apply_filters(
339
+			'FHEE__Controller_Model_Read_fields_ignored',
340
+			[]
341
+		);
342
+	}
343
+
344
+
345
+	/**
346
+	 * If this field one that should be ignored by the API?
347
+	 *
348
+	 * @param EE_Model_Field_Base
349
+	 * @return boolean
350
+	 */
351
+	public function fieldIsIgnored($field_obj): bool
352
+	{
353
+		return $this->isSubclassOfOne($field_obj, $this->fieldsIgnored());
354
+	}
355
+
356
+
357
+	/**
358
+	 * Returns the list of model field classes that have a "raw" and non-raw formats.
359
+	 * Normally the "raw" versions are only accessible to those who can edit them.
360
+	 *
361
+	 * @return array an array of EE_Model_Field_Base child classnames
362
+	 */
363
+	public function fieldsThatHaveRenderedFormat(): array
364
+	{
365
+		return apply_filters(
366
+			'FHEE__Controller_Model_Read__fields_raw',
367
+			['EE_Post_Content_Field', 'EE_Full_HTML_Field']
368
+		);
369
+	}
370
+
371
+
372
+	/**
373
+	 * If this field one that has a raw format
374
+	 *
375
+	 * @param EE_Model_Field_Base
376
+	 * @return boolean
377
+	 */
378
+	public function fieldHasRenderedFormat($field_obj): bool
379
+	{
380
+		return $this->isSubclassOfOne($field_obj, $this->fieldsThatHaveRenderedFormat());
381
+	}
382
+
383
+
384
+	/**
385
+	 * Returns the list of model field classes that have a "_pretty" and non-pretty versions.
386
+	 * The pretty version of the field is NOT query-able or editable, but requires no extra permissions
387
+	 * to view
388
+	 *
389
+	 * @return array an array of EE_Model_Field_Base child classnames
390
+	 */
391
+	public function fieldsThatHavePrettyFormat(): array
392
+	{
393
+		return apply_filters(
394
+			'FHEE__Controller_Model_Read__fields_pretty',
395
+			['EE_Enum_Integer_Field', 'EE_Enum_Text_Field', 'EE_Money_Field']
396
+		);
397
+	}
398
+
399
+
400
+	/**
401
+	 * If this field one that has a pretty equivalent
402
+	 *
403
+	 * @param EE_Model_Field_Base
404
+	 * @return boolean
405
+	 */
406
+	public function fieldHasPrettyFormat($field_obj): bool
407
+	{
408
+		return $this->isSubclassOfOne($field_obj, $this->fieldsThatHavePrettyFormat());
409
+	}
410
+
411
+
412
+	/**
413
+	 * Returns an array describing what extra API resource properties have been added through the versions
414
+	 *
415
+	 * @return array
416
+	 * @see _extra_resource_properties_for_models()
417
+	 */
418
+	public function resourceChanges(): array
419
+	{
420
+		return $this->resource_changes;
421
+	}
422
+
423
+
424
+	/**
425
+	 * Returns an array where keys are extra resource properties in this version of the API,
426
+	 * and values are key-value pairs describing the new properties.
427
+	 *
428
+	 * @param EEM_Base $model
429
+	 * @return array
430
+	 * @see Model_Version::_resource_changes
431
+	 *
432
+	 */
433
+	public function extraResourcePropertiesForModel(EEM_Base $model): array
434
+	{
435
+		$extra_properties = [];
436
+		foreach ($this->resourceChangesBetweenRequestedVersionAndCurrent() as $model_classnames) {
437
+			foreach ($model_classnames as $model_classname => $properties_added_in_this_version) {
438
+				if (is_subclass_of($model, $model_classname)) {
439
+					$extra_properties = array_merge($extra_properties, $properties_added_in_this_version);
440
+				}
441
+			}
442
+		}
443
+		return $extra_properties;
444
+	}
445
+
446
+
447
+	/**
448
+	 * Gets all the related models for the specified model. It's good to use this
449
+	 * in case this model didn't exist for this version or something
450
+	 *
451
+	 * @param EEM_Base $model
452
+	 * @return EE_Model_Relation_Base[]
453
+	 */
454
+	public function relationSettings(EEM_Base $model): array
455
+	{
456
+		$relations = [];
457
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
458
+			if ($this->isModelNameInThisVersion($relation_name)) {
459
+				$relations[ $relation_name ] = $relation_obj;
460
+			}
461
+		}
462
+		// filter the results, but use the old filter name
463
+		return apply_filters(
464
+			'FHEE__Read__create_entity_from_wpdb_result__related_models_to_include',
465
+			$relations,
466
+			$model
467
+		);
468
+	}
469 469
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -107,7 +107,7 @@  discard block
 block discarded – undo
107 107
             'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
108 108
             []
109 109
         );
110
-        $defaults               = [
110
+        $defaults = [
111 111
             'raw'              => false,
112 112
             'type'             => 'N/A',
113 113
             'nullable'         => true,
@@ -118,10 +118,10 @@  discard block
 block discarded – undo
118 118
         foreach ($this->resource_changes as $model_classnames) {
119 119
             foreach ($model_classnames as $model_classname => $extra_fields) {
120 120
                 foreach ($extra_fields as $field_name => $field_data) {
121
-                    $this->resource_changes[ $model_classname ][ $field_name ]['name'] = $field_name;
121
+                    $this->resource_changes[$model_classname][$field_name]['name'] = $field_name;
122 122
                     foreach ($defaults as $attribute => $default_value) {
123
-                        if (! isset($this->resource_changes[ $model_classname ][ $field_name ][ $attribute ])) {
124
-                            $this->resource_changes[ $model_classname ][ $field_name ][ $attribute ] = $default_value;
123
+                        if ( ! isset($this->resource_changes[$model_classname][$field_name][$attribute])) {
124
+                            $this->resource_changes[$model_classname][$field_name][$attribute] = $default_value;
125 125
                         }
126 126
                     }
127 127
                 }
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
             $model_changes = [];
144 144
             foreach ($this->modelChanges() as $version => $models_changed_in_version) {
145 145
                 if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
146
-                    $model_changes[ $version ] = $models_changed_in_version;
146
+                    $model_changes[$version] = $models_changed_in_version;
147 147
                 }
148 148
             }
149 149
             $this->cached_model_changes_between_requested_version_and_current = $model_changes;
@@ -165,7 +165,7 @@  discard block
 block discarded – undo
165 165
             $resource_changes = [];
166 166
             foreach ($this->resourceChanges() as $version => $model_classnames) {
167 167
                 if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
168
-                    $resource_changes[ $version ] = $model_classnames;
168
+                    $resource_changes[$version] = $model_classnames;
169 169
                 }
170 170
             }
171 171
             $this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
             foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
217 217
                 foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
218 218
                     if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
219
-                        unset($all_models_in_current_version[ $model_name ]);
219
+                        unset($all_models_in_current_version[$model_name]);
220 220
                     }
221 221
                 }
222 222
             }
@@ -241,7 +241,7 @@  discard block
 block discarded – undo
241 241
     public function isModelNameInThisVersion(string $model_name): bool
242 242
     {
243 243
         $model_names = $this->modelsForRequestedVersion();
244
-        if (isset($model_names[ $model_name ])) {
244
+        if (isset($model_names[$model_name])) {
245 245
             return true;
246 246
         } else {
247 247
             return false;
@@ -285,7 +285,7 @@  discard block
 block discarded – undo
285 285
      */
286 286
     public function fieldsOnModelInThisVersion(EEM_Base $model): array
287 287
     {
288
-        if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
288
+        if ( ! isset($this->cached_fields_on_models[$model->get_this_model_name()])) {
289 289
             // get all model changes between the requested version and current core version
290 290
             $changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
291 291
             // fetch all fields currently on this model
@@ -293,12 +293,12 @@  discard block
 block discarded – undo
293 293
             // remove all fields that have been added since
294 294
             foreach ($changes as $changes_in_version) {
295 295
                 if (
296
-                    isset($changes_in_version[ $model->get_this_model_name() ])
297
-                    && $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
296
+                    isset($changes_in_version[$model->get_this_model_name()])
297
+                    && $changes_in_version[$model->get_this_model_name()] !== ModelVersionInfo::MODEL_ADDED
298 298
                 ) {
299 299
                     $current_fields = array_diff_key(
300 300
                         $current_fields,
301
-                        array_flip($changes_in_version[ $model->get_this_model_name() ])
301
+                        array_flip($changes_in_version[$model->get_this_model_name()])
302 302
                     );
303 303
                 }
304 304
             }
@@ -456,7 +456,7 @@  discard block
 block discarded – undo
456 456
         $relations = [];
457 457
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
458 458
             if ($this->isModelNameInThisVersion($relation_name)) {
459
-                $relations[ $relation_name ] = $relation_obj;
459
+                $relations[$relation_name] = $relation_obj;
460 460
             }
461 461
         }
462 462
         // filter the results, but use the old filter name
Please login to merge, or discard this patch.