Completed
Pull Request — master (#977)
by
unknown
08:52
created
core/libraries/rest_api/ModelDataTranslator.php 2 patches
Unused Use Statements   -4 removed lines patch added patch discarded remove patch
@@ -4,14 +4,10 @@
 block discarded – undo
4 4
 
5 5
 use DomainException;
6 6
 use EE_Boolean_Field;
7
-use EE_Capabilities;
8 7
 use EE_Datetime_Field;
9 8
 use EE_Error;
10 9
 use EE_Infinite_Integer_Field;
11
-use EE_Maybe_Serialized_Simple_HTML_Field;
12 10
 use EE_Model_Field_Base;
13
-use EE_Password_Field;
14
-use EE_Restriction_Generator_Base;
15 11
 use EE_Serialized_Text_Field;
16 12
 use EED_Core_Rest_Api;
17 13
 use EEM_Base;
Please login to merge, or discard this patch.
Indentation   +642 added lines, -642 removed lines patch added patch discarded remove patch
@@ -39,646 +39,646 @@
 block discarded – undo
39 39
 class ModelDataTranslator
40 40
 {
41 41
 
42
-    /**
43
-     * We used to use -1 for infinity in the rest api, but that's ambiguous for
44
-     * fields that COULD contain -1; so we use null
45
-     */
46
-    const EE_INF_IN_REST = null;
47
-
48
-
49
-    /**
50
-     * Prepares a possible array of input values from JSON for use by the models
51
-     *
52
-     * @param EE_Model_Field_Base $field_obj
53
-     * @param mixed               $original_value_maybe_array
54
-     * @param string              $requested_version
55
-     * @param string              $timezone_string treat values as being in this timezone
56
-     * @return mixed
57
-     * @throws RestException
58
-     */
59
-    public static function prepareFieldValuesFromJson(
60
-        $field_obj,
61
-        $original_value_maybe_array,
62
-        $requested_version,
63
-        $timezone_string = 'UTC'
64
-    ) {
65
-        if (is_array($original_value_maybe_array)
66
-            && ! $field_obj instanceof EE_Serialized_Text_Field
67
-        ) {
68
-            $new_value_maybe_array = array();
69
-            foreach ($original_value_maybe_array as $array_key => $array_item) {
70
-                $new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
71
-                    $field_obj,
72
-                    $array_item,
73
-                    $requested_version,
74
-                    $timezone_string
75
-                );
76
-            }
77
-        } else {
78
-            $new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
79
-                $field_obj,
80
-                $original_value_maybe_array,
81
-                $requested_version,
82
-                $timezone_string
83
-            );
84
-        }
85
-        return $new_value_maybe_array;
86
-    }
87
-
88
-
89
-    /**
90
-     * Prepares an array of field values FOR use in JSON/REST API
91
-     *
92
-     * @param EE_Model_Field_Base $field_obj
93
-     * @param mixed               $original_value_maybe_array
94
-     * @param string              $request_version (eg 4.8.36)
95
-     * @return array
96
-     */
97
-    public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
98
-    {
99
-        if (is_array($original_value_maybe_array)) {
100
-            $new_value = array();
101
-            foreach ($original_value_maybe_array as $key => $value) {
102
-                $new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
103
-                    $field_obj,
104
-                    $value,
105
-                    $request_version
106
-                );
107
-            }
108
-        } else {
109
-            $new_value = ModelDataTranslator::prepareFieldValueForJson(
110
-                $field_obj,
111
-                $original_value_maybe_array,
112
-                $request_version
113
-            );
114
-        }
115
-        return $new_value;
116
-    }
117
-
118
-
119
-    /**
120
-     * Prepares incoming data from the json or $_REQUEST parameters for the models'
121
-     * "$query_params".
122
-     *
123
-     * @param EE_Model_Field_Base $field_obj
124
-     * @param mixed               $original_value
125
-     * @param string              $requested_version
126
-     * @param string              $timezone_string treat values as being in this timezone
127
-     * @return mixed
128
-     * @throws RestException
129
-     * @throws DomainException
130
-     * @throws EE_Error
131
-     */
132
-    public static function prepareFieldValueFromJson(
133
-        $field_obj,
134
-        $original_value,
135
-        $requested_version,
136
-        $timezone_string = 'UTC' // UTC
137
-    ) {
138
-        // check if they accidentally submitted an error value. If so throw an exception
139
-        if (is_array($original_value)
140
-            && isset($original_value['error_code'], $original_value['error_message'])) {
141
-            throw new RestException(
142
-                'rest_submitted_error_value',
143
-                sprintf(
144
-                    esc_html__(
145
-                        'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
146
-                        'event_espresso'
147
-                    ),
148
-                    $field_obj->get_name()
149
-                ),
150
-                array(
151
-                    'status' => 400,
152
-                )
153
-            );
154
-        }
155
-        // double-check for serialized PHP. We never accept serialized PHP. No way Jose.
156
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
157
-        $timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
158
-        $new_value = null;
159
-        // walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
160
-        // way Jose.
161
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
162
-        if ($field_obj instanceof EE_Infinite_Integer_Field
163
-            && in_array($original_value, array(null, ''), true)
164
-        ) {
165
-            $new_value = EE_INF;
166
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
167
-            $new_value = rest_parse_date(
168
-                self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
169
-            );
170
-            if ($new_value === false) {
171
-                throw new RestException(
172
-                    'invalid_format_for_timestamp',
173
-                    sprintf(
174
-                        esc_html__(
175
-                            '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.',
176
-                            'event_espresso'
177
-                        ),
178
-                        'RFC3339',
179
-                        'ISO8601',
180
-                        $original_value
181
-                    ),
182
-                    array(
183
-                        'status' => 400,
184
-                    )
185
-                );
186
-            }
187
-        } elseif ($field_obj instanceof EE_Boolean_Field) {
188
-            // Interpreted the strings "false", "true", "on", "off" appropriately.
189
-            $new_value = filter_var($original_value, FILTER_VALIDATE_BOOLEAN);
190
-        } else {
191
-            $new_value = $original_value;
192
-        }
193
-        return $new_value;
194
-    }
195
-
196
-
197
-    /**
198
-     * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
199
-     * information via details obtained from the host site.
200
-     *
201
-     * @param string            $original_timestamp
202
-     * @param EE_Datetime_Field $datetime_field
203
-     * @param                   $timezone_string
204
-     * @return string
205
-     * @throws DomainException
206
-     */
207
-    private static function getTimestampWithTimezoneOffset(
208
-        $original_timestamp,
209
-        EE_Datetime_Field $datetime_field,
210
-        $timezone_string
211
-    ) {
212
-        // already have timezone information?
213
-        if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
214
-            // yes, we're ignoring the timezone.
215
-            return $original_timestamp;
216
-        }
217
-        // need to append timezone
218
-        list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
219
-            $datetime_field->get_timezone_offset(
220
-                new \DateTimeZone($timezone_string),
221
-                $original_timestamp
222
-            )
223
-        );
224
-        $offset_string =
225
-            str_pad(
226
-                floor($offset_secs / HOUR_IN_SECONDS),
227
-                2,
228
-                '0',
229
-                STR_PAD_LEFT
230
-            )
231
-            . ':'
232
-            . str_pad(
233
-                ($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
234
-                2,
235
-                '0',
236
-                STR_PAD_LEFT
237
-            );
238
-        return $original_timestamp . $offset_sign . $offset_string;
239
-    }
240
-
241
-
242
-    /**
243
-     * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
244
-     * think that can happen). If $data is an array, recurses into its keys and values
245
-     *
246
-     * @param mixed $data
247
-     * @throws RestException
248
-     * @return void
249
-     */
250
-    public static function throwExceptionIfContainsSerializedData($data)
251
-    {
252
-        if (is_array($data)) {
253
-            foreach ($data as $key => $value) {
254
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
255
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
256
-            }
257
-        } else {
258
-            if (is_serialized($data) || is_object($data)) {
259
-                throw new RestException(
260
-                    'serialized_data_submission_prohibited',
261
-                    esc_html__(
262
-                    // @codingStandardsIgnoreStart
263
-                        'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
264
-                        // @codingStandardsIgnoreEnd
265
-                        'event_espresso'
266
-                    )
267
-                );
268
-            }
269
-        }
270
-    }
271
-
272
-
273
-    /**
274
-     * determines what's going on with them timezone strings
275
-     *
276
-     * @param int $timezone_offset
277
-     * @return array
278
-     */
279
-    private static function parseTimezoneOffset($timezone_offset)
280
-    {
281
-        $first_char = substr((string) $timezone_offset, 0, 1);
282
-        if ($first_char === '+' || $first_char === '-') {
283
-            $offset_sign = $first_char;
284
-            $offset_secs = substr((string) $timezone_offset, 1);
285
-        } else {
286
-            $offset_sign = '+';
287
-            $offset_secs = $timezone_offset;
288
-        }
289
-        return array($offset_sign, $offset_secs);
290
-    }
291
-
292
-
293
-    /**
294
-     * Prepares a field's value for display in the API
295
-     *
296
-     * @param EE_Model_Field_Base $field_obj
297
-     * @param mixed               $original_value
298
-     * @param string              $requested_version
299
-     * @return mixed
300
-     */
301
-    public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
302
-    {
303
-        if ($original_value === EE_INF) {
304
-            $new_value = ModelDataTranslator::EE_INF_IN_REST;
305
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
306
-            if (is_string($original_value)) {
307
-                // did they submit a string of a unix timestamp?
308
-                if (is_numeric($original_value)) {
309
-                    $datetime_obj = new \DateTime();
310
-                    $datetime_obj->setTimestamp((int) $original_value);
311
-                } else {
312
-                    // first, check if its a MySQL timestamp in GMT
313
-                    $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
314
-                }
315
-                if (! $datetime_obj instanceof \DateTime) {
316
-                    // so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
317
-                    $datetime_obj = $field_obj->prepare_for_set($original_value);
318
-                }
319
-                $original_value = $datetime_obj;
320
-            }
321
-            if ($original_value instanceof \DateTime) {
322
-                $new_value = $original_value->format('Y-m-d H:i:s');
323
-            } elseif (is_int($original_value) || is_float($original_value)) {
324
-                $new_value = date('Y-m-d H:i:s', $original_value);
325
-            } elseif ($original_value === null || $original_value === '') {
326
-                $new_value = null;
327
-            } else {
328
-                // so it's not a datetime object, unix timestamp (as string or int),
329
-                // MySQL timestamp, or even a string in the field object's format. So no idea what it is
330
-                throw new \EE_Error(
331
-                    sprintf(
332
-                        esc_html__(
333
-                        // @codingStandardsIgnoreStart
334
-                            '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".',
335
-                            // @codingStandardsIgnoreEnd
336
-                            'event_espresso'
337
-                        ),
338
-                        $original_value,
339
-                        $field_obj->get_name(),
340
-                        $field_obj->get_model_name(),
341
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
342
-                    )
343
-                );
344
-            }
345
-            if ($new_value !== null) {
346
-                $new_value = mysql_to_rfc3339($new_value);
347
-            }
348
-        } else {
349
-            $new_value = $original_value;
350
-        }
351
-        // are we about to send an object? just don't. We have no good way to represent it in JSON.
352
-        // can't just check using is_object() because that missed PHP incomplete objects
353
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
354
-            $new_value = array(
355
-                'error_code'    => 'php_object_not_return',
356
-                'error_message' => esc_html__(
357
-                    'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
358
-                    'event_espresso'
359
-                ),
360
-            );
361
-        }
362
-        return apply_filters(
363
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
364
-            $new_value,
365
-            $field_obj,
366
-            $original_value,
367
-            $requested_version
368
-        );
369
-    }
370
-
371
-
372
-    /**
373
-     * Prepares condition-query-parameters (like what's in where and having) from
374
-     * the format expected in the API to use in the models
375
-     *
376
-     * @param array $inputted_query_params_of_this_type
377
-     * @param EEM_Base $model
378
-     * @param string $requested_version
379
-     * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
380
-     *                          If we're writing to the DB, we don't expect any operators, or any logic query
381
-     *                          parameters, and we also won't accept serialized data unless the current user has
382
-     *                          unfiltered_html.
383
-     * @return array
384
-     * @throws DomainException
385
-     * @throws EE_Error
386
-     * @throws RestException
387
-     * @throws InvalidDataTypeException
388
-     * @throws InvalidInterfaceException
389
-     * @throws InvalidArgumentException
390
-     */
391
-    public static function prepareConditionsQueryParamsForModels(
392
-        $inputted_query_params_of_this_type,
393
-        EEM_Base $model,
394
-        $requested_version,
395
-        $writing = false
396
-    ) {
397
-        $query_param_for_models = array();
398
-        $context = new RestIncomingQueryParamContext($model, $requested_version, $writing);
399
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
400
-            $query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
401
-            if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
402
-                $translated_value = $query_param_meta->determineConditionsQueryParameterValue();
403
-                if ((isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ]) && $query_param_meta->isGmtField())
404
-                    || $translated_value === null
405
-                ) {
406
-                    // they have already provided a non-gmt field, ignore the gmt one. That's what WP core
407
-                    // currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
408
-                    // OR we couldn't create a translated value from their input
409
-                    continue;
410
-                }
411
-                $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
412
-            } else {
413
-                $nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
414
-                if ($nested_query_params) {
415
-                    $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
416
-                }
417
-            }
418
-        }
419
-        return $query_param_for_models;
420
-    }
421
-
422
-    /**
423
-     * Mostly checks if the last 4 characters are "_gmt", indicating its a
424
-     * gmt date field name
425
-     *
426
-     * @param string $field_name
427
-     * @return boolean
428
-     */
429
-    public static function isGmtDateFieldName($field_name)
430
-    {
431
-        return substr(
432
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
433
-            -4,
434
-            4
435
-        ) === '_gmt';
436
-    }
437
-
438
-
439
-    /**
440
-     * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
441
-     *
442
-     * @param string $field_name
443
-     * @return string
444
-     */
445
-    public static function removeGmtFromFieldName($field_name)
446
-    {
447
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
448
-            return $field_name;
449
-        }
450
-        $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
451
-            $field_name
452
-        );
453
-        $query_param_sans_gmt_and_sans_stars = substr(
454
-            $query_param_sans_stars,
455
-            0,
456
-            strrpos(
457
-                $field_name,
458
-                '_gmt'
459
-            )
460
-        );
461
-        return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
462
-    }
463
-
464
-
465
-    /**
466
-     * Takes a field name from the REST API and prepares it for the model querying
467
-     *
468
-     * @param string $field_name
469
-     * @return string
470
-     */
471
-    public static function prepareFieldNameFromJson($field_name)
472
-    {
473
-        if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
474
-            return ModelDataTranslator::removeGmtFromFieldName($field_name);
475
-        }
476
-        return $field_name;
477
-    }
478
-
479
-
480
-    /**
481
-     * Takes array of field names from REST API and prepares for models
482
-     *
483
-     * @param array $field_names
484
-     * @return array of field names (possibly include model prefixes)
485
-     */
486
-    public static function prepareFieldNamesFromJson(array $field_names)
487
-    {
488
-        $new_array = array();
489
-        foreach ($field_names as $key => $field_name) {
490
-            $new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
491
-        }
492
-        return $new_array;
493
-    }
494
-
495
-
496
-    /**
497
-     * Takes array where array keys are field names (possibly with model path prefixes)
498
-     * from the REST API and prepares them for model querying
499
-     *
500
-     * @param array $field_names_as_keys
501
-     * @return array
502
-     */
503
-    public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
504
-    {
505
-        $new_array = array();
506
-        foreach ($field_names_as_keys as $field_name => $value) {
507
-            $new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
508
-        }
509
-        return $new_array;
510
-    }
511
-
512
-
513
-    /**
514
-     * Prepares an array of model query params for use in the REST API
515
-     *
516
-     * @param array    $model_query_params
517
-     * @param EEM_Base $model
518
-     * @param string   $requested_version  eg "4.8.36". If null is provided, defaults to the latest release of the EE4
519
-     *                                     REST API
520
-     * @return array which can be passed into the EE4 REST API when querying a model resource
521
-     * @throws EE_Error
522
-     */
523
-    public static function prepareQueryParamsForRestApi(
524
-        array $model_query_params,
525
-        EEM_Base $model,
526
-        $requested_version = null
527
-    ) {
528
-        if ($requested_version === null) {
529
-            $requested_version = EED_Core_Rest_Api::latest_rest_api_version();
530
-        }
531
-        $rest_query_params = $model_query_params;
532
-        if (isset($model_query_params[0])) {
533
-            $rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
534
-                $model_query_params[0],
535
-                $model,
536
-                $requested_version
537
-            );
538
-            unset($rest_query_params[0]);
539
-        }
540
-        if (isset($model_query_params['having'])) {
541
-            $rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
542
-                $model_query_params['having'],
543
-                $model,
544
-                $requested_version
545
-            );
546
-        }
547
-        return apply_filters(
548
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
549
-            $rest_query_params,
550
-            $model_query_params,
551
-            $model,
552
-            $requested_version
553
-        );
554
-    }
555
-
556
-
557
-    /**
558
-     * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
559
-     *
560
-     * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
561
-     *                                                      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
562
-     * @param EEM_Base $model
563
-     * @param string   $requested_version                   eg "4.8.36"
564
-     * @return array ready for use in the rest api query params
565
-     * @throws EE_Error
566
-     * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
567
-     *                                                      (which would be really unusual)
568
-     */
569
-    public static function prepareConditionsQueryParamsForRestApi(
570
-        $inputted_query_params_of_this_type,
571
-        EEM_Base $model,
572
-        $requested_version
573
-    ) {
574
-        $query_param_for_models = array();
575
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
576
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
577
-                ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
578
-                $model
579
-            );
580
-            if ($field instanceof EE_Model_Field_Base) {
581
-                // did they specify an operator?
582
-                if (is_array($query_param_value)) {
583
-                    $op = $query_param_value[0];
584
-                    $translated_value = array($op);
585
-                    if (isset($query_param_value[1])) {
586
-                        $value = $query_param_value[1];
587
-                        $translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
588
-                            $field,
589
-                            $value,
590
-                            $requested_version
591
-                        );
592
-                    }
593
-                } else {
594
-                    $translated_value = ModelDataTranslator::prepareFieldValueForJson(
595
-                        $field,
596
-                        $query_param_value,
597
-                        $requested_version
598
-                    );
599
-                }
600
-                $query_param_for_models[ $query_param_key ] = $translated_value;
601
-            } else {
602
-                // so it's not for a field, assume it's a logic query param key
603
-                $query_param_for_models[ $query_param_key ] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
604
-                    $query_param_value,
605
-                    $model,
606
-                    $requested_version
607
-                );
608
-            }
609
-        }
610
-        return $query_param_for_models;
611
-    }
612
-
613
-
614
-    /**
615
-     * @param $condition_query_param_key
616
-     * @return string
617
-     */
618
-    public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
619
-    {
620
-        $pos_of_star = strpos($condition_query_param_key, '*');
621
-        if ($pos_of_star === false) {
622
-            return $condition_query_param_key;
623
-        } else {
624
-            $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
625
-            return $condition_query_param_sans_star;
626
-        }
627
-    }
628
-
629
-
630
-    /**
631
-     * Takes the input parameter and finds the model field that it indicates.
632
-     *
633
-     * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
634
-     * @param EEM_Base $model
635
-     * @return EE_Model_Field_Base
636
-     * @throws EE_Error
637
-     */
638
-    public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
639
-    {
640
-        // ok, now proceed with deducing which part is the model's name, and which is the field's name
641
-        // which will help us find the database table and column
642
-        $query_param_parts = explode('.', $query_param_name);
643
-        if (empty($query_param_parts)) {
644
-            throw new EE_Error(
645
-                sprintf(
646
-                    __(
647
-                        '_extract_column_name is empty when trying to extract column and table name from %s',
648
-                        'event_espresso'
649
-                    ),
650
-                    $query_param_name
651
-                )
652
-            );
653
-        }
654
-        $number_of_parts = count($query_param_parts);
655
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
656
-        if ($number_of_parts === 1) {
657
-            $field_name = $last_query_param_part;
658
-        } else {// $number_of_parts >= 2
659
-            // the last part is the column name, and there are only 2parts. therefore...
660
-            $field_name = $last_query_param_part;
661
-            $model = \EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
662
-        }
663
-        try {
664
-            return $model->field_settings_for($field_name, false);
665
-        } catch (EE_Error $e) {
666
-            return null;
667
-        }
668
-    }
669
-
670
-
671
-    /**
672
-     * Returns true if $data can be easily represented in JSON.
673
-     * Basically, objects and resources can't be represented in JSON easily.
674
-     *
675
-     * @param mixed $data
676
-     * @return bool
677
-     */
678
-    protected static function isRepresentableInJson($data)
679
-    {
680
-        return is_scalar($data)
681
-               || is_array($data)
682
-               || is_null($data);
683
-    }
42
+	/**
43
+	 * We used to use -1 for infinity in the rest api, but that's ambiguous for
44
+	 * fields that COULD contain -1; so we use null
45
+	 */
46
+	const EE_INF_IN_REST = null;
47
+
48
+
49
+	/**
50
+	 * Prepares a possible array of input values from JSON for use by the models
51
+	 *
52
+	 * @param EE_Model_Field_Base $field_obj
53
+	 * @param mixed               $original_value_maybe_array
54
+	 * @param string              $requested_version
55
+	 * @param string              $timezone_string treat values as being in this timezone
56
+	 * @return mixed
57
+	 * @throws RestException
58
+	 */
59
+	public static function prepareFieldValuesFromJson(
60
+		$field_obj,
61
+		$original_value_maybe_array,
62
+		$requested_version,
63
+		$timezone_string = 'UTC'
64
+	) {
65
+		if (is_array($original_value_maybe_array)
66
+			&& ! $field_obj instanceof EE_Serialized_Text_Field
67
+		) {
68
+			$new_value_maybe_array = array();
69
+			foreach ($original_value_maybe_array as $array_key => $array_item) {
70
+				$new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
71
+					$field_obj,
72
+					$array_item,
73
+					$requested_version,
74
+					$timezone_string
75
+				);
76
+			}
77
+		} else {
78
+			$new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
79
+				$field_obj,
80
+				$original_value_maybe_array,
81
+				$requested_version,
82
+				$timezone_string
83
+			);
84
+		}
85
+		return $new_value_maybe_array;
86
+	}
87
+
88
+
89
+	/**
90
+	 * Prepares an array of field values FOR use in JSON/REST API
91
+	 *
92
+	 * @param EE_Model_Field_Base $field_obj
93
+	 * @param mixed               $original_value_maybe_array
94
+	 * @param string              $request_version (eg 4.8.36)
95
+	 * @return array
96
+	 */
97
+	public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
98
+	{
99
+		if (is_array($original_value_maybe_array)) {
100
+			$new_value = array();
101
+			foreach ($original_value_maybe_array as $key => $value) {
102
+				$new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
103
+					$field_obj,
104
+					$value,
105
+					$request_version
106
+				);
107
+			}
108
+		} else {
109
+			$new_value = ModelDataTranslator::prepareFieldValueForJson(
110
+				$field_obj,
111
+				$original_value_maybe_array,
112
+				$request_version
113
+			);
114
+		}
115
+		return $new_value;
116
+	}
117
+
118
+
119
+	/**
120
+	 * Prepares incoming data from the json or $_REQUEST parameters for the models'
121
+	 * "$query_params".
122
+	 *
123
+	 * @param EE_Model_Field_Base $field_obj
124
+	 * @param mixed               $original_value
125
+	 * @param string              $requested_version
126
+	 * @param string              $timezone_string treat values as being in this timezone
127
+	 * @return mixed
128
+	 * @throws RestException
129
+	 * @throws DomainException
130
+	 * @throws EE_Error
131
+	 */
132
+	public static function prepareFieldValueFromJson(
133
+		$field_obj,
134
+		$original_value,
135
+		$requested_version,
136
+		$timezone_string = 'UTC' // UTC
137
+	) {
138
+		// check if they accidentally submitted an error value. If so throw an exception
139
+		if (is_array($original_value)
140
+			&& isset($original_value['error_code'], $original_value['error_message'])) {
141
+			throw new RestException(
142
+				'rest_submitted_error_value',
143
+				sprintf(
144
+					esc_html__(
145
+						'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
146
+						'event_espresso'
147
+					),
148
+					$field_obj->get_name()
149
+				),
150
+				array(
151
+					'status' => 400,
152
+				)
153
+			);
154
+		}
155
+		// double-check for serialized PHP. We never accept serialized PHP. No way Jose.
156
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
157
+		$timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
158
+		$new_value = null;
159
+		// walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
160
+		// way Jose.
161
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
162
+		if ($field_obj instanceof EE_Infinite_Integer_Field
163
+			&& in_array($original_value, array(null, ''), true)
164
+		) {
165
+			$new_value = EE_INF;
166
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
167
+			$new_value = rest_parse_date(
168
+				self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
169
+			);
170
+			if ($new_value === false) {
171
+				throw new RestException(
172
+					'invalid_format_for_timestamp',
173
+					sprintf(
174
+						esc_html__(
175
+							'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.',
176
+							'event_espresso'
177
+						),
178
+						'RFC3339',
179
+						'ISO8601',
180
+						$original_value
181
+					),
182
+					array(
183
+						'status' => 400,
184
+					)
185
+				);
186
+			}
187
+		} elseif ($field_obj instanceof EE_Boolean_Field) {
188
+			// Interpreted the strings "false", "true", "on", "off" appropriately.
189
+			$new_value = filter_var($original_value, FILTER_VALIDATE_BOOLEAN);
190
+		} else {
191
+			$new_value = $original_value;
192
+		}
193
+		return $new_value;
194
+	}
195
+
196
+
197
+	/**
198
+	 * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
199
+	 * information via details obtained from the host site.
200
+	 *
201
+	 * @param string            $original_timestamp
202
+	 * @param EE_Datetime_Field $datetime_field
203
+	 * @param                   $timezone_string
204
+	 * @return string
205
+	 * @throws DomainException
206
+	 */
207
+	private static function getTimestampWithTimezoneOffset(
208
+		$original_timestamp,
209
+		EE_Datetime_Field $datetime_field,
210
+		$timezone_string
211
+	) {
212
+		// already have timezone information?
213
+		if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
214
+			// yes, we're ignoring the timezone.
215
+			return $original_timestamp;
216
+		}
217
+		// need to append timezone
218
+		list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
219
+			$datetime_field->get_timezone_offset(
220
+				new \DateTimeZone($timezone_string),
221
+				$original_timestamp
222
+			)
223
+		);
224
+		$offset_string =
225
+			str_pad(
226
+				floor($offset_secs / HOUR_IN_SECONDS),
227
+				2,
228
+				'0',
229
+				STR_PAD_LEFT
230
+			)
231
+			. ':'
232
+			. str_pad(
233
+				($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
234
+				2,
235
+				'0',
236
+				STR_PAD_LEFT
237
+			);
238
+		return $original_timestamp . $offset_sign . $offset_string;
239
+	}
240
+
241
+
242
+	/**
243
+	 * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
244
+	 * think that can happen). If $data is an array, recurses into its keys and values
245
+	 *
246
+	 * @param mixed $data
247
+	 * @throws RestException
248
+	 * @return void
249
+	 */
250
+	public static function throwExceptionIfContainsSerializedData($data)
251
+	{
252
+		if (is_array($data)) {
253
+			foreach ($data as $key => $value) {
254
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
255
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
256
+			}
257
+		} else {
258
+			if (is_serialized($data) || is_object($data)) {
259
+				throw new RestException(
260
+					'serialized_data_submission_prohibited',
261
+					esc_html__(
262
+					// @codingStandardsIgnoreStart
263
+						'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
264
+						// @codingStandardsIgnoreEnd
265
+						'event_espresso'
266
+					)
267
+				);
268
+			}
269
+		}
270
+	}
271
+
272
+
273
+	/**
274
+	 * determines what's going on with them timezone strings
275
+	 *
276
+	 * @param int $timezone_offset
277
+	 * @return array
278
+	 */
279
+	private static function parseTimezoneOffset($timezone_offset)
280
+	{
281
+		$first_char = substr((string) $timezone_offset, 0, 1);
282
+		if ($first_char === '+' || $first_char === '-') {
283
+			$offset_sign = $first_char;
284
+			$offset_secs = substr((string) $timezone_offset, 1);
285
+		} else {
286
+			$offset_sign = '+';
287
+			$offset_secs = $timezone_offset;
288
+		}
289
+		return array($offset_sign, $offset_secs);
290
+	}
291
+
292
+
293
+	/**
294
+	 * Prepares a field's value for display in the API
295
+	 *
296
+	 * @param EE_Model_Field_Base $field_obj
297
+	 * @param mixed               $original_value
298
+	 * @param string              $requested_version
299
+	 * @return mixed
300
+	 */
301
+	public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
302
+	{
303
+		if ($original_value === EE_INF) {
304
+			$new_value = ModelDataTranslator::EE_INF_IN_REST;
305
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
306
+			if (is_string($original_value)) {
307
+				// did they submit a string of a unix timestamp?
308
+				if (is_numeric($original_value)) {
309
+					$datetime_obj = new \DateTime();
310
+					$datetime_obj->setTimestamp((int) $original_value);
311
+				} else {
312
+					// first, check if its a MySQL timestamp in GMT
313
+					$datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
314
+				}
315
+				if (! $datetime_obj instanceof \DateTime) {
316
+					// so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
317
+					$datetime_obj = $field_obj->prepare_for_set($original_value);
318
+				}
319
+				$original_value = $datetime_obj;
320
+			}
321
+			if ($original_value instanceof \DateTime) {
322
+				$new_value = $original_value->format('Y-m-d H:i:s');
323
+			} elseif (is_int($original_value) || is_float($original_value)) {
324
+				$new_value = date('Y-m-d H:i:s', $original_value);
325
+			} elseif ($original_value === null || $original_value === '') {
326
+				$new_value = null;
327
+			} else {
328
+				// so it's not a datetime object, unix timestamp (as string or int),
329
+				// MySQL timestamp, or even a string in the field object's format. So no idea what it is
330
+				throw new \EE_Error(
331
+					sprintf(
332
+						esc_html__(
333
+						// @codingStandardsIgnoreStart
334
+							'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".',
335
+							// @codingStandardsIgnoreEnd
336
+							'event_espresso'
337
+						),
338
+						$original_value,
339
+						$field_obj->get_name(),
340
+						$field_obj->get_model_name(),
341
+						$field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
342
+					)
343
+				);
344
+			}
345
+			if ($new_value !== null) {
346
+				$new_value = mysql_to_rfc3339($new_value);
347
+			}
348
+		} else {
349
+			$new_value = $original_value;
350
+		}
351
+		// are we about to send an object? just don't. We have no good way to represent it in JSON.
352
+		// can't just check using is_object() because that missed PHP incomplete objects
353
+		if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
354
+			$new_value = array(
355
+				'error_code'    => 'php_object_not_return',
356
+				'error_message' => esc_html__(
357
+					'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
358
+					'event_espresso'
359
+				),
360
+			);
361
+		}
362
+		return apply_filters(
363
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
364
+			$new_value,
365
+			$field_obj,
366
+			$original_value,
367
+			$requested_version
368
+		);
369
+	}
370
+
371
+
372
+	/**
373
+	 * Prepares condition-query-parameters (like what's in where and having) from
374
+	 * the format expected in the API to use in the models
375
+	 *
376
+	 * @param array $inputted_query_params_of_this_type
377
+	 * @param EEM_Base $model
378
+	 * @param string $requested_version
379
+	 * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
380
+	 *                          If we're writing to the DB, we don't expect any operators, or any logic query
381
+	 *                          parameters, and we also won't accept serialized data unless the current user has
382
+	 *                          unfiltered_html.
383
+	 * @return array
384
+	 * @throws DomainException
385
+	 * @throws EE_Error
386
+	 * @throws RestException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws InvalidInterfaceException
389
+	 * @throws InvalidArgumentException
390
+	 */
391
+	public static function prepareConditionsQueryParamsForModels(
392
+		$inputted_query_params_of_this_type,
393
+		EEM_Base $model,
394
+		$requested_version,
395
+		$writing = false
396
+	) {
397
+		$query_param_for_models = array();
398
+		$context = new RestIncomingQueryParamContext($model, $requested_version, $writing);
399
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
400
+			$query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
401
+			if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
402
+				$translated_value = $query_param_meta->determineConditionsQueryParameterValue();
403
+				if ((isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ]) && $query_param_meta->isGmtField())
404
+					|| $translated_value === null
405
+				) {
406
+					// they have already provided a non-gmt field, ignore the gmt one. That's what WP core
407
+					// currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
408
+					// OR we couldn't create a translated value from their input
409
+					continue;
410
+				}
411
+				$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
412
+			} else {
413
+				$nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
414
+				if ($nested_query_params) {
415
+					$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
416
+				}
417
+			}
418
+		}
419
+		return $query_param_for_models;
420
+	}
421
+
422
+	/**
423
+	 * Mostly checks if the last 4 characters are "_gmt", indicating its a
424
+	 * gmt date field name
425
+	 *
426
+	 * @param string $field_name
427
+	 * @return boolean
428
+	 */
429
+	public static function isGmtDateFieldName($field_name)
430
+	{
431
+		return substr(
432
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
433
+			-4,
434
+			4
435
+		) === '_gmt';
436
+	}
437
+
438
+
439
+	/**
440
+	 * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
441
+	 *
442
+	 * @param string $field_name
443
+	 * @return string
444
+	 */
445
+	public static function removeGmtFromFieldName($field_name)
446
+	{
447
+		if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
448
+			return $field_name;
449
+		}
450
+		$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
451
+			$field_name
452
+		);
453
+		$query_param_sans_gmt_and_sans_stars = substr(
454
+			$query_param_sans_stars,
455
+			0,
456
+			strrpos(
457
+				$field_name,
458
+				'_gmt'
459
+			)
460
+		);
461
+		return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
462
+	}
463
+
464
+
465
+	/**
466
+	 * Takes a field name from the REST API and prepares it for the model querying
467
+	 *
468
+	 * @param string $field_name
469
+	 * @return string
470
+	 */
471
+	public static function prepareFieldNameFromJson($field_name)
472
+	{
473
+		if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
474
+			return ModelDataTranslator::removeGmtFromFieldName($field_name);
475
+		}
476
+		return $field_name;
477
+	}
478
+
479
+
480
+	/**
481
+	 * Takes array of field names from REST API and prepares for models
482
+	 *
483
+	 * @param array $field_names
484
+	 * @return array of field names (possibly include model prefixes)
485
+	 */
486
+	public static function prepareFieldNamesFromJson(array $field_names)
487
+	{
488
+		$new_array = array();
489
+		foreach ($field_names as $key => $field_name) {
490
+			$new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
491
+		}
492
+		return $new_array;
493
+	}
494
+
495
+
496
+	/**
497
+	 * Takes array where array keys are field names (possibly with model path prefixes)
498
+	 * from the REST API and prepares them for model querying
499
+	 *
500
+	 * @param array $field_names_as_keys
501
+	 * @return array
502
+	 */
503
+	public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
504
+	{
505
+		$new_array = array();
506
+		foreach ($field_names_as_keys as $field_name => $value) {
507
+			$new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
508
+		}
509
+		return $new_array;
510
+	}
511
+
512
+
513
+	/**
514
+	 * Prepares an array of model query params for use in the REST API
515
+	 *
516
+	 * @param array    $model_query_params
517
+	 * @param EEM_Base $model
518
+	 * @param string   $requested_version  eg "4.8.36". If null is provided, defaults to the latest release of the EE4
519
+	 *                                     REST API
520
+	 * @return array which can be passed into the EE4 REST API when querying a model resource
521
+	 * @throws EE_Error
522
+	 */
523
+	public static function prepareQueryParamsForRestApi(
524
+		array $model_query_params,
525
+		EEM_Base $model,
526
+		$requested_version = null
527
+	) {
528
+		if ($requested_version === null) {
529
+			$requested_version = EED_Core_Rest_Api::latest_rest_api_version();
530
+		}
531
+		$rest_query_params = $model_query_params;
532
+		if (isset($model_query_params[0])) {
533
+			$rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
534
+				$model_query_params[0],
535
+				$model,
536
+				$requested_version
537
+			);
538
+			unset($rest_query_params[0]);
539
+		}
540
+		if (isset($model_query_params['having'])) {
541
+			$rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
542
+				$model_query_params['having'],
543
+				$model,
544
+				$requested_version
545
+			);
546
+		}
547
+		return apply_filters(
548
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
549
+			$rest_query_params,
550
+			$model_query_params,
551
+			$model,
552
+			$requested_version
553
+		);
554
+	}
555
+
556
+
557
+	/**
558
+	 * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
559
+	 *
560
+	 * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
561
+	 *                                                      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
562
+	 * @param EEM_Base $model
563
+	 * @param string   $requested_version                   eg "4.8.36"
564
+	 * @return array ready for use in the rest api query params
565
+	 * @throws EE_Error
566
+	 * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
567
+	 *                                                      (which would be really unusual)
568
+	 */
569
+	public static function prepareConditionsQueryParamsForRestApi(
570
+		$inputted_query_params_of_this_type,
571
+		EEM_Base $model,
572
+		$requested_version
573
+	) {
574
+		$query_param_for_models = array();
575
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
576
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
577
+				ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
578
+				$model
579
+			);
580
+			if ($field instanceof EE_Model_Field_Base) {
581
+				// did they specify an operator?
582
+				if (is_array($query_param_value)) {
583
+					$op = $query_param_value[0];
584
+					$translated_value = array($op);
585
+					if (isset($query_param_value[1])) {
586
+						$value = $query_param_value[1];
587
+						$translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
588
+							$field,
589
+							$value,
590
+							$requested_version
591
+						);
592
+					}
593
+				} else {
594
+					$translated_value = ModelDataTranslator::prepareFieldValueForJson(
595
+						$field,
596
+						$query_param_value,
597
+						$requested_version
598
+					);
599
+				}
600
+				$query_param_for_models[ $query_param_key ] = $translated_value;
601
+			} else {
602
+				// so it's not for a field, assume it's a logic query param key
603
+				$query_param_for_models[ $query_param_key ] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
604
+					$query_param_value,
605
+					$model,
606
+					$requested_version
607
+				);
608
+			}
609
+		}
610
+		return $query_param_for_models;
611
+	}
612
+
613
+
614
+	/**
615
+	 * @param $condition_query_param_key
616
+	 * @return string
617
+	 */
618
+	public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
619
+	{
620
+		$pos_of_star = strpos($condition_query_param_key, '*');
621
+		if ($pos_of_star === false) {
622
+			return $condition_query_param_key;
623
+		} else {
624
+			$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
625
+			return $condition_query_param_sans_star;
626
+		}
627
+	}
628
+
629
+
630
+	/**
631
+	 * Takes the input parameter and finds the model field that it indicates.
632
+	 *
633
+	 * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
634
+	 * @param EEM_Base $model
635
+	 * @return EE_Model_Field_Base
636
+	 * @throws EE_Error
637
+	 */
638
+	public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
639
+	{
640
+		// ok, now proceed with deducing which part is the model's name, and which is the field's name
641
+		// which will help us find the database table and column
642
+		$query_param_parts = explode('.', $query_param_name);
643
+		if (empty($query_param_parts)) {
644
+			throw new EE_Error(
645
+				sprintf(
646
+					__(
647
+						'_extract_column_name is empty when trying to extract column and table name from %s',
648
+						'event_espresso'
649
+					),
650
+					$query_param_name
651
+				)
652
+			);
653
+		}
654
+		$number_of_parts = count($query_param_parts);
655
+		$last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
656
+		if ($number_of_parts === 1) {
657
+			$field_name = $last_query_param_part;
658
+		} else {// $number_of_parts >= 2
659
+			// the last part is the column name, and there are only 2parts. therefore...
660
+			$field_name = $last_query_param_part;
661
+			$model = \EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
662
+		}
663
+		try {
664
+			return $model->field_settings_for($field_name, false);
665
+		} catch (EE_Error $e) {
666
+			return null;
667
+		}
668
+	}
669
+
670
+
671
+	/**
672
+	 * Returns true if $data can be easily represented in JSON.
673
+	 * Basically, objects and resources can't be represented in JSON easily.
674
+	 *
675
+	 * @param mixed $data
676
+	 * @return bool
677
+	 */
678
+	protected static function isRepresentableInJson($data)
679
+	{
680
+		return is_scalar($data)
681
+			   || is_array($data)
682
+			   || is_null($data);
683
+	}
684 684
 }
Please login to merge, or discard this patch.