PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 2.8.23.4
Pods – Custom Content Types and Fields v2.8.23.4
2.8.23.4 2.9.19.4 3.0.10.4 3.1.4.2 3.2.8.3 3.3.9.1 trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / includes / access.php
pods / includes Last commit date
compatibility 2 weeks ago access.php 2 weeks ago classes.php 2 weeks ago data.php 2 weeks ago forms.php 2 weeks ago general.php 2 weeks ago media.php 2 weeks ago
access.php
2121 lines
1 <?php
2 /**
3 * @package Pods\Global\Functions\Access
4 */
5
6 use Pods\Whatsit\Pod;
7
8 /**
9 * Normalize Pod information with a Pods object or object info.
10 *
11 * @since 3.1.0
12 *
13 * @param array $args {
14 * The arguments to use.
15 *
16 * @type string|null $object_type The object type.
17 * @type string|null $object_name The object name.
18 * @type int|string|null $item_id The item ID.
19 * @type Pods|null $pods The Pods object.
20 * @type Pod|null $pod The Pod object.
21 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
22 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
23 * }
24 *
25 * @return array {
26 * The arguments to use.
27 *
28 * @type string|null $object_type The object type (if set).
29 * @type string|null $object_name The object name (if set).
30 * @type int|string|null $item_id The item ID (if set).
31 * @type Pods|null $pods The Pods object (if built or provided).
32 * @type Pod|null $pod The Pod object (if built or provided).
33 * }
34 */
35 function pods_info_from_args( array $args ) {
36 $info = [
37 'object_type' => null,
38 'object_name' => null,
39 'item_id' => null,
40 'pods' => null,
41 'pod' => null,
42 ];
43
44 $build_pods = false;
45 $build_pod = false;
46
47 if ( isset( $args['build_pods'] ) ) {
48 $build_pods = $args['build_pods'];
49
50 unset( $args['build_pods'] );
51 }
52
53 if ( isset( $args['build_pod'] ) ) {
54 $build_pod = $args['build_pod'];
55
56 unset( $args['build_pod'] );
57 }
58
59 // Merge in the args with the defaults.
60 $info = array_merge( $info, $args );
61
62 $object_type_set = null !== $info['object_type'];
63 $object_name_set = null !== $info['object_name'];
64
65 // Maybe auto-set the object name from the type if we can.
66 if (
67 $object_type_set
68 && ! $object_name_set
69 && in_array( $info['object_type'], [ 'comment', 'media', 'user' ], true )
70 ) {
71 $info['object_name'] = $info['object_type'];
72
73 $object_name_set = true;
74 }
75
76 // Normalize the Pods info to null if it's not valid.
77 if (
78 $info['pods'] instanceof Pods
79 && ! $info['pods']->is_valid()
80 ) {
81 $info['pods'] = null;
82 }
83
84 // Maybe build the Pods object from the info.
85 if (
86 $build_pods
87 && $object_name_set
88 && ! $info['pods'] instanceof Pods
89 ) {
90 $pods = pods( $info['object_name'], $info['item_id'], true );
91
92 if (
93 $pods instanceof Pods
94 && $pods->is_valid()
95 && (
96 empty( $info['object_type'] )
97 || $info['object_type'] === $pods->pod_data->get_type()
98 )
99 ) {
100 $info['pods'] = $pods;
101
102 if ( ! $info['pod'] instanceof Pod ) {
103 $info['pod'] = clone $pods->pod_data;
104 }
105 }
106 } elseif (
107 $info['pods'] instanceof Pods
108 && $info['pods']->is_valid()
109 && ! $info['pod'] instanceof Pod
110 ) {
111 $info['pod'] = clone $info['pods']->pod_data;
112 }
113
114 // Maybe build the Pod object from the info.
115 if (
116 $build_pod
117 && $object_name_set
118 && ! $info['pod'] instanceof Pod
119 ) {
120 try {
121 $pod = pods_api()->load_pod( [
122 'name' => $info['object_name'],
123 ] );
124 } catch ( Exception $e ) {
125 $pod = null;
126 }
127
128 if (
129 $pod instanceof Pod
130 && (
131 empty( $info['object_type'] )
132 || $info['object_type'] === $pod->get_type()
133 )
134 ) {
135 $info['pod'] = $pod;
136 }
137 }
138
139 if ( $info['pod'] instanceof Pod ) {
140 $info['object_type'] = $info['pod']->get_type();
141 $info['object_name'] = $info['pod']->get_name();
142 }
143
144 return $info;
145 }
146
147 /**
148 * Determine whether the current user has access to an object.
149 *
150 * @since 3.1.0
151 *
152 * @param array $args {
153 * The arguments to use.
154 *
155 * @type string|null $object_type The object type.
156 * @type string|null $object_name The object name.
157 * @type int|string|null $item_id The item ID.
158 * @type Pods|null $pods The Pods object.
159 * @type Pod|null $pod The Pod object.
160 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
161 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
162 * }
163 * @param int|null $user_id The user ID to check against, set to 0 or null for anonymous access check.
164 * @param string $access_type The type of access to check for (read, add, edit, delete).
165 * @param string|null $context The unique slug that can be referenced by hooks for context.
166 *
167 * @return bool Whether the current user has access to an object.
168 */
169 function pods_user_can_access_object( array $args, $user_id, $access_type = 'edit', $context = null ) {
170 $info = pods_info_from_args( $args );
171
172 if ( null === $user_id ) {
173 $user_id = 0;
174 }
175
176 // Check if the user exists.
177 $user = get_userdata( $user_id );
178
179 if ( ! $user instanceof WP_User ) {
180 // If the user does not exist and it was not anonymous, do not allow access to an invalid user.
181 if ( 0 < $user_id ) {
182 return false;
183 }
184
185 // If the user was 0 to begin with (anonymous) then set up a user object to work with.
186 $user = new WP_User();
187 }
188
189 // Determine if this is a user in WP that has full access.
190 if ( $user_id && pods_is_admin() ) {
191 return true;
192 }
193
194 if ( 'pod' === $info['object_type'] || 'table' === $info['object_type'] ) {
195 // If no object name is provided, we cannot check access.
196 if ( empty( $info['object_name'] ) ) {
197 return false;
198 }
199
200 // Determine if this user has full content access.
201 if ( $user->has_cap('pods_content' ) ) {
202 return true;
203 }
204 }
205
206 $capabilities = pods_access_map_capabilities( $info, $user_id );
207
208 // Unsupported capabilities returned.
209 if ( null === $capabilities ) {
210 return false;
211 }
212
213 /**
214 * Allow filtering the list of capabilities used for checking access against an object.
215 *
216 * @since 3.1.0
217 *
218 * @param array $capabilities The list of capabilities used for checking access against an object.
219 * @param int $user_id The user ID to check against.
220 * @param array $info {
221 * The normalized Pod information referenced.
222 *
223 * @type string|null $object_type The object type (if set).
224 * @type string|null $object_name The object name (if set).
225 * @type int|string|null $item_id The item ID (if set).
226 * @type Pods|null $pods The Pods object (if built or provided).
227 * @type Pod|null $pod The Pod object (if built or provided).
228 * }
229 * @param string $access_type The type of access to check for (read, add, edit, delete).
230 * @param string|null $context The unique slug that can be referenced by hooks for context.
231 */
232 $capabilities = (array) apply_filters(
233 'pods_user_can_access_object_get_capabilities',
234 $capabilities,
235 $user_id,
236 $info,
237 $access_type,
238 $context
239 );
240
241 // No capability mapped, do not allow access.
242 if ( ! array_key_exists( $access_type, $capabilities ) ) {
243 return false;
244 }
245
246 /**
247 * Allow filtering whether a user has access to an object before the normal capability check runs.
248 *
249 * @since 3.1.0
250 *
251 * @param null|bool $can_access Whether a user has access to an object (return null to run normal check).
252 * @param int $user_id The user ID to check against.
253 * @param array $info {
254 * The normalized Pod information referenced.
255 *
256 * @type string|null $object_type The object type (if set).
257 * @type string|null $object_name The object name (if set).
258 * @type int|string|null $item_id The item ID (if set).
259 * @type Pods|null $pods The Pods object (if built or provided).
260 * @type Pod|null $pod The Pod object (if built or provided).
261 * }
262 * @param string $access_type The type of access to check for (read, add, edit, delete).
263 * @param string|null $context The unique slug that can be referenced by hooks for context.
264 * @param array $capabilities The list of capabilities used for checking access against an object.
265 */
266 $can_access = apply_filters(
267 'pods_user_can_access_object_pre_check',
268 null,
269 $user_id,
270 $info,
271 $access_type,
272 $context,
273 $capabilities
274 );
275
276 // Check for access override and return that instead.
277 if ( null !== $can_access ) {
278 return $can_access;
279 }
280
281 // If we are allowing all access, null will be set for the capability.
282 if ( null === $capabilities[ $access_type ] ) {
283 $can_access = true;
284 } else {
285 // Support multiple capability checks ("OR" logic).
286 $capabilities[ $access_type ] = (array) $capabilities[ $access_type ];
287
288 $can_access = false;
289
290 foreach ( $capabilities[ $access_type ] as $capability ) {
291 if ( $info['item_id'] ) {
292 $can_access = $user->has_cap( $capability, $info['item_id'] );
293 } else {
294 $can_access = $user->has_cap( $capability );
295 }
296
297 if ( $can_access ) {
298 break;
299 }
300 }
301 }
302
303 $is_read_access = 'read' === $access_type;
304
305 // Check for password-protected post.
306 if (
307 $can_access
308 && 'post_type' === $info['object_type']
309 && $info['item_id']
310 && (
311 (
312 $is_read_access
313 && pods_access_bypass_post_with_password( $info )
314 )
315 || (
316 ! $is_read_access
317 && post_password_required( $info['item_id'] )
318 )
319 )
320 ) {
321 $can_access = false;
322 }
323
324 /**
325 * Allow filtering whether a user has access to an object after the normal capability check runs.
326 *
327 * @since 3.1.0
328 *
329 * @param bool $can_access Whether a user has access to an object.
330 * @param int $user_id The user ID to check against.
331 * @param array $info {
332 * The normalized Pod information referenced.
333 *
334 * @type string|null $object_type The object type (if set).
335 * @type string|null $object_name The object name (if set).
336 * @type int|string|null $item_id The item ID (if set).
337 * @type Pods|null $pods The Pods object (if built or provided).
338 * @type Pod|null $pod The Pod object (if built or provided).
339 * }
340 * @param string $access_type The type of access to check for (read, add, edit, delete).
341 * @param string|null $context The unique slug that can be referenced by hooks for context.
342 * @param array $capabilities The list of capabilities used for checking access against an object.
343 */
344 return (bool) apply_filters(
345 'pods_user_can_access_object',
346 $can_access,
347 $user_id,
348 $info,
349 $access_type,
350 $context,
351 $capabilities
352 );
353 }
354
355 /**
356 * Determine whether the current user has access to an object.
357 *
358 * @since 3.1.0
359 *
360 * @param array $args {
361 * The arguments to use.
362 *
363 * @type string|null $object_type The object type.
364 * @type string|null $object_name The object name.
365 * @type int|string|null $item_id The item ID.
366 * @type Pods|null $pods The Pods object.
367 * @type Pod|null $pod The Pod object.
368 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
369 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
370 * }
371 * @param string $access_type The type of access to check for (read, add, edit, delete).
372 * @param string|null $context The unique slug that can be referenced by hooks for context.
373 *
374 * @return bool Whether the current user has access to an object.
375 */
376 function pods_current_user_can_access_object( $args, $access_type = 'edit', $context = null ) {
377 $user_id = null;
378
379 if ( is_user_logged_in() ) {
380 $user_id = get_current_user_id();
381 }
382
383 return pods_user_can_access_object( $args, $user_id, $access_type, $context );
384 }
385
386 /**
387 * Build and map the capabilities that a specific object type/name/ID have in relation to a user ID.
388 *
389 * @since 3.1.0
390 *
391 * @param array $args {
392 * The arguments to use.
393 *
394 * @type string|null $object_type The object type.
395 * @type string|null $object_name The object name.
396 * @type int|string|null $item_id The item ID.
397 * @type Pods|null $pods The Pods object.
398 * @type Pod|null $pod The Pod object.
399 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
400 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
401 * }
402 * @param int|null $user_id The user ID accessing the object.
403 * @param bool $strict Whether to strictly get the capabilities or have the 'read' capability evaluate to null if it's public (defaults to false).
404 *
405 * @return array|null The capabilities that a specific object type/name/ID have in relation to a user ID, or null if invalid.
406 */
407 function pods_access_map_capabilities( array $args, $user_id = null, $strict = false ) {
408 $args['build_pods'] = true;
409 $args['build_pod'] = true;
410
411 $info = pods_info_from_args( $args );
412
413 // If no object type or name, we cannot check access.
414 if ( empty( $info['object_type'] ) || empty( $info['object_name'] ) ) {
415 return null;
416 }
417
418 $wp_object = null;
419
420 $capabilities = [];
421
422 if ( 'post_type' === $info['object_type'] ) {
423 $info['item_id'] = (int) $info['item_id'];
424
425 if ( $info['item_id'] ) {
426 $capabilities['read'] = 'read_post';
427 $capabilities['edit'] = 'edit_post';
428 $capabilities['delete'] = 'delete_post';
429 } else {
430 $capabilities['read'] = 'read';
431 $capabilities['edit'] = 'edit_posts';
432 $capabilities['delete'] = 'delete_posts';
433 }
434
435 $capabilities['add'] = 'create_posts';
436 $capabilities['read_private'] = 'read_private_posts';
437 $capabilities['edit_others'] = 'edit_others_posts';
438 $capabilities['delete_others'] = 'delete_others_posts';
439 $capabilities['delete_published'] = 'delete_published_posts';
440 $capabilities['delete_private'] = 'delete_private_posts';
441
442 // Maybe map capabilities to the post type.
443 $wp_object = get_post_type_object( $info['object_name'] );
444
445 if ( $info['item_id'] ) {
446 $post = get_post( $info['item_id'] );
447
448 // If the post was found, do fine-grained access checks.
449 if ( $post instanceof WP_Post ) {
450 $status_obj = get_post_status_object( $post->post_status );
451
452 // Check if the person is allowed to read other posts.
453 if (
454 $user_id
455 && $post->post_author
456 && (int) $user_id === (int) $post->post_author
457 ) {
458 // This is their own post, they can have access.
459 $capabilities['read'] = 'read';
460 } elseif (
461 ! $status_obj
462 || $status_obj->private
463 ) {
464 // This is a private post, check private post capability.
465 $capabilities['read'] = $capabilities['read_private'];
466 }
467 }
468 }
469 } elseif ( 'taxonomy' === $info['object_type'] ) {
470 $info['item_id'] = (int) $info['item_id'];
471
472 $capabilities['read'] = 'read';
473 $capabilities['add'] = 'manage_terms';
474 $capabilities['edit'] = 'edit_terms';
475 $capabilities['delete'] = 'delete_terms';
476
477 // Maybe map capabilities to the post type.
478 $wp_object = get_taxonomy( $info['object_name'] );
479 } elseif ( 'user' === $info['object_type'] ) {
480 $info['item_id'] = (int) $info['item_id'];
481
482 $capabilities['read'] = 'list_users';
483 $capabilities['add'] = 'create_users';
484 $capabilities['edit'] = 'edit_users';
485 $capabilities['delete'] = 'delete_users';
486
487 // If an object ID is provided, check for access for that specific user.
488 if ( ! empty( $info['item_id'] ) ) {
489 $capabilities['edit'] = 'edit_user';
490 $capabilities['delete'] = 'delete_user';
491 }
492
493 // Fake the WP object for the logic below.
494 $wp_object = (object) [
495 'public' => false,
496 'cap' => (object) [],
497 ];
498 } elseif ( 'media' === $info['object_type'] ) {
499 $info['item_id'] = (int) $info['item_id'];
500
501 $capabilities['read'] = 'read';
502 $capabilities['add'] = 'upload_files';
503 $capabilities['edit'] = 'upload_files';
504 $capabilities['delete'] = 'upload_files';
505
506 // Fake the WP object for the logic below.
507 $wp_object = (object) [
508 'public' => false,
509 'cap' => (object) [],
510 ];
511 } elseif ( 'comment' === $info['object_type'] ) {
512 $info['item_id'] = (int) $info['item_id'];
513
514 $capabilities['read'] = 'read';
515 $capabilities['add'] = 1 === (int) get_option( 'comment_registration' ) ? 'read' : null;
516 $capabilities['edit'] = 'moderate_comments';
517 $capabilities['delete'] = 'moderate_comments';
518
519 // If an object ID is provided, check for access for that specific user.
520 if ( ! empty( $info['item_id'] ) ) {
521 $capabilities['edit'] = 'edit_comment';
522 }
523
524 // Fake the WP object for the logic below.
525 $wp_object = (object) [
526 'public' => true,
527 'cap' => (object) [],
528 ];
529 } elseif ( 'settings' === $info['object_type'] ) {
530 $capabilities['read'] = 'manage_options';
531 $capabilities['edit'] = 'manage_options';
532 $capabilities['delete'] = 'manage_options';
533
534 // Fake the WP object for the logic below.
535 $wp_object = (object) [
536 'public' => false,
537 'cap' => (object) [],
538 ];
539 } elseif ( 'pod' === $info['object_type'] || 'table' === $info['object_type'] ) {
540 $info['item_id'] = (int) $info['item_id'];
541
542 $capabilities['read'] = 'pods_read_' . $info['object_name'];
543 $capabilities['add'] = 'pods_add_' . $info['object_name'];
544 $capabilities['edit'] = 'pods_edit_' . $info['object_name'];
545 $capabilities['delete'] = 'pods_delete_' . $info['object_name'];
546 $capabilities['edit_others'] = 'pods_edit_others_' . $info['object_name'];
547 $capabilities['delete_others'] = 'pods_delete_others_' . $info['object_name'];
548
549 $is_public = false;
550
551 if ( $info['pods'] instanceof Pods && $info['pod'] instanceof Pod ) {
552 // If an object ID is provided, check for access for that specific item.
553 if ( $info['item_id'] && $info['pods']->exists() ) {
554 // Check for author field.
555 $author_field = $info['pod']->get_field( 'author' );
556
557 $author_user_id = $author_field ? (int) $info['pods']->field( $author_field->get_name() . '.ID' ) : null;
558
559 // If we have an author field, check if they are the author.
560 if ( $author_field ) {
561 if ( $user_id && $author_user_id === $user_id ) {
562 // This is their own post, they can also have access if have edit access.
563 $capabilities['read'] = [
564 $capabilities['read'],
565 'pods_edit_' . $info['object_name'],
566 ];
567 } else {
568 // This is not their post, check if they have access to others.
569 $capabilities['edit'] = 'pods_edit_others_' . $info['object_name'];
570 $capabilities['delete'] = 'pods_delete_others_' . $info['object_name'];
571 }
572 }
573 }
574
575 $is_public = $info['pod']->get_arg( 'public', '0', true );
576 $is_public = filter_var( $is_public, FILTER_VALIDATE_BOOLEAN );
577
578 // Fake the WP object for the logic below.
579 $wp_object = (object) [
580 'public' => $is_public,
581 'cap' => (object) [],
582 ];
583 }
584
585 if ( $is_public ) {
586 $capabilities['read'] = 'read';
587 }
588 }
589
590 // If no post type object is found, we cannot check access.
591 if ( ! $wp_object ) {
592 return null;
593 }
594
595 // Check if there are any capabilities mapped for this type object.
596 foreach ( $capabilities as $access_type => $capability ) {
597 if ( $capability ) {
598 if ( is_array( $capability ) ) {
599 foreach ( $capability as $k => $cap ) {
600 if ( isset( $wp_object->cap->{$cap} ) ) {
601 $capabilities[ $access_type ][ $k ] = $wp_object->cap->{$cap};
602 }
603 }
604 } elseif ( isset( $wp_object->cap->{$capability} ) ) {
605 $capabilities[ $access_type ] = $wp_object->cap->{$capability};
606 }
607 }
608 }
609
610 // If the object is public, allow read for anyone even logged out.
611 if ( ! $strict && $wp_object->public && 'read' === $capabilities['read'] && ! $user_id ) {
612 $capabilities['read'] = null;
613 }
614
615 /**
616 * Allow filtering the list of capabilities used for checking access against an object type or singular object.
617 *
618 * @since 3.1.0
619 *
620 * @param array $capabilities The list of capabilities used for checking access against an object type or singular object.
621 * @param int $user_id The user ID to check against.
622 * @param array $info {
623 * The normalized Pod information referenced.
624 *
625 * @type string|null $object_type The object type (if set).
626 * @type string|null $object_name The object name (if set).
627 * @type int|string|null $item_id The item ID (if set).
628 * @type Pods|null $pods The Pods object (if built or provided).
629 * @type Pod|null $pod The Pod object (if built or provided).
630 * }
631 */
632 return (array) apply_filters(
633 'pods_access_map_capabilities',
634 $capabilities,
635 $user_id,
636 $info
637 );
638 }
639
640 /**
641 * Determine whether the object type/name is public.
642 *
643 * @since 3.1.0
644 *
645 * @param array $args {
646 * The arguments to use.
647 *
648 * @type string|null $object_type The object type.
649 * @type string|null $object_name The object name.
650 * @type int|string|null $item_id The item ID.
651 * @type Pods|null $pods The Pods object.
652 * @type Pod|null $pod The Pod object.
653 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
654 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
655 * }
656 * @param string $context The context we are checking from (defaults to shortcode).
657 *
658 * @return bool Whether the object type/name is public.
659 */
660 function pods_is_type_public( array $args, $context = 'shortcode' ) {
661 $args['build_pod'] = true;
662
663 $info = pods_info_from_args( $args );
664
665 $is_public = true;
666
667 $pod_has_public = null;
668
669 $is_post_type = 'post_type' === $info['object_type'];
670 $is_taxonomy = 'taxonomy' === $info['object_type'];
671 $is_pod = 'pod' === $info['object_type'];
672 $is_settings_pod = 'settings' === $info['object_type'];
673
674 $is_shortcode_context = 'shortcode' === $context;
675
676 if (
677 $info['pod'] instanceof Pod
678 && (
679 $is_post_type
680 || $is_taxonomy
681 || $is_pod
682 || $is_settings_pod
683 )
684 ) {
685 $is_extended = $info['pod']->is_extended();
686
687 if ( ! $is_extended ) {
688 $is_public = $info['pod']->get_arg( 'public', null, true );
689
690 if ( null !== $is_public ) {
691 $pod_has_public = true;
692
693 $is_public = filter_var( $is_public, FILTER_VALIDATE_BOOLEAN );
694
695 if ( $is_post_type || $is_taxonomy ) {
696 $is_public = $is_public && 1 === (int) $info['pod']->get_arg( 'publicly_queryable', $is_public, true );
697 }
698 }
699 }
700 }
701
702 // Maybe handle looking up the visibility based on the object type.
703 if ( null === $pod_has_public ) {
704 if ( $is_post_type ) {
705 // If no object name is provided, we cannot check if it is public.
706 if ( empty( $info['object_name'] ) ) {
707 $is_public = false;
708 } else {
709 $post_type_object = get_post_type_object( $info['object_name'] );
710
711 // Post type not found.
712 if ( ! $post_type_object ) {
713 $is_public = false;
714 } else {
715 $is_public = $post_type_object->public && $post_type_object->publicly_queryable;
716 }
717 }
718 } elseif ( $is_taxonomy ) {
719 // If no object name is provided, we cannot check if it is public.
720 if ( empty( $info['object_name'] ) ) {
721 $is_public = false;
722 } else {
723 $taxonomy_object = get_taxonomy( $info['object_name'] );
724
725 // Post type not found.
726 if ( ! $taxonomy_object ) {
727 $is_public = false;
728 } else {
729 $is_public = $taxonomy_object->public && $taxonomy_object->publicly_queryable;
730 }
731 }
732 } elseif ( 'user' === $info['object_type'] ) {
733 // Users are not public for shortcodes.
734 if ( $is_shortcode_context ) {
735 $is_public = false;
736 }
737 } elseif ( $is_pod || $is_settings_pod ) {
738 // Pods need special default handling for shortcodes.
739 if ( $is_shortcode_context ) {
740 $first_pods_version = get_option( 'pods_framework_version_first' );
741 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
742
743 $is_public = version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? true : false;
744 }
745 }
746 }
747
748 /**
749 * Allow filtering whether the object type/name is public.
750 *
751 * @since 3.1.0
752 *
753 * @param bool $is_public Whether the object type/name is public.
754 * @param array $info {
755 * The normalized Pod information referenced.
756 *
757 * @type string|null $object_type The object type (if set).
758 * @type string|null $object_name The object name (if set).
759 * @type int|string|null $item_id The item ID (if set).
760 * @type Pods|null $pods The Pods object (if built or provided).
761 * @type Pod|null $pod The Pod object (if built or provided).
762 * }
763 * @param string|null $context The context we are checking from (shortcode or null).
764 */
765 return (bool) apply_filters(
766 'pods_is_type_public',
767 $is_public,
768 $info,
769 $context
770 );
771 }
772
773 /**
774 * Determine whether a post should be bypassed because it it has a password.
775 *
776 * @since 3.1.0
777 *
778 * @param array $args {
779 * The arguments to use.
780 *
781 * @type string|null $object_type The object type.
782 * @type string|null $object_name The object name.
783 * @type int|string|null $item_id The item ID.
784 * @type Pods|null $pods The Pods object.
785 * @type Pod|null $pod The Pod object.
786 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
787 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
788 * }
789 *
790 * @return bool Whether a post should be bypassed because it it has a password.
791 */
792 function pods_access_bypass_post_with_password( array $args ) {
793 $info = pods_info_from_args( $args );
794
795 if ( 'post_type' !== $info['object_type'] || ! $info['item_id'] ) {
796 return false;
797 }
798
799 $post = get_post( (int) $info['item_id'] );
800
801 if ( ! $post instanceof WP_Post ) {
802 return false;
803 }
804
805 // Bypass posts that have a password required but not provided.
806 $bypass_post_with_password = post_password_required( $post );
807
808 /**
809 * Allow filtering whether a post should be bypassed because it it has a password.
810 *
811 * @since 3.1.0
812 *
813 * @param bool $bypass_post_with_password Whether a post should be bypassed because it it has a password.
814 * @param array $info {
815 * The normalized Pod information referenced.
816 *
817 * @type string|null $object_type The object type (if set).
818 * @type string|null $object_name The object name (if set).
819 * @type int|string|null $item_id The item ID (if set).
820 * @type Pods|null $pods The Pods object (if built or provided).
821 * @type Pod|null $pod The Pod object (if built or provided).
822 * }
823 */
824 return (bool) apply_filters(
825 'pods_access_bypass_post_with_password',
826 $bypass_post_with_password,
827 $info
828 );
829 }
830
831 /**
832 * Determine whether a post should be bypassed because it is private and capabilities are not met.
833 *
834 * @since 3.1.0
835 *
836 * @param array $args {
837 * The arguments to use.
838 *
839 * @type string|null $object_type The object type.
840 * @type string|null $object_name The object name.
841 * @type int|string|null $item_id The item ID.
842 * @type Pods|null $pods The Pods object.
843 * @type Pod|null $pod The Pod object.
844 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
845 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
846 * }
847 *
848 * @return bool Whether a post should be bypassed because it is private and capabilities are not met.
849 */
850 function pods_access_bypass_private_post( array $args ) {
851 $info = pods_info_from_args( $args );
852
853 if ( 'post_type' !== $info['object_type'] || ! $info['item_id'] ) {
854 return false;
855 }
856
857 $post = get_post( $info['item_id'] );
858
859 if ( ! $post instanceof WP_Post ) {
860 return false;
861 }
862
863 $status_obj = get_post_status_object( $post->post_status );
864
865 $bypass_private_post = false;
866
867 if (
868 ! is_object( $status_obj ) ||
869 ! empty( $status_obj->internal ) ||
870 ! empty( $status_obj->protected )
871 ) {
872 $is_public = false;
873 } else {
874 $is_public = ! empty( $status_obj->publicly_queryable ) || ( ! empty( $status_obj->_builtin ) && ! empty( $status_obj->public ) );
875 }
876
877 if ( ! $is_public ) {
878 $bypass_private_post = ! pods_current_user_can_access_object( $info, 'read' );
879 }
880
881 /**
882 * Allow filtering whether a post should be bypassed because it is private.
883 *
884 * @since 3.1.0
885 *
886 * @param bool $bypass_private_post Whether a post should be bypassed because it is private.
887 * @param array $info {
888 * The normalized Pod information referenced.
889 *
890 * @type string|null $object_type The object type (if set).
891 * @type string|null $object_name The object name (if set).
892 * @type int|string|null $item_id The item ID (if set).
893 * @type Pods|null $pods The Pods object (if built or provided).
894 * @type Pod|null $pod The Pod object (if built or provided).
895 * }
896 */
897 return (bool) apply_filters(
898 'pods_access_bypass_private_post',
899 $bypass_private_post,
900 $info
901 );
902 }
903
904 /**
905 * Determine whether dynamic features can be used.
906 *
907 * @since 3.1.0
908 *
909 * @return bool Whether dynamic features can be used.
910 */
911 function pods_can_use_dynamic_features( $pod = null ) {
912 if ( defined( 'PODS_DYNAMIC_FEATURES_ALLOW' ) ) {
913 return PODS_DYNAMIC_FEATURES_ALLOW;
914 }
915
916 $can_use_dynamic_features = apply_filters( 'pods_access_can_use_dynamic_features', null, $pod );
917
918 if ( is_bool( $can_use_dynamic_features ) ) {
919 return $can_use_dynamic_features;
920 }
921
922 $dynamic_features_allow = true;
923
924 if ( $pod instanceof Pod ) {
925 $dynamic_features_allow = pods_is_type_public(
926 [
927 'pod' => $pod,
928 ]
929 );
930 }
931
932 return $dynamic_features_allow;
933 }
934
935 /**
936 * Determine whether any or a specific dynamic feature can be used.
937 *
938 * @since 3.1.0
939 *
940 * @param string $type The dynamic feature type.
941 *
942 * @return bool Whether any or a specific dynamic feature can be used.
943 */
944 function pods_can_use_dynamic_feature( $type ) {
945 if ( ! pods_can_use_dynamic_features() ) {
946 return false;
947 }
948
949 if ( empty( $type ) ) {
950 return false;
951 }
952
953 // Handle the constants.
954 if ( 'view' === $type && defined( 'PODS_SHORTCODE_ALLOW_VIEWS' ) && ! PODS_SHORTCODE_ALLOW_VIEWS ) {
955 return false;
956 }
957
958 $can_use_dynamic_feature = apply_filters( 'pods_access_can_use_dynamic_feature', null, $type );
959
960 if ( is_bool( $can_use_dynamic_feature ) ) {
961 return $can_use_dynamic_feature;
962 }
963
964 $dynamic_features_enabled = [
965 'display',
966 'form',
967 ];
968
969 $constant_dynamic_features_enabled = defined( 'PODS_DYNAMIC_FEATURES_ENABLED' ) ? PODS_DYNAMIC_FEATURES_ENABLED : false;
970
971 if ( false !== $constant_dynamic_features_enabled && ! is_array( $constant_dynamic_features_enabled ) ) {
972 $constant_dynamic_features_enabled = explode( ',', $constant_dynamic_features_enabled );
973 $constant_dynamic_features_enabled = array_filter( $constant_dynamic_features_enabled );
974
975 $dynamic_features_enabled = $constant_dynamic_features_enabled;
976 }
977
978 if ( empty( $dynamic_features_enabled ) ) {
979 return false;
980 }
981
982 return in_array( $type, $dynamic_features_enabled, true );
983 }
984
985 /**
986 * Determine whether specific dynamic feature is unrestricted.
987 *
988 * @since 3.1.0
989 *
990 * @param array $args {
991 * The arguments to use.
992 *
993 * @type string|null $object_type The object type.
994 * @type string|null $object_name The object name.
995 * @type int|string|null $item_id The item ID.
996 * @type Pods|null $pods The Pods object.
997 * @type Pod|null $pod The Pod object.
998 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
999 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1000 * }
1001 * @param string $type The dynamic feature type.
1002 * @param string $mode The dynamic feature mode (like "add" or "edit" for the form feature).
1003 *
1004 * @return bool Whether specific dynamic feature is unrestricted.
1005 */
1006 function pods_can_use_dynamic_feature_unrestricted( array $args, $type, $mode = null ) {
1007 if ( ! pods_can_use_dynamic_feature( $type ) ) {
1008 return false;
1009 }
1010
1011 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICT' ) && ! PODS_DYNAMIC_FEATURES_RESTRICT ) {
1012 return true;
1013 }
1014
1015 $can_use_dynamic_features_unrestricted = apply_filters( 'pods_access_can_use_dynamic_features_unrestricted', null, $args, $type, $mode );
1016
1017 if ( is_bool( $can_use_dynamic_features_unrestricted ) ) {
1018 return $can_use_dynamic_features_unrestricted;
1019 }
1020
1021 $can_use_unrestricted = false;
1022
1023 $args['build_pod'] = true;
1024
1025 $info = pods_info_from_args( $args );
1026
1027 if ( ! $info['pod'] ) {
1028 $can_use_unrestricted = false;
1029 } else {
1030 $is_public_content_type = pods_is_type_public( $info );
1031
1032 $default_restricted_dynamic_features = [
1033 'form',
1034 ];
1035
1036 if ( ! $is_public_content_type ) {
1037 $default_restricted_dynamic_features[] = 'display';
1038 }
1039
1040 $default_restricted_dynamic_features_forms = [
1041 'edit',
1042 ];
1043
1044 if ( ! $is_public_content_type ) {
1045 $default_restricted_dynamic_features_forms[] = 'add';
1046 }
1047
1048 if ( ! empty( $type ) ) {
1049 $restricted_dynamic_features = $default_restricted_dynamic_features;
1050
1051 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICTED' ) && false !== PODS_DYNAMIC_FEATURES_RESTRICTED ) {
1052 $constant_restricted_dynamic_features = PODS_DYNAMIC_FEATURES_RESTRICTED;
1053
1054 if ( ! is_array( $constant_restricted_dynamic_features ) ) {
1055 $constant_restricted_dynamic_features = explode( ',', $constant_restricted_dynamic_features );
1056 }
1057
1058 $restricted_dynamic_features = $constant_restricted_dynamic_features;
1059 }
1060
1061 $restricted_dynamic_features = array_filter( $restricted_dynamic_features );
1062
1063 if ( empty( $restricted_dynamic_features ) ) {
1064 $can_use_unrestricted = true;
1065 } else {
1066 $can_use_unrestricted = ! in_array( $type, $restricted_dynamic_features, true );
1067 }
1068
1069 if ( ! $can_use_unrestricted && 'form' === $type && $mode ) {
1070 $restricted_dynamic_features_forms = $default_restricted_dynamic_features_forms;
1071
1072 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS' ) && false !== PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS ) {
1073 $constant_restricted_dynamic_features_forms = PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS;
1074
1075 if ( ! is_array( $constant_restricted_dynamic_features_forms ) ) {
1076 $constant_restricted_dynamic_features_forms = explode( ',', $constant_restricted_dynamic_features_forms );
1077 }
1078
1079 $restricted_dynamic_features_forms = $constant_restricted_dynamic_features_forms;
1080 }
1081
1082 $restricted_dynamic_features_forms = array_filter( $restricted_dynamic_features_forms );
1083
1084 if ( empty( $restricted_dynamic_features_forms ) ) {
1085 $can_use_unrestricted = true;
1086 } else {
1087 $can_use_unrestricted = ! in_array( $mode, $restricted_dynamic_features_forms, true );
1088 }
1089 }
1090 }
1091 }
1092
1093 return $can_use_unrestricted;
1094 }
1095
1096 /**
1097 * Get the access notice for admin user based on object type and object name.
1098 *
1099 * @since 3.1.0
1100 *
1101 * @param array $args {
1102 * The arguments to use.
1103 *
1104 * @type string|null $object_type The object type.
1105 * @type string|null $object_name The object name.
1106 * @type int|string|null $item_id The item ID.
1107 * @type Pods|null $pods The Pods object.
1108 * @type Pod|null $pod The Pod object.
1109 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1110 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1111 * }
1112 * @param bool $force_message Whether to force the message to show even if messages are hidden by a setting.
1113 *
1114 * @return string The access notice for admin user based on object type and object name.
1115 */
1116 function pods_get_access_admin_notice( $args, $force_message = false ) {
1117 $args['build_pod'] = true;
1118
1119 $info = pods_info_from_args( $args );
1120
1121 $identifier_for_html = esc_html( json_encode( [
1122 'object_type' => $info['object_type'],
1123 'object_name' => $info['object_name'],
1124 'item_id' => $info['item_id'],
1125 ] ) );
1126
1127 // Check if constant is hiding all notices.
1128 if ( ! $force_message && defined( 'PODS_ACCESS_HIDE_NOTICES' ) && PODS_ACCESS_HIDE_NOTICES ) {
1129 return '<!-- pods:access-notices/admin/hidden-by-constant ' . $identifier_for_html . ' -->';
1130 }
1131
1132 return '<!-- pods:access-notices/admin/content-hidden ' . $identifier_for_html . ' -->';
1133 }
1134
1135 /**
1136 * Get the access notice for non-admin user based on object type and object name.
1137 *
1138 * @since 3.1.0
1139 *
1140 * @param array $args {
1141 * The arguments to use.
1142 *
1143 * @type string|null $object_type The object type.
1144 * @type string|null $object_name The object name.
1145 * @type int|string|null $item_id The item ID.
1146 * @type Pods|null $pods The Pods object.
1147 * @type Pod|null $pod The Pod object.
1148 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1149 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1150 * }
1151 * @param bool $force_message Whether to force the message to show even if messages are hidden by a setting.
1152 * @param string|null $message A custom message to use for the notice text.
1153 *
1154 * @return string The access notice for non-admin user based on object type and object name.
1155 */
1156 function pods_get_access_user_notice( array $args, $force_message = false, $message = null ) {
1157 $args['build_pod'] = true;
1158
1159 $info = pods_info_from_args( $args );
1160
1161 $identifier_for_html = esc_html( json_encode( [
1162 'object_type' => $info['object_type'],
1163 'object_name' => $info['object_name'],
1164 'item_id' => $info['item_id'],
1165 ] ) );
1166
1167 // Check for password-protected post.
1168 if ( $info['item_id'] && pods_access_bypass_post_with_password( $info ) ) {
1169 $message = get_the_password_form( $info['item_id'] );
1170
1171 return '<!-- pods:access-notices/user/protected/message ' . $identifier_for_html . ' -->' . $message;
1172 }
1173
1174 // Check if constant is hiding all notices.
1175 if ( ! $force_message && defined( 'PODS_ACCESS_HIDE_NOTICES' ) && PODS_ACCESS_HIDE_NOTICES ) {
1176 return '<!-- pods:access-notices/user/hidden-by-constant ' . $identifier_for_html . ' -->';
1177 }
1178
1179 return '<!-- pods:access-notices/user/content-hidden ' . $identifier_for_html . ' -->';
1180 }
1181
1182 /**
1183 * Determine whether a callback can be used.
1184 *
1185 * Only plain function-name string callbacks are permitted by default. Closures,
1186 * invokable objects, array callables ( [ $object, 'method' ] / [ 'Class', 'method' ] ),
1187 * and string class method references ( "Class::method" ) are rejected unless
1188 * class callbacks are enabled via the PODS_ALLOW_CLASS_CALLBACKS constant or the
1189 * "pods_access_allow_class_callbacks" filter.
1190 *
1191 * @since 3.1.0
1192 *
1193 * @param string|callable $callback The callback to check.
1194 * @param array $params Parameters used by Pods::helper() method.
1195 *
1196 * @return bool Whether the callback can be used.
1197 */
1198 function pods_access_callback_allowed( $callback, $params = [] ) {
1199 // Class-based callbacks are disabled by default; only plain function-name string callbacks are permitted. Set the PODS_ALLOW_CLASS_CALLBACKS constant to true (or use the "pods_access_allow_class_callbacks" filter) to permit closures, invokable objects, array callables, and "Class::method" strings.
1200 $allow_class_callbacks = defined( 'PODS_ALLOW_CLASS_CALLBACKS' ) && PODS_ALLOW_CLASS_CALLBACKS;
1201
1202 /**
1203 * Filter whether class-based callbacks are permitted (closures, invokable
1204 * objects, array callables, and "Class::method" strings).
1205 *
1206 * @since 3.3.9.1
1207 *
1208 * @param bool $allow_class_callbacks Whether class-based callbacks are allowed.
1209 * @param string|callable $callback The callback being checked.
1210 * @param array $params Parameters used by Pods::helper() method.
1211 */
1212 $allow_class_callbacks = (bool) apply_filters( 'pods_access_allow_class_callbacks', $allow_class_callbacks, $callback, $params );
1213
1214 if ( ! is_string( $callback ) ) {
1215 return $allow_class_callbacks;
1216 }
1217
1218 if ( ! pods_can_use_dynamic_feature( 'display' ) ) {
1219 return false;
1220 }
1221
1222 if (
1223 defined( 'PODS_DISPLAY_CALLBACKS' )
1224 && ! PODS_DISPLAY_CALLBACKS
1225 ) {
1226 return false;
1227 }
1228
1229 /**
1230 * Allows changing whether callbacks are allowed to run.
1231 *
1232 * @param bool $allow_callbacks Whether callbacks are allowed to run.
1233 * @param array $params Parameters used by Pods::helper() method.
1234 *
1235 * @since 2.8.0
1236 */
1237 $allow_callbacks = (bool) apply_filters( 'pods_helper_allow_callbacks', true, $params );
1238
1239 if ( ! $allow_callbacks ) {
1240 return false;
1241 }
1242
1243 // Disallowed callbacks. A callback listed here can never be used, even if it also appears in the allowed list. Comparison is case- and namespace-insensitive, so entries are lowercase.
1244 $disallowed = [
1245 // Regex related (callback execution + ReDoS).
1246 'preg_replace',
1247 'preg_replace_array',
1248 'preg_replace_callback',
1249 'preg_replace_callback_array',
1250 'preg_match',
1251 'preg_match_all',
1252 'mb_ereg_replace_callback',
1253
1254 // Shell / command execution.
1255 'system',
1256 'exec',
1257 'passthru',
1258 'shell_exec',
1259 'popen',
1260 'proc_open',
1261 'proc_close',
1262 'proc_get_status',
1263 'proc_nice',
1264 'proc_terminate',
1265 'pcntl_exec',
1266 'escapeshellarg',
1267 'escapeshellcmd',
1268 'dl',
1269
1270 // Code evaluation / dynamic invocation.
1271 'eval',
1272 'assert',
1273 'create_function',
1274 'call_user_func',
1275 'call_user_func_array',
1276 'forward_static_call',
1277 'forward_static_call_array',
1278 'array_map',
1279 'array_filter',
1280 'array_walk',
1281 'array_walk_recursive',
1282 'array_reduce',
1283 'usort',
1284 'uasort',
1285 'uksort',
1286 'ob_start',
1287 'register_shutdown_function',
1288 'register_tick_function',
1289 'set_error_handler',
1290 'set_exception_handler',
1291 'spl_autoload_register',
1292 'iterator_apply',
1293 'header_register_callback',
1294 'stream_filter_register',
1295 'stream_wrapper_register',
1296
1297 // Deserialization.
1298 'unserialize',
1299 'maybe_unserialize',
1300
1301 // Variable / scope handling.
1302 'extract',
1303 'compact',
1304 'parse_str',
1305 'mb_parse_str',
1306 'import_request_variables',
1307
1308 // File read / write / delete / manipulation.
1309 'include',
1310 'include_once',
1311 'require',
1312 'require_once',
1313 'file_get_contents',
1314 'file_put_contents',
1315 'readfile',
1316 'fopen',
1317 'fread',
1318 'fgets',
1319 'fgetcsv',
1320 'fscanf',
1321 'fwrite',
1322 'fputs',
1323 'fpassthru',
1324 'file',
1325 'unlink',
1326 'copy',
1327 'rename',
1328 'rmdir',
1329 'mkdir',
1330 'chmod',
1331 'chown',
1332 'chgrp',
1333 'touch',
1334 'symlink',
1335 'link',
1336 'tempnam',
1337 'tmpfile',
1338 'move_uploaded_file',
1339 'scandir',
1340 'glob',
1341 'opendir',
1342 'readdir',
1343 'realpath',
1344 'parse_ini_file',
1345 'parse_ini_string',
1346 'highlight_file',
1347 'show_source',
1348 'php_strip_whitespace',
1349
1350 // Network / HTTP.
1351 'fsockopen',
1352 'pfsockopen',
1353 'stream_socket_client',
1354 'stream_socket_server',
1355 'curl_init',
1356 'curl_exec',
1357 'curl_multi_exec',
1358 'curl_setopt',
1359 'curl_setopt_array',
1360
1361 // Template / include (WordPress).
1362 'get_template_part',
1363 'load_template',
1364 'locate_template',
1365 'get_header',
1366 'get_footer',
1367 'get_sidebar',
1368 'comments_template',
1369
1370 // Nonce related.
1371 'wp_nonce_url',
1372 'wp_nonce_field',
1373 'wp_create_nonce',
1374 'check_admin_referer',
1375 'check_ajax_referer',
1376 'wp_verify_nonce',
1377
1378 // PHP environment.
1379 'constant',
1380 'defined',
1381 'get_current_user',
1382 'get_defined_constants',
1383 'get_defined_functions',
1384 'get_defined_vars',
1385 'get_extension_funcs',
1386 'get_include_path',
1387 'get_included_files',
1388 'get_loaded_extensions',
1389 'get_required_files',
1390 'get_resources',
1391 'getcwd',
1392 'sys_get_temp_dir',
1393 'get_cfg_var',
1394 'getmypid',
1395 'getmyuid',
1396 'getmygid',
1397 'getmyinode',
1398 'getlastmod',
1399 'getrusage',
1400 'getenv',
1401 'getopt',
1402 'putenv',
1403 'ini_alter',
1404 'ini_get',
1405 'ini_get_all',
1406 'ini_restore',
1407 'ini_set',
1408 'php_ini_loaded_file',
1409 'php_ini_scanned_files',
1410 'php_sapi_name',
1411 'php_uname',
1412 'phpinfo',
1413 'phpversion',
1414 'phpcredits',
1415 'debug_backtrace',
1416 'debug_print_backtrace',
1417 'error_log',
1418 'error_get_last',
1419 'apache_setenv',
1420 'apache_getenv',
1421 'apache_note',
1422 'posix_getpwuid',
1423 'posix_getuid',
1424 'posix_geteuid',
1425 'posix_getgid',
1426 'posix_kill',
1427
1428 // WordPress data access / modification.
1429 'get_userdata',
1430 'get_currentuserinfo',
1431 'wp_get_current_user',
1432 'get_post',
1433 'get_posts',
1434 'get_term',
1435 'get_terms',
1436 'get_comment',
1437 'get_users',
1438 'get_option',
1439 'add_option',
1440 'update_option',
1441 'delete_option',
1442 'get_site_option',
1443 'update_site_option',
1444 'get_user_meta',
1445 'add_user_meta',
1446 'update_user_meta',
1447 'delete_user_meta',
1448 'get_post_meta',
1449 'update_post_meta',
1450 'delete_post_meta',
1451 'wp_insert_post',
1452 'wp_update_post',
1453 'wp_delete_post',
1454 'wp_insert_user',
1455 'wp_create_user',
1456 'wp_update_user',
1457 'wp_delete_user',
1458 'wp_set_password',
1459 'wp_set_auth_cookie',
1460 'wp_set_current_user',
1461 'wp_signon',
1462
1463 // WordPress filesystem / uploads / HTTP.
1464 'wp_filesystem',
1465 'request_filesystem_credentials',
1466 'wp_upload_bits',
1467 'wp_handle_upload',
1468 'wp_remote_get',
1469 'wp_remote_post',
1470 'wp_remote_request',
1471 'wp_remote_head',
1472 'wp_safe_remote_get',
1473 'wp_safe_remote_post',
1474 'wp_safe_remote_request',
1475 'wp_safe_remote_head',
1476 'wp_mail',
1477
1478 // WordPress hooks / shortcodes.
1479 'do_action',
1480 'add_action',
1481 'remove_action',
1482 'add_filter',
1483 'remove_filter',
1484 'apply_filters',
1485 'do_shortcode',
1486
1487 // Additional disallowed callbacks (Pods tag/shortcode rendering wrappers).
1488 'pods_do_shortcode',
1489 'pods_evaluate_tag',
1490 'pods_evaluate_tags',
1491 'pods_evaluate_tag_sanitized',
1492 'pods_evaluate_tags_sql',
1493
1494 // Debug / introspection output.
1495 'print_r',
1496 'var_dump',
1497 'var_export',
1498 'debug_zval_dump',
1499 ];
1500
1501 $allowed = [];
1502
1503 if ( defined( 'PODS_DISPLAY_CALLBACKS' ) ) {
1504 $display_callbacks = PODS_DISPLAY_CALLBACKS;
1505 } else {
1506 $first_pods_version = get_option( 'pods_framework_version_first' );
1507 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
1508
1509 $display_callbacks = 'restricted';
1510 }
1511
1512 if ( '0' === $display_callbacks ) {
1513 return false;
1514 }
1515
1516 // Maybe specify the list of allowed callbacks.
1517 if ( 'customized' === $display_callbacks ) {
1518 if ( defined( 'PODS_DISPLAY_CALLBACKS_ALLOWED' ) ) {
1519 $display_callbacks_allowed = PODS_DISPLAY_CALLBACKS_ALLOWED;
1520 } else {
1521 // Maybe specify the list of allowed callbacks
1522 $display_callbacks_allowed = 'esc_attr,esc_html';
1523 }
1524
1525 if ( ! is_array( $display_callbacks_allowed ) ) {
1526 $display_callbacks_allowed = str_replace( "\n", ',', $display_callbacks_allowed );
1527 $display_callbacks_allowed = explode( ',', $display_callbacks_allowed );
1528 }
1529
1530 $display_callbacks_allowed = array_map( 'trim', $display_callbacks_allowed );
1531 $display_callbacks_allowed = array_filter( $display_callbacks_allowed );
1532
1533 if ( ! empty( $display_callbacks_allowed ) ) {
1534 $allowed = $display_callbacks_allowed;
1535 }
1536 }
1537
1538 /**
1539 * Allows adjusting the disallowed callbacks as needed.
1540 *
1541 * @param array $disallowed List of callbacks not allowed.
1542 * @param array $params Parameters used by Pods::helper() method.
1543 *
1544 * @since 2.7.0
1545 */
1546 $disallowed = apply_filters( 'pods_helper_disallowed_callbacks', $disallowed, $params );
1547
1548 /**
1549 * Allows adjusting the allowed callbacks as needed.
1550 *
1551 * @param array $allowed List of callbacks explicitly allowed.
1552 * @param array $params Parameters used by Pods::helper() method.
1553 *
1554 * @since 2.7.0
1555 */
1556 $allowed = apply_filters( 'pods_helper_allowed_callbacks', $allowed, $params );
1557
1558 // Clean up helper callback (if string).
1559 if ( is_string( $callback ) ) {
1560 $callback = strip_tags( str_replace( array( '`', chr( 96 ) ), "'", $callback ) );
1561 }
1562
1563 /*
1564 * Normalize for comparison. PHP function/method names are case-insensitive
1565 * and may be written with a leading namespace separator, so "SYSTEM",
1566 * "System", and "\system" must all be treated as "system". The allowed and
1567 * disallowed lists are normalized the same way so matching is consistent.
1568 */
1569 $normalized_callback = ltrim( strtolower( trim( (string) $callback ) ), '\\' );
1570
1571 /*
1572 * Reject class method callbacks expressed as strings unless class callbacks
1573 * are explicitly enabled. The scope resolution operator "::" only appears in
1574 * static method references such as "Class::method", "\Namespace\Class::method",
1575 * or "parent::method".
1576 */
1577 if ( ! $allow_class_callbacks && false !== strpos( $normalized_callback, '::' ) ) {
1578 return false;
1579 }
1580
1581 $disallowed = array_map( 'strtolower', $disallowed );
1582 $allowed = array_map( 'strtolower', $allowed );
1583
1584 return (
1585 ! in_array( $normalized_callback, $disallowed, true )
1586 && (
1587 empty( $allowed )
1588 || in_array( $normalized_callback, $allowed, true )
1589 )
1590 );
1591 }
1592
1593 /**
1594 * Get the bleep placeholder text.
1595 *
1596 * @since 3.1.0
1597 *
1598 * @return string The bleep placeholder text.
1599 */
1600 function pods_access_bleep_placeholder() {
1601 return '****************';
1602 }
1603
1604 /**
1605 * Process the value and bleep it if it needs to be.
1606 *
1607 * @since 3.1.0
1608 *
1609 * @param string|mixed $value The value to be bleeped.
1610 *
1611 * @return string|mixed The bleeped text if not empty, otherwise the value as it was.
1612 */
1613 function pods_access_bleep_text( $value ) {
1614 $bleep_text = pods_access_bleep_placeholder();
1615
1616 if ( 0 < strlen( (string) $value ) ) {
1617 $value = $bleep_text;
1618 }
1619
1620 return $value;
1621 }
1622
1623 /**
1624 * Process the data and bleep anything that needs to be.
1625 *
1626 * @since 3.1.0
1627 *
1628 * @param array|object $data The data to be bleeped.
1629 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
1630 *
1631 * @return array|object The bleeped data.
1632 */
1633 function pods_access_bleep_data( $data, array $additional_bleep_properties = [] ) {
1634 $bleep_properties = [
1635 'user_pass',
1636 'user_activation_key',
1637 'post_password',
1638 ];
1639
1640 /**
1641 * Allow filtering the additional properties to be bleeped from objects and arrays.
1642 *
1643 * @since 3.1.0
1644 *
1645 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
1646 * @param array|object $data The data to be bleeped.
1647 */
1648 $additional_bleep_properties = apply_filters( 'pods_access_bleep_properties', $additional_bleep_properties, $data );
1649
1650 $bleep_properties = array_merge( $bleep_properties, $additional_bleep_properties );
1651
1652 $bleep_text = pods_access_bleep_placeholder();
1653
1654 if ( is_object( $data ) ) {
1655 foreach ( $bleep_properties as $bleep_property ) {
1656 if ( isset( $data->{$bleep_property} ) ) {
1657 $data->{$bleep_property} = 0 < strlen( (string) $data->{$bleep_property} ) ? $bleep_text : '';
1658 }
1659 }
1660 } elseif ( is_array( $data ) ) {
1661 foreach ( $bleep_properties as $bleep_property ) {
1662 if ( isset( $data[ $bleep_property ] ) ) {
1663 $data[ $bleep_property ] = 0 < strlen( (string) $data[ $bleep_property ] ) ? $bleep_text : '';
1664 }
1665 }
1666 }
1667
1668 return $data;
1669 }
1670
1671 /**
1672 * Process the data and bleep anything that needs to be.
1673 *
1674 * @since 3.1.0
1675 *
1676 * @param array $items The items to be bleeped.
1677 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
1678 *
1679 * @return array|object The bleeped data.
1680 */
1681 function pods_access_bleep_items( array $items, array $additional_bleep_properties = [] ) {
1682 // Call the pods_access_bleep_data() function for all items in the $items array.
1683 return array_map(
1684 static function ( $item ) use ( $additional_bleep_properties ) {
1685 return pods_access_bleep_data( $item, $additional_bleep_properties );
1686 },
1687 $items
1688 );
1689 }
1690
1691 /**
1692 * Determine whether the SQL fragment is allowed to be used.
1693 *
1694 * @since 3.1.0
1695 *
1696 * @param string $sql The SQL fragment to check.
1697 * @param string $context The SQL fragment context.
1698 * @param array $args {
1699 * The arguments to use.
1700 *
1701 * @type string|null $object_type The object type.
1702 * @type string|null $object_name The object name.
1703 * @type int|string|null $item_id The item ID.
1704 * @type Pods|null $pods The Pods object.
1705 * @type Pod|null $pod The Pod object.
1706 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1707 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1708 * }
1709 *
1710 * @return bool Whether the SQL fragment is allowed to be used.
1711 */
1712 function pods_access_sql_fragment_is_allowed( $sql, $context, array $args = [] ) {
1713 $context = strtoupper( $context );
1714
1715 $info = pods_info_from_args( $args );
1716
1717 /**
1718 * Allows filtering whether the SQL fragment is allowed to be used.
1719 *
1720 * @since 3.1.0
1721 *
1722 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1723 * @param string $sql The SQL fragment to check.
1724 * @param string $context The SQL fragment context.
1725 * @param array $info Pod information.
1726 */
1727 return (bool) apply_filters( 'pods_access_sql_fragment_is_allowed', true, $sql, $context, $info );
1728 }
1729
1730 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_mismatch_parenthesis', 10, 2 );
1731 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_comments', 10, 2 );
1732 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_unsafe_functions', 10, 2 );
1733 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_unsafe_keywords', 10, 2 );
1734 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_unsafe_tables', 10, 2 );
1735 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_double_hyphens', 10, 2 );
1736 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_subqueries', 10, 2 );
1737 //add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_post_status', 10, 4 );
1738
1739 /**
1740 * Disallow parenthesis in SQL fragments that are not balanced at every position.
1741 *
1742 * @since 3.1.0
1743 *
1744 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1745 * @param string $sql The SQL fragment to check.
1746 *
1747 * @return bool Whether the SQL fragment is allowed to be used.
1748 */
1749 function pods_access_sql_fragment_disallow_mismatch_parenthesis( $allowed, $sql ) {
1750 if ( ! $allowed ) {
1751 return $allowed;
1752 }
1753
1754 // Remove quoted string literals ('' and "" quoting, with backslash/doubled-quote escaping).
1755 $stripped = preg_replace(
1756 [
1757 "/'(?:[^'\\\\]|\\\\.|'')*'/s",
1758 '/"(?:[^"\\\\]|\\\\.|"")*"/s',
1759 ],
1760 '',
1761 $sql
1762 );
1763
1764 if ( null === $stripped ) {
1765 // preg_replace failed (e.g. malformed input); fail closed.
1766 return false;
1767 }
1768
1769 $depth = 0;
1770 $length = strlen( $stripped );
1771
1772 for ( $i = 0; $i < $length; $i++ ) {
1773 $char = $stripped[ $i ];
1774
1775 if ( '(' === $char ) {
1776 $depth++;
1777 } elseif ( ')' === $char ) {
1778 $depth--;
1779
1780 // More closes than opens at this point: the fragment escapes its wrapping.
1781 if ( $depth < 0 ) {
1782 return false;
1783 }
1784 }
1785 }
1786
1787 return 0 === $depth;
1788 }
1789
1790 /**
1791 * Disallow unsafe functions from being used in SQL fragments.
1792 *
1793 * @since 3.1.0
1794 *
1795 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1796 * @param string $sql The SQL fragment to check.
1797 *
1798 * @return bool Whether the SQL fragment is allowed to be used.
1799 */
1800 function pods_access_sql_fragment_disallow_unsafe_functions( $allowed, $sql ) {
1801 if ( ! $allowed ) {
1802 return $allowed;
1803 }
1804
1805 $unsafe_functions = [
1806 // Server / database / session information functions.
1807 'USER',
1808 'CURRENT_USER',
1809 'SESSION_USER',
1810 'SYSTEM_USER',
1811 'DATABASE',
1812 'SCHEMA',
1813 'VERSION',
1814 'CONNECTION_ID',
1815 'CURRENT_ROLE',
1816 'ROW_COUNT',
1817 'LAST_INSERT_ID',
1818 'CHARSET',
1819 'COLLATION',
1820 'COERCIBILITY',
1821 'STATEMENT_DIGEST',
1822 'STATEMENT_DIGEST_TEXT',
1823
1824 // Filesystem access.
1825 'LOAD_FILE',
1826
1827 // Timing / locking functions.
1828 'SLEEP',
1829 'BENCHMARK',
1830 'GET_LOCK',
1831 'RELEASE_LOCK',
1832 'RELEASE_ALL_LOCKS',
1833 'IS_FREE_LOCK',
1834 'IS_USED_LOCK',
1835 'WAIT_FOR_EXECUTED_GTID_SET',
1836 'WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS',
1837 'MASTER_POS_WAIT',
1838 'SOURCE_POS_WAIT',
1839 'GTID_SUBSET',
1840 'GTID_SUBTRACT',
1841
1842 // Encoding / encryption / compression functions.
1843 'FROM_BASE64',
1844 'TO_BASE64',
1845 'UNHEX',
1846 'AES_ENCRYPT',
1847 'AES_DECRYPT',
1848 'DES_ENCRYPT',
1849 'DES_DECRYPT',
1850 'ENCODE',
1851 'DECODE',
1852 'COMPRESS',
1853 'UNCOMPRESS',
1854 'UNCOMPRESSED_LENGTH',
1855
1856 // Error-based extraction (leak data through forced XPath / other errors).
1857 'EXTRACTVALUE',
1858 'UPDATEXML',
1859
1860 // Deprecated analysis clause.
1861 'ANALYSE',
1862
1863 // Common lib_mysqludf_sys UDFs.
1864 'SYS_EXEC',
1865 'SYS_EVAL',
1866 ];
1867
1868 /**
1869 * Allow filtering the list of additional unsafe functions to disallow.
1870 *
1871 * @since 3.1.0
1872 *
1873 * @param array $unsafe_functions The list of unsafe functions to disallow.
1874 * @param string $sql The SQL fragment to check.
1875 */
1876 $additional_unsafe_functions = (array) apply_filters( 'pods_access_sql_fragment_disallow_unsafe_functions', $unsafe_functions, $sql );
1877
1878 $unsafe_functions = array_unique( array_filter( array_merge( $unsafe_functions, $additional_unsafe_functions ) ) );
1879
1880 foreach ( $unsafe_functions as $unsafe_function ) {
1881 if ( 1 === (int) preg_match( '/\s*' . preg_quote( $unsafe_function, '/' ) . '\s*\(/i', $sql ) ) {
1882 return false;
1883 }
1884 }
1885
1886 return $allowed;
1887 }
1888
1889 /**
1890 * Disallow unsafe tables from being used in SQL fragments.
1891 *
1892 * @since 3.1.0
1893 *
1894 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1895 * @param string $sql The SQL fragment to check.
1896 *
1897 * @return bool Whether the SQL fragment is allowed to be used.
1898 */
1899 function pods_access_sql_fragment_disallow_unsafe_tables( $allowed, $sql ) {
1900 if ( ! $allowed ) {
1901 return $allowed;
1902 }
1903
1904 $unsafe_tables = [
1905 'mysql.',
1906 'information_schema.',
1907 'performance_schema.',
1908 'sys.',
1909 ];
1910
1911 /**
1912 * Allow filtering the list of unsafe tables to disallow.
1913 *
1914 * @since 3.1.0
1915 *
1916 * @param array $unsafe_tables The list of unsafe tables to disallow.
1917 * @param string $sql The SQL fragment to check.
1918 */
1919 $unsafe_tables = (array) apply_filters( 'pods_access_sql_fragment_disallow_unsafe_tables', $unsafe_tables, $sql );
1920
1921 $unsafe_tables = array_filter( $unsafe_tables );
1922
1923 /*
1924 * Normalize the fragment before matching so that identifier quoting and
1925 * spacing around the "." separator cannot be used to evade the check, e.g.
1926 * "`information_schema`.`tables`" or "information_schema . tables" both
1927 * normalize to "information_schema.tables".
1928 */
1929 $normalized_sql = str_replace( '`', '', $sql );
1930 $normalized_sql = preg_replace( '/\s*\.\s*/', '.', $normalized_sql );
1931
1932 foreach ( $unsafe_tables as $unsafe_table ) {
1933 if ( 1 === (int) preg_match( '/' . preg_quote( $unsafe_table, '/' ) . '/i', $normalized_sql ) ) {
1934 return false;
1935 }
1936 }
1937
1938 return $allowed;
1939 }
1940
1941 /**
1942 * Disallow double hyphens from being used in SQL fragments.
1943 *
1944 * @since 3.1.0
1945 *
1946 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1947 * @param string $sql The SQL fragment to check.
1948 *
1949 * @return bool Whether the SQL fragment is allowed to be used.
1950 */
1951 function pods_access_sql_fragment_disallow_double_hyphens( $allowed, $sql ) {
1952 return (
1953 $allowed
1954 && false === strpos( $sql, '--' )
1955 );
1956 }
1957
1958 /**
1959 * Disallow SQL comment markers from being used in SQL fragments.
1960 *
1961 * @since 3.1.0
1962 * @param bool $allowed Whether the SQL fragment is allowed to be used.
1963 * @param string $sql The SQL fragment to check.
1964 * @return bool Whether the SQL fragment is allowed to be used.
1965 */
1966 function pods_access_sql_fragment_disallow_comments( $allowed, $sql ) {
1967 if ( ! $allowed ) {
1968 return $allowed;
1969 }
1970
1971 if (
1972 false !== strpos( $sql, '--' )
1973 || false !== strpos( $sql, '/*' )
1974 || false !== strpos( $sql, '*/' )
1975 ) {
1976 return false;
1977 }
1978
1979 // Strip quoted string literals so a "#" inside a value is not treated as a comment.
1980 $stripped = preg_replace(
1981 [
1982 "/'(?:[^'\\\\]|\\\\.|'')*'/s",
1983 '/"(?:[^"\\\\]|\\\\.|"")*"/s',
1984 ],
1985 '',
1986 $sql
1987 );
1988
1989 if ( null === $stripped ) {
1990 // preg_replace failed (e.g. malformed input); fail closed.
1991 return false;
1992 }
1993
1994 return false === strpos( $stripped, '#' );
1995 }
1996
1997 /**
1998 * Disallow unsafe keywords from being used in SQL fragments.
1999 *
2000 * @since 3.1.0
2001 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2002 * @param string $sql The SQL fragment to check.
2003 * @return bool Whether the SQL fragment is allowed to be used.
2004 */
2005 function pods_access_sql_fragment_disallow_unsafe_keywords( $allowed, $sql ) {
2006 if ( ! $allowed ) {
2007 return $allowed;
2008 }
2009
2010 $unsafe_patterns = [
2011 // System / session variables.
2012 '/@@/',
2013 // Combining result sets.
2014 '/\bUNION\b/i',
2015 // File output keywords.
2016 '/\bINTO\s+(?:OUTFILE|DUMPFILE)\b/i',
2017 // File read keywords.
2018 '/\bLOAD\s+DATA\b/i',
2019 // Statement separator.
2020 '/;/',
2021 ];
2022
2023 /**
2024 * Allow filtering the list of unsafe keyword patterns to disallow.
2025 *
2026 * Each entry is a full PCRE pattern (including delimiters and flags) that is
2027 * tested against the SQL fragment; a match disallows the fragment.
2028 *
2029 * @since 3.1.0
2030 *
2031 * @param array $unsafe_patterns The list of unsafe keyword patterns to disallow.
2032 * @param string $sql The SQL fragment to check.
2033 */
2034 $unsafe_patterns = (array) apply_filters( 'pods_access_sql_fragment_disallow_unsafe_keywords', $unsafe_patterns, $sql );
2035
2036 $unsafe_patterns = array_filter( $unsafe_patterns );
2037
2038 foreach ( $unsafe_patterns as $unsafe_pattern ) {
2039 if ( 1 === (int) preg_match( $unsafe_pattern, $sql ) ) {
2040 return false;
2041 }
2042 }
2043
2044 return $allowed;
2045 }
2046
2047 /**
2048 * Disallow subqueries from being used in SQL fragments.
2049 *
2050 * @since 3.1.0
2051 *
2052 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2053 * @param string $sql The SQL fragment to check.
2054 *
2055 * @return bool Whether the SQL fragment is allowed to be used.
2056 */
2057 function pods_access_sql_fragment_disallow_subqueries( $allowed, $sql ) {
2058 return (
2059 $allowed
2060 && 0 === (int) preg_match( '/\s*SELECT(\s|\()+/i', $sql )
2061 );
2062 }
2063
2064 /**
2065 * Disallow post_status from being used in the WHERE/HAVING SQL fragment unless they have admin access.
2066 *
2067 * @since 3.1.0
2068 *
2069 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2070 * @param string $sql The SQL fragment to check.
2071 * @param string $context The SQL fragment context.
2072 * @param array $info Pod information.
2073 *
2074 * @return bool Whether the SQL fragment is allowed to be used.
2075 */
2076 function pods_access_sql_fragment_disallow_post_status( $allowed, $sql, $context, array $info ) {
2077 if ( 'WHERE' !== $context && 'HAVING' !== $context && 'FIELD' !== $context ) {
2078 return $allowed;
2079 }
2080
2081 return (
2082 $allowed
2083 && (
2084 false === stripos( $sql, 'post_status' )
2085 || pods_is_admin( 'edit_posts' )
2086 )
2087 );
2088 }
2089
2090 /**
2091 * Safely unserialize data if it's PHP serialized.
2092 *
2093 * @since 3.1.0
2094 *
2095 * @param string|mixed $data The data to unserialize.
2096 *
2097 * @return array|string|mixed The unserialized data if it was PHP serialized, otherwise the data as it was.
2098 */
2099 function pods_maybe_safely_unserialize( $data ) {
2100 // The $options parameter of unserialize() requires PHP 7.0+.
2101 if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
2102 // On PHP < 7, refuse payloads that contain a serialized object; other data falls back to the normal WP function, to help prevent security issues.
2103 if ( is_string( $data ) && preg_match( '/(?:^|;|{)[OC]:\d+:"/', $data ) ) {
2104 return $data;
2105 }
2106
2107 // Fall back to normal WP function.
2108 return maybe_unserialize( $data );
2109 }
2110
2111 // Check if the data is serialized.
2112 if ( is_serialized( $data ) ) {
2113 $data = trim( $data );
2114
2115 // Unserialize the data but exclude classes.
2116 return @unserialize( $data, [ 'allowed_classes' => false ] );
2117 }
2118
2119 return $data;
2120 }
2121