Completed
Branch FET/rest-relation-endpoints (02db8d)
by
unknown
27:05 queued 18:22
created

assertOnlyAdminCanReadPasswordFields()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\libraries\rest_api;
4
5
use DomainException;
6
use EE_Datetime_Field;
7
use EE_Error;
8
use EE_Model_Field_Base;
9
use EE_Password_Field;
10
use EE_Restriction_Generator_Base;
11
use EEH_Array;
12
use EEH_DTT_Helper;
13
use EventEspresso\core\exceptions\InvalidDataTypeException;
14
use EventEspresso\core\exceptions\InvalidInterfaceException;
15
use InvalidArgumentException;
16
use EED_Core_Rest_Api;
17
18
/**
19
 * Class RestQueryParamMetadata
20
 *
21
 * Object for passing around metadata derived from the query parameters passed in via the REST API.
22
 * This is convenient when interpreting REST API query params and generating model query params.
23
 *
24
 * @package     Event Espresso
25
 * @author         Mike Nelson
26
 * @since         4.9.72.p
27
 *
28
 */
29
class RestIncomingQueryParamMetadata
30
{
31
    private $query_param_key;
32
    private $query_param_value;
33
    /**
34
     * @var RestIncomingQueryParamContext
35
     */
36
    private $context;
37
38
    /**
39
     * @var EE_Model_Field_Base|null
40
     */
41
    private $field;
42
43
    /**
44
     * @var string same as $query_param_key but has the * and anything after it removed
45
     */
46
    private $query_param_key_sans_stars;
47
48
    /**
49
     * @var string for timezone or timezone offset
50
     */
51
    private $timezone;
52
53
    /**
54
     * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
55
     */
56
    private $is_gmt_field = false;
57
58
    /**
59
     * RestIncomingQueryParamMetadata constructor.
60
     * You probably want to call
61
     * @param string $query_param_key
62
     * @param string $query_param_value
63
     * @param RestIncomingQueryParamContext $context
64
     */
65
    public function __construct($query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
66
    {
67
        $this->query_param_key = $query_param_key;
68
        $this->query_param_value = $query_param_value;
69
        $this->context = $context;
70
        $this->determineFieldAndTimezone();
71
    }
72
73
    /**
74
     * Gets the query parameter key. This may have been modified (see setQueryParamValue())
75
     * @return string
76
     */
77
    public function getQueryParamKey()
78
    {
79
        return $this->query_param_key;
80
    }
81
82
    /**
83
     * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
84
     * query parameters into the legacy structure)
85
     * @param string|array|int|float $query_param_value
86
     */
87
    private function setQueryParamValue($query_param_value)
88
    {
89
        $this->query_param_value = $query_param_value;
90
    }
91
92
    /**
93
     * Gets the original query parameter value passed in.
94
     * @return string
95
     */
96
    public function getQueryParamValue()
97
    {
98
        return $this->query_param_value;
99
    }
100
101
    /**
102
     * Gets the context object.
103
     * @return RestIncomingQueryParamContext
104
     */
105
    public function getContext()
106
    {
107
        return $this->context;
108
    }
109
110
    /**
111
     * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
112
     * @param string $query_param_key
113
     */
114
    private function setQueryParamKey($query_param_key)
115
    {
116
        $this->query_param_key = $query_param_key;
117
    }
118
119
    /**
120
     * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
121
     * did not indicate a field, eg if it were `OR`).
122
     * @return EE_Model_Field_Base|null
123
     */
124
    public function getField()
125
    {
126
        return $this->field;
127
    }
128
129
    /**
130
     * Gets the query parameter key (with the star and everything afterwards removed).
131
     * @return string
132
     */
133
    public function getQueryParamKeySansStars()
134
    {
135
        return $this->query_param_key_sans_stars;
136
    }
137
138
    /**
139
     * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
140
     * @return string
141
     */
142
    public function getTimezone()
143
    {
144
        return $this->timezone;
145
    }
146
147
    /**
148
     * Returns whether or not this is a GMT field
149
     * @return boolean
150
     */
151
    public function isGmtField()
152
    {
153
        return $this->is_gmt_field;
154
    }
155
156
    /**
157
     * Sets the field indicated by the query parameter key (might be null).
158
     * @param EE_Model_Field_Base|null $field
159
     */
160
    private function setField(EE_Model_Field_Base $field = null)
161
    {
162
        $this->field = $field;
163
    }
164
165
    /**
166
     * Sets the query parameter key-with-stars-removed.
167
     * @param string $query_param_key_sans_stars
168
     */
169
    private function setQueryParamKeySansStars($query_param_key_sans_stars)
170
    {
171
        $this->query_param_key_sans_stars = $query_param_key_sans_stars;
172
    }
173
174
    /**
175
     * Sets the timezone (this could be a timezeon offset string).
176
     * @param string $timezone
177
     */
178
    private function setTimezone($timezone)
179
    {
180
        $this->timezone = $timezone;
181
    }
182
183
    /**
184
     * @param mixed $is_gmt_field
185
     */
186
    private function setIsGmtField($is_gmt_field)
187
    {
188
        $this->is_gmt_field = $is_gmt_field;
189
    }
190
191
    /**
192
     * Determines what field, query param name, and query param name without stars, and timezone to use.
193
     * @since 4.9.72.p
194
     * @type EE_Model_Field_Base $field
195
     * @return void {
196
     * @throws EE_Error
197
     * @throws InvalidDataTypeException
198
     * @throws InvalidInterfaceException
199
     * @throws InvalidArgumentException
200
     */
201
    private function determineFieldAndTimezone()
202
    {
203
        $this->setQueryParamKeySansStars(ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
204
            $this->getQueryParamKey()
205
        ));
206
        $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
207
            $this->getQueryParamKeySansStars(),
208
            $this->getContext()->getModel()
209
        ));
210
        // double-check is it a *_gmt field?
211
        if (!$this->getField() instanceof EE_Model_Field_Base
212
            && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
213
        ) {
214
            // yep, take off '_gmt', and find the field
215
            $this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
216
            $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
217
                $this->getQueryParamKey(),
218
                $this->context->getModel()
219
            ));
220
            $this->setTimezone('UTC');
221
            $this->setIsGmtField(true);
222
        } elseif ($this->getField() instanceof EE_Datetime_Field) {
223
            // so it's not a GMT field. Set the timezone on the model to the default
224
            $this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
225
        } else {
226
            // just keep using what's already set for the timezone
227
            $this->setTimezone($this->context->getModel()->get_timezone());
228
        }
229
        $this->assertOnlyAdminCanReadPasswordFields();
230
    }
231
232
    /**
233
     * Throws an exception if a non-admin is trying to query by password.
234
     * @since 4.9.74.p
235
     * @throws RestException
236
     */
237
    private function assertOnlyAdminCanReadPasswordFields()
238
    {
239
        if ($this->getField() instanceof EE_Password_Field
240
            && ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())) {
241
            // only full admins can query by password. sorry bub!
242
            throw new RestException(
243
                'only_admins_can_query_by_password',
244
                // @codingStandardsIgnoreStart
245
                esc_html__('You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.', 'event_espresso'),
246
                // @codingStandardsIgnoreEnd
247
                array(
248
                    'status' => 403
249
                )
250
            );
251
        }
252
    }
253
254
    /**
255
     * Given a ton of input, determines the value to use for the models.
256
     * @since 4.9.72.p
257
     * @return array|null
258
     * @throws DomainException
259
     * @throws EE_Error
260
     * @throws RestException
261
     * @throws DomainException
262
     */
263
    public function determineConditionsQueryParameterValue()
264
    {
265
        if ($this->valueIsArrayDuringRead()) {
266
            return $this->determineModelValueGivenRestInputArray();
267
        }
268
        return ModelDataTranslator::prepareFieldValueFromJson(
269
            $this->getField(),
0 ignored issues
show
Bug introduced by
It seems like $this->getField() can be null; however, prepareFieldValueFromJson() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
270
            $this->getQueryParamValue(),
271
            $this->getContext()->getRequestedVersion(),
272
            $this->getTimezone()
273
        );
274
    }
275
276
    /**
277
     * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
278
     * @since 4.9.72.p
279
     * @return array|null
280
     * @throws RestException
281
     */
282
    private function determineModelValueGivenRestInputArray()
283
    {
284
        $this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
285
        // did they specify an operator?
286
        if ($this->valueIsLegacySpecifiedOperator()) {
287
            $query_param_value = $this->getQueryParamValue();
288
            $sub_array_key = $query_param_value[0];
289
            $translated_value = array($sub_array_key);
290
            if ($this->operatorIsNAry($sub_array_key)) {
291
                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
292
            } elseif ($this->operatorIsTernary($sub_array_key)) {
293
                $translated_value[] = array(
294
                    $this->prepareValuesFromJson($query_param_value[1][0]),
295
                    $this->prepareValuesFromJson($query_param_value[1][1])
296
                );
297
            } elseif ($this->operatorIsLike($sub_array_key)) {
298
                // we want to leave this value mostly-as-is (eg don't force it to be a float
299
                // or a boolean or an enum value. Leave it as-is with wildcards etc)
300
                // but do verify it at least doesn't have any serialized data
301
                ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
302
                $translated_value[] = $query_param_value[1];
303
            } elseif ($this->operatorIsUnary($sub_array_key)) {
304
                // no arguments should have been provided, so don't look for any
305
            } elseif ($this->operatorisBinary($sub_array_key)) {
306
                // it's a valid operator, but none of the exceptions. Treat it normally.
307
                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
308
            } else {
309
                // so they provided a valid operator, but wrong number of arguments
310
                $this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
311
                $translated_value = null;
312
            }
313
        } else {
314
            // so they didn't provide a valid operator
315
            // if we aren't in debug mode, then just try our best to fulfill the user's request
316
            $this->throwInvalidOperatorExceptionIfDebugging();
317
            $translated_value = null;
318
        }
319
        return $translated_value;
320
    }
321
322
    /**
323
     * Returns if this request is a "read" request and the value provided was an array.
324
     * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
325
     * @since 4.9.72.p
326
     * @return boolean
327
     */
328
    private function valueIsArrayDuringRead()
329
    {
330
        return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
331
    }
332
333
    /**
334
     * Returns if the value provided was an associative array (we should have already verified it's an array of some
335
     * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
336
     * @since 4.9.72.p
337
     * @return boolean
338
     */
339
    private function valueIsAssociativeArray()
340
    {
341
        return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
342
    }
343
344
    /**
345
     * Checks if the array value is itself an array that fits into the simplified specified operator structure
346
     * (eg `array('!=' => 123)`).
347
     * @since 4.9.72.p
348
     * @return boolean
349
     */
350
    private function valueIsSimplifiedSpecifiedOperator()
351
    {
352
        return count($this->getQueryParamValue()) === 1
353
            && array_key_exists(
354
                key($this->getQueryParamValue()),
355
                $this->getContext()->getModel()->valid_operators()
356
            );
357
    }
358
359
    /**
360
     * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
361
     * of either comma-separated-values, or a JSON array.
362
     * @since 4.9.72.p
363
     * @param $sub_array_key
364
     * @param $sub_array_value
365
     * @throws RestException
366
     */
367
    private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
368
    {
369
        if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
370
            throw new RestException(
371
                'csv_or_json_string_only',
372
                sprintf(
373
                    /* translators: 1: variable name*/
374
                    esc_html__(
375
                        'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
376
                        'event_espresso'
377
                    ),
378
                    $sub_array_key
379
                ),
380
                array(
381
                    'status' => 400,
382
                )
383
            );
384
        }
385
    }
386
387
    /**
388
     * Determines if the sub-array key is an operator taking 3 or more operators.
389
     * @since 4.9.72.p
390
     * @param $sub_array_key
391
     * @return boolean
392
     */
393
    private function subArrayKeyIsNonBinaryOperator($sub_array_key)
394
    {
395
        return array_key_exists(
396
            $sub_array_key,
397
            array_merge(
398
                $this->getContext()->getModel()->valid_in_style_operators(),
399
                $this->getContext()->getModel()->valid_between_style_operators()
400
            )
401
        );
402
    }
403
404
    /**
405
     * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
406
     * @since 4.9.72.p
407
     * @param string $sub_array_key
408
     * @return boolean
409
     */
410
    private function subArrayKeyIsUnaryOperator($sub_array_key)
411
    {
412
        return array_key_exists(
413
            $sub_array_key,
414
            $this->getContext()->getModel()->valid_null_style_operators()
415
        );
416
    }
417
418
    /**
419
     * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
420
     * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
421
     * @since 4.9.72.p
422
     * @param $sub_array_value
423
     * @return array|mixed|object
424
     */
425
    private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
426
    {
427
        // the value should be JSON or CSV
428
        $values = json_decode($sub_array_value);
429
        if (!is_array($values)) {
430
            $values = array_filter(
431
                array_map(
432
                    'trim',
433
                    explode(
434
                        ',',
435
                        $sub_array_value
436
                    )
437
                )
438
            );
439
        }
440
        return $values;
441
    }
442
443
    /**
444
     * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
445
     * @since 4.9.72.p
446
     * @throws RestException
447
     */
448 View Code Duplication
    private function assertSimplifiedSpecifiedOperator()
449
    {
450
        if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
451
            throw new RestException(
452
                'numerically_indexed_array_of_values_only',
453
                sprintf(
454
                    /* translators: 1: variable name*/
455
                    esc_html__(
456
                        'The array provided for the parameter "%1$s" should be numerically indexed.',
457
                        'event_espresso'
458
                    ),
459
                    $this->getQueryParamKey()
460
                ),
461
                array(
462
                    'status' => 400,
463
                )
464
            );
465
        }
466
    }
467
468
    /**
469
     * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
470
     * @since 4.9.72.p
471
     * @throws RestException
472
     */
473
    private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
474
    {
475
        if ($this->valueIsAssociativeArray()) {
476
            $this->assertSimplifiedSpecifiedOperator();
477
            $query_param_value = $this->getQueryParamValue();
478
            $sub_array_value = reset($query_param_value);
479
            $sub_array_key = key($query_param_value);
480
            $this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
481
            // they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
482
            if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
483
                $this->setQueryParamValue(array(
484
                    $sub_array_key,
485
                    $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value)
486
                ));
487
            } elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
488
                $this->setQueryParamValue(array($sub_array_key));
489
            } else {
490
                $this->setQueryParamValue(array($sub_array_key, $sub_array_value));
491
            }
492
        }
493
    }
494
495
    /**
496
     * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
497
     * @since 4.9.72.p
498
     * @return boolean
499
     */
500
    private function valueIsLegacySpecifiedOperator()
501
    {
502
        $valid_operators = $this->getContext()->getModel()->valid_operators();
503
        $query_param_value = $this->getQueryParamValue();
504
        return isset($query_param_value[0])
505
            && isset($valid_operators[ $query_param_value[0] ]);
506
    }
507
508
    /**
509
     * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
510
     * @since 4.9.72.p
511
     * @param $operator
512
     * @return boolean
513
     */
514 View Code Duplication
    private function operatorIsNAry($operator)
515
    {
516
        $valueArray = $this->getQueryParamValue();
517
        return array_key_exists(
518
            $operator,
519
            $this->getContext()->getModel()->valid_in_style_operators()
520
        )
521
            && isset($valueArray[1])
522
            && is_array($valueArray[1])
523
            && !isset($valueArray[2]);
524
    }
525
526
    /**
527
     * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
528
     * So we're looking for a value that looks like
529
     * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
530
     * @since 4.9.72.p
531
     * @param $operator
532
     * @return boolean
533
     */
534
    private function operatorIsTernary($operator)
535
    {
536
        $query_param_value = $this->getQueryParamValue();
537
        return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
538
            && isset($query_param_value[1])
539
            && is_array($query_param_value[1])
540
            && isset($query_param_value[1][0], $query_param_value[1][1])
541
            && !isset($query_param_value[1][2])
542
            && !isset($query_param_value[2]);
543
    }
544
545
    /**
546
     * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
547
     * @since 4.9.72.p
548
     * @param $operator
549
     * @return boolean
550
     */
551 View Code Duplication
    private function operatorIsLike($operator)
552
    {
553
        $query_param_value = $this->getQueryParamValue();
554
        return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
555
            && isset($query_param_value[1])
556
            && !isset($query_param_value[2]);
557
    }
558
559
    /**
560
     * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
561
     * @since 4.9.72.p
562
     * @param $operator
563
     * @return boolean
564
     */
565
    private function operatorIsUnary($operator)
566
    {
567
        $query_param_value = $this->getQueryParamValue();
568
        return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
569
            && !isset($query_param_value[1]);
570
    }
571
572
    /**
573
     * Returns true if the operator specified is a binary opeator (eg `=`, `!=`)
574
     * @since 4.9.72.p
575
     * @param $operator
576
     * @return boolean
577
     */
578
    private function operatorisBinary($operator)
579
    {
580
        $query_param_value = $this->getQueryParamValue();
581
        $model = $this->getContext()->getModel();
582
        return isset($query_param_value[1])
583
            && !isset($query_param_value[2])
584
            && !array_key_exists(
585
                $operator,
586
                array_merge(
587
                    $model->valid_in_style_operators(),
588
                    $model->valid_null_style_operators(),
589
                    $model->valid_like_style_operators(),
590
                    $model->valid_between_style_operators()
591
                )
592
            );
593
    }
594
595
    /**
596
     * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
597
     * @since 4.9.72.p
598
     * @param $operator
599
     * @throws RestException
600
     */
601
    private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
602
    {
603
        if (EED_Core_Rest_Api::debugMode()) {
604
            throw new RestException(
605
                'wrong_number_of_arguments',
606
                sprintf(
607
                    esc_html__(
608
                        'The operator you provided, "%1$s" had the wrong number of arguments',
609
                        'event_espresso'
610
                    ),
611
                    $operator
612
                ),
613
                array(
614
                    'status' => 400,
615
                )
616
            );
617
        }
618
    }
619
620
    /**
621
     * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
622
     * @since 4.9.72.p
623
     * @param $value
624
     * @return mixed
625
     * @throws RestException
626
     */
627
    private function prepareValuesFromJson($value)
628
    {
629
        return ModelDataTranslator::prepareFieldValuesFromJson(
630
            $this->getField(),
0 ignored issues
show
Bug introduced by
It seems like $this->getField() can be null; however, prepareFieldValuesFromJson() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
631
            $value,
632
            $this->getContext()->getRequestedVersion(),
633
            $this->getTimezone()
634
        );
635
    }
636
637
    /**
638
     * Throws an exception if an invalid operator was specified and we're debugging.
639
     * @since 4.9.72.p
640
     * @throws RestException
641
     */
642 View Code Duplication
    private function throwInvalidOperatorExceptionIfDebugging()
643
    {
644
        // so they didn't provide a valid operator
645
        if (EED_Core_Rest_Api::debugMode()) {
646
            throw new RestException(
647
                'invalid_operator',
648
                sprintf(
649
                    esc_html__(
650
                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
651
                        'event_espresso'
652
                    ),
653
                    $this->getQueryParamKey(),
654
                    $this->getQueryParamValue()
655
                ),
656
                array(
657
                    'status' => 400,
658
                )
659
            );
660
        }
661
    }
662
663
    /**
664
     * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
665
     * @since 4.9.72.p
666
     * @return boolean
667
     */
668
    private function isLogicQueryParam()
669
    {
670
        return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
671
    }
672
673
674
    /**
675
     * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
676
     * @since 4.9.72.p
677
     * @return array
678
     * @throws DomainException
679
     * @throws EE_Error
680
     * @throws RestException
681
     * @throws InvalidDataTypeException
682
     * @throws InvalidInterfaceException
683
     * @throws InvalidArgumentException
684
     */
685
    public function determineNestedConditionQueryParameters()
686
    {
687
688
        // so this param doesn't correspond to a field eh?
689
        if ($this->getContext()->isWriting()) {
690
            // always tell API clients about invalid parameters when they're creating data. Otherwise,
691
            // they are probably going to create invalid data
692
            throw new RestException(
693
                'invalid_field',
694
                sprintf(
695
                    /* translators: 1: variable name */
696
                    esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
697
                    $this->getQueryParamKey()
698
                )
699
            );
700
        }
701
        // so it's not for a field, is it a logic query param key?
702
        if ($this->isLogicQueryParam()) {
703
            return ModelDataTranslator::prepareConditionsQueryParamsForModels(
704
                $this->getQueryParamValue(),
705
                $this->getContext()->getModel(),
706
                $this->getContext()->getRequestedVersion()
707
            );
708
        }
709
        if (EED_Core_Rest_Api::debugMode()) {
710
            // only tell API clients they got it wrong if we're in debug mode
711
            // otherwise try our best ot fulfill their request by ignoring this invalid data
712
            throw new RestException(
713
                'invalid_parameter',
714
                sprintf(
715
                    /* translators: 1: variable name */
716
                    esc_html__(
717
                        'You provided an invalid parameter, with key "%1$s"',
718
                        'event_espresso'
719
                    ),
720
                    $this->getQueryParamKey()
721
                ),
722
                array(
723
                    'status' => 400,
724
                )
725
            );
726
        }
727
        return null;
728
    }
729
}
730
// End of file RestQueryParamMetadata.php
731
// Location: EventEspresso\core\libraries\rest_api/RestQueryParamMetadata.php
732