cli
23 hours ago
fields
23 hours ago
widgets
23 hours ago
Pods.php
23 hours ago
PodsAPI.php
23 hours ago
PodsAdmin.php
23 hours ago
PodsArray.php
23 hours ago
PodsComponent.php
23 hours ago
PodsComponents.php
23 hours ago
PodsData.php
23 hours ago
PodsField.php
23 hours ago
PodsForm.php
23 hours ago
PodsI18n.php
23 hours ago
PodsInit.php
23 hours ago
PodsMeta.php
23 hours ago
PodsMigrate.php
23 hours ago
PodsRESTFields.php
23 hours ago
PodsRESTHandlers.php
23 hours ago
PodsTermSplitting.php
23 hours ago
PodsUI.php
23 hours ago
PodsView.php
23 hours ago
PodsView.php
999 lines
| 1 | <?php |
| 2 | |
| 3 | // Don't load directly. |
| 4 | if ( ! defined( 'ABSPATH' ) ) { |
| 5 | die( '-1' ); |
| 6 | } |
| 7 | |
| 8 | use Pods\Static_Cache; |
| 9 | |
| 10 | /** |
| 11 | * @package Pods |
| 12 | */ |
| 13 | class PodsView { |
| 14 | |
| 15 | /** |
| 16 | * List of keys that have been cached grouped by cache mode. |
| 17 | * |
| 18 | * @since 3.0 |
| 19 | * |
| 20 | * @var array |
| 21 | */ |
| 22 | private static $cached_keys = [ |
| 23 | 'transient' => [], |
| 24 | 'site-transient' => [], |
| 25 | 'cache' => [], |
| 26 | 'static-cache' => [], |
| 27 | 'option-cache' => [], |
| 28 | ]; |
| 29 | |
| 30 | /** |
| 31 | * @var array $cache_modes Array of available cache modes |
| 32 | */ |
| 33 | public static $cache_modes = [ |
| 34 | 'none' => true, |
| 35 | 'transient' => true, |
| 36 | 'site-transient' => true, |
| 37 | 'cache' => true, |
| 38 | 'static-cache' => true, |
| 39 | 'option-cache' => true, |
| 40 | ]; |
| 41 | |
| 42 | /** |
| 43 | * @return \PodsView |
| 44 | */ |
| 45 | private function __construct() { |
| 46 | // !nope |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Add a cache key to keep track of for a cache mode. |
| 51 | * |
| 52 | * @since 3.0 |
| 53 | * |
| 54 | * @param string $cache_mode The cache mode. |
| 55 | * @param string $cache_key The cache key. |
| 56 | * @param null|string $group The cache group, if needed. |
| 57 | * @param null|string $original_key The original cache key, if different from the cache key. |
| 58 | */ |
| 59 | public static function add_cached_key( $cache_mode, $cache_key, $group = null, $original_key = null ) { |
| 60 | if ( ! isset( self::$cached_keys[ $cache_mode ] ) ) { |
| 61 | self::$cached_keys[ $cache_mode ] = []; |
| 62 | } |
| 63 | |
| 64 | if ( null === $original_key ) { |
| 65 | $original_key = $cache_key; |
| 66 | } |
| 67 | |
| 68 | if ( $group ) { |
| 69 | if ( ! isset( self::$cached_keys[ $cache_mode ][ $group ] ) ) { |
| 70 | self::$cached_keys[ $cache_mode ][ $group ] = []; |
| 71 | } |
| 72 | |
| 73 | self::$cached_keys[ $cache_mode ][ $group ][ $cache_key ] = $original_key; |
| 74 | |
| 75 | return; |
| 76 | } |
| 77 | |
| 78 | self::$cached_keys[ $cache_mode ][ $cache_key ] = $original_key; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Remove a cache key from tracking for a cache mode. |
| 83 | * |
| 84 | * @since 3.0 |
| 85 | * |
| 86 | * @param string $cache_mode The cache mode. |
| 87 | * @param string $cache_key The cache key. |
| 88 | * @param null|string $group The cache group, if needed. |
| 89 | */ |
| 90 | public static function remove_cached_key( $cache_mode, $cache_key, $group = null ) { |
| 91 | if ( ! isset( self::$cached_keys[ $cache_mode ] ) ) { |
| 92 | return; |
| 93 | } |
| 94 | |
| 95 | if ( $group ) { |
| 96 | if ( ! isset( self::$cached_keys[ $cache_mode ][ $group ] ) ) { |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | unset( self::$cached_keys[ $cache_mode ][ $group ][ $cache_key ] ); |
| 101 | |
| 102 | return; |
| 103 | } |
| 104 | |
| 105 | if ( ! isset( self::$cached_keys[ $cache_mode ][ $cache_key ] ) ) { |
| 106 | return; |
| 107 | } |
| 108 | |
| 109 | unset( self::$cached_keys[ $cache_mode ][ $cache_key ] ); |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Get the list of cache keys based on cache mode. |
| 114 | * |
| 115 | * @since 3.0 |
| 116 | * |
| 117 | * @param string $cache_mode The cache mode. |
| 118 | * @param null|string $group The cache group, if needed. |
| 119 | */ |
| 120 | public static function get_cached_keys( $cache_mode, $group = null ) : array { |
| 121 | if ( $group ) { |
| 122 | return self::$cached_keys[ $cache_mode ][ $group ] ?? []; |
| 123 | } |
| 124 | |
| 125 | return self::$cached_keys[ $cache_mode ] ?? []; |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Reset cache keys based on cache mode. |
| 130 | * |
| 131 | * @since 3.0 |
| 132 | * |
| 133 | * @param null|string $cache_mode The cache mode, null if resetting all. |
| 134 | * @param null|string $group The cache group, if needed. |
| 135 | */ |
| 136 | public static function reset_cached_keys( $cache_mode = null, $group = null ) { |
| 137 | if ( null === $cache_mode ) { |
| 138 | foreach ( self::$cache_modes as $cache_mode_to_reset => $unused ) { |
| 139 | if ( isset( self::$cached_keys[ $cache_mode_to_reset ] ) ) { |
| 140 | self::$cached_keys[ $cache_mode_to_reset ] = []; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | if ( $group ) { |
| 148 | if ( isset( self::$cached_keys[ $cache_mode ][ $group ] ) ) { |
| 149 | self::$cached_keys[ $cache_mode ][ $group ] = []; |
| 150 | } |
| 151 | |
| 152 | return; |
| 153 | } |
| 154 | |
| 155 | if ( isset( self::$cached_keys[ $cache_mode ] ) ) { |
| 156 | self::$cached_keys[ $cache_mode ] = []; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * @static |
| 162 | * |
| 163 | * @param string $view Path of the view file |
| 164 | * @param array|null $data (optional) Data to pass on to the template |
| 165 | * @param bool|int|array $expires (optional) Time in seconds for the cache to expire, if 0 no expiration. |
| 166 | * @param string $cache_mode (optional) Decides the caching method to use for the view. |
| 167 | * @param bool $limited (optional) Whether to limit the view to only the theme directory, defaults to false |
| 168 | * |
| 169 | * @return bool|mixed|null|string|void |
| 170 | * |
| 171 | * @since 2.0.0 |
| 172 | */ |
| 173 | public static function view( $view, $data = null, $expires = false, $cache_mode = 'cache', $limited = false ) { |
| 174 | |
| 175 | /** |
| 176 | * Allow filtering the view before the logic runs. |
| 177 | * |
| 178 | * @since 2.4.1 |
| 179 | * |
| 180 | * @param null|false|string $filter_check The filter check. If not set to null, return the value as the output. Set to false to fail to load the view. |
| 181 | * @param string $view Path of the view file. |
| 182 | * @param array|null $data Data to pass on to the template. |
| 183 | * @param bool|int|array $expires Time in seconds for the cache to expire, if 0 no expiration. |
| 184 | * @param string $cache_mode Decides the caching method to use for the view. |
| 185 | * @param bool $limited Whether to limit the view to only the theme directory, defaults to false. |
| 186 | */ |
| 187 | $filter_check = apply_filters( 'pods_view_alt_view', null, $view, $data, $expires, $cache_mode, $limited ); |
| 188 | |
| 189 | if ( null !== $filter_check ) { |
| 190 | return $filter_check; |
| 191 | } |
| 192 | |
| 193 | // Advanced $expires handling |
| 194 | $expires = self::expires( $expires, $cache_mode ); |
| 195 | |
| 196 | if ( ! self::is_cache_mode_valid( $cache_mode ) ) { |
| 197 | $cache_mode = 'cache'; |
| 198 | } |
| 199 | |
| 200 | // Support my-view.php?custom-key=X#hash keying for cache |
| 201 | $view_id = ''; |
| 202 | |
| 203 | if ( ! is_array( $view ) ) { |
| 204 | $view_q = explode( '?', $view ); |
| 205 | |
| 206 | if ( 1 < count( $view_q ) ) { |
| 207 | $view_id = '?' . $view_q[1]; |
| 208 | |
| 209 | $view = $view_q[0]; |
| 210 | } |
| 211 | |
| 212 | $view_h = explode( '#', $view ); |
| 213 | |
| 214 | if ( 1 < count( $view_h ) ) { |
| 215 | $view_id .= '#' . $view_h[1]; |
| 216 | |
| 217 | $view = $view_h[0]; |
| 218 | } |
| 219 | |
| 220 | // Support dynamic tags! |
| 221 | $view_id = pods_evaluate_tags( $view_id ); |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Allow filtering the path of the view to use. |
| 226 | * |
| 227 | * @since unknown |
| 228 | * |
| 229 | * @param string $view Path of the view file. |
| 230 | * @param array|null $data Data to pass on to the template. |
| 231 | * @param bool|int|array $expires Time in seconds for the cache to expire, if 0 no expiration. |
| 232 | * @param string $cache_mode Decides the caching method to use for the view. |
| 233 | * @param bool $limited Whether to limit the view to only the theme directory, defaults to false. |
| 234 | */ |
| 235 | $view = apply_filters( 'pods_view_inc', $view, $data, $expires, $cache_mode, $limited ); |
| 236 | |
| 237 | $view_key = $view; |
| 238 | |
| 239 | if ( is_array( $view_key ) ) { |
| 240 | $view_key = implode( '-', $view_key ) . '.php'; |
| 241 | } |
| 242 | |
| 243 | if ( false !== realpath( $view_key ) ) { |
| 244 | $view_key = realpath( $view_key ); |
| 245 | } |
| 246 | |
| 247 | $view_key = (string) $view_key; |
| 248 | |
| 249 | $pods_ui_dir = realpath( PODS_DIR . 'ui/' ); |
| 250 | $pods_components_dir = realpath( PODS_DIR . 'components/' ); |
| 251 | $abspath_dir = realpath( ABSPATH ); |
| 252 | |
| 253 | $cache_key = pods_str_replace( $abspath_dir, '/', $view_key, 1 ); |
| 254 | |
| 255 | $output = false; |
| 256 | |
| 257 | $caching = false; |
| 258 | |
| 259 | if ( false !== $expires && false === strpos( $view_key, $pods_ui_dir ) && false === strpos( $view_key, $pods_components_dir ) ) { |
| 260 | $caching = true; |
| 261 | } |
| 262 | |
| 263 | if ( $caching ) { |
| 264 | $output = self::get( 'pods-view-' . $cache_key . $view_id, $cache_mode, 'pods_view' ); |
| 265 | } |
| 266 | |
| 267 | if ( false === $output || null === $output ) { |
| 268 | $output = self::get_template_part( $view, $data ); |
| 269 | } |
| 270 | |
| 271 | if ( false !== $output && $caching ) { |
| 272 | self::set( 'pods-view-' . $cache_key . $view_id, $output, $expires, $cache_mode, 'pods_view' ); |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Allow filtering the path of the view to use based on the cache key. |
| 277 | * |
| 278 | * @since unknown |
| 279 | * |
| 280 | * @param string|false $output The view output. Returns as false if the view fails to load. |
| 281 | * @param string $view Path of the view file. |
| 282 | * @param array|null $data Data to pass on to the template. |
| 283 | * @param bool|int|array $expires Time in seconds for the cache to expire, if 0 no expiration. |
| 284 | * @param string $cache_mode Decides the caching method to use for the view. |
| 285 | * @param bool $limited Whether to limit the view to only the theme directory, defaults to false. |
| 286 | */ |
| 287 | $output = apply_filters( "pods_view_output_{$cache_key}", $output, $view, $data, $expires, $cache_mode, $limited ); |
| 288 | |
| 289 | /** |
| 290 | * Allow filtering the path of the view to use. |
| 291 | * |
| 292 | * @since unknown |
| 293 | * |
| 294 | * @param string|false $output The view output. Returns as false if the view fails to load. |
| 295 | * @param string $view Path of the view file. |
| 296 | * @param array|null $data Data to pass on to the template. |
| 297 | * @param bool|int|array $expires Time in seconds for the cache to expire, if 0 no expiration. |
| 298 | * @param string $cache_mode Decides the caching method to use for the view. |
| 299 | * @param bool $limited Whether to limit the view to only the theme directory, defaults to false. |
| 300 | */ |
| 301 | $output = apply_filters( 'pods_view_output', $output, $view, $data, $expires, $cache_mode, $limited ); |
| 302 | |
| 303 | return $output; |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Get the full path of the view if it exists. |
| 308 | * |
| 309 | * @since 3.1.0 |
| 310 | * |
| 311 | * @param string $view Path of the view file |
| 312 | * @param bool $limited (optional) Whether to limit the view to only the theme directory, defaults to false |
| 313 | * |
| 314 | * @return string|false The full path of the view if it exists. |
| 315 | */ |
| 316 | public static function view_get_path( $view, $limited = false ) { |
| 317 | // Support my-view.php?custom-key=X#hash keying for cache |
| 318 | if ( ! is_array( $view ) ) { |
| 319 | $view_q = explode( '?', $view ); |
| 320 | |
| 321 | if ( 1 < count( $view_q ) ) { |
| 322 | $view = $view_q[0]; |
| 323 | } |
| 324 | |
| 325 | $view_h = explode( '#', $view ); |
| 326 | |
| 327 | if ( 1 < count( $view_h ) ) { |
| 328 | $view = $view_h[0]; |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | $view = apply_filters( 'pods_view_inc', $view, null, false, 'cache', $limited ); |
| 333 | |
| 334 | $view_key = $view; |
| 335 | |
| 336 | if ( is_array( $view_key ) ) { |
| 337 | $view_key = implode( '-', $view_key ) . '.php'; |
| 338 | } |
| 339 | |
| 340 | if ( false !== realpath( $view_key ) ) { |
| 341 | $view_key = realpath( $view_key ); |
| 342 | } |
| 343 | |
| 344 | $view_path = self::locate_template( $view_key, $limited ); |
| 345 | |
| 346 | if ( empty( $view_path ) ) { |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | return $view_path; |
| 351 | } |
| 352 | |
| 353 | /** |
| 354 | * Get the cache key, salted with current Pods version, peppered with md5 if too long |
| 355 | * |
| 356 | * @param string $key |
| 357 | * @param string $group_key |
| 358 | * |
| 359 | * @return string |
| 360 | * |
| 361 | * @since 2.6.2 |
| 362 | */ |
| 363 | public static function get_key( string $key, string $group_key = '' ): string { |
| 364 | |
| 365 | // Add some salt |
| 366 | $key .= '-' . sanitize_key( PODS_VERSION ); |
| 367 | |
| 368 | // Patch for limitations in DB |
| 369 | if ( is_string( $group_key ) && 44 < strlen( $group_key . $key ) ) { |
| 370 | $key = md5( $key ); |
| 371 | } |
| 372 | |
| 373 | return $key; |
| 374 | |
| 375 | } |
| 376 | |
| 377 | /** |
| 378 | * @static |
| 379 | * |
| 380 | * @param string $key Key for the cache |
| 381 | * @param string $cache_mode (optional) Decides the caching method to use for the view. |
| 382 | * @param string $group (optional) Set the group of the value. |
| 383 | * @param string $callback (optional) Callback function to run to set the value if not cached. |
| 384 | * |
| 385 | * @return bool|mixed|null|void |
| 386 | * |
| 387 | * @since 2.0.0 |
| 388 | */ |
| 389 | public static function get( $key, $cache_mode = 'cache', $group = '', $callback = null ) { |
| 390 | $external_object_cache = wp_using_ext_object_cache(); |
| 391 | |
| 392 | $object_cache_enabled = ( |
| 393 | ( |
| 394 | isset( $GLOBALS['wp_object_cache'] ) |
| 395 | && is_object( $GLOBALS['wp_object_cache'] ) |
| 396 | ) |
| 397 | || $external_object_cache |
| 398 | ); |
| 399 | |
| 400 | if ( ! self::is_cache_mode_valid( $cache_mode ) ) { |
| 401 | $cache_mode = 'cache'; |
| 402 | } |
| 403 | |
| 404 | $group_key = 'pods_'; |
| 405 | |
| 406 | if ( ! empty( $group ) ) { |
| 407 | $group_key = $group . '_'; |
| 408 | } |
| 409 | |
| 410 | $original_key = $key; |
| 411 | |
| 412 | // Get proper cache key |
| 413 | $key = self::get_key( $key, $group_key ); |
| 414 | |
| 415 | $value = null; |
| 416 | |
| 417 | $called = false; |
| 418 | |
| 419 | $pods_nocache = pods_v( 'pods_nocache' ); |
| 420 | $nocache = array(); |
| 421 | |
| 422 | if ( null !== $pods_nocache && 'static-cache' !== $cache_mode && pods_is_admin() ) { |
| 423 | if ( is_string( $pods_nocache ) && 1 < strlen( $pods_nocache ) ) { |
| 424 | $nocache = explode( ',', $pods_nocache ); |
| 425 | $nocache = array_flip( $nocache ); |
| 426 | } else { |
| 427 | $nocache = self::$cache_modes; |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | $cache_enabled = ! isset( $nocache[ $cache_mode ] ); |
| 432 | |
| 433 | if ( apply_filters( 'pods_view_cache_alt_get', false, $cache_mode, $group_key . $key, $original_key, $group ) ) { |
| 434 | $value = apply_filters( 'pods_view_cache_alt_get_value', $value, $cache_mode, $group_key . $key, $original_key, $group ); |
| 435 | } elseif ( $cache_enabled ) { |
| 436 | if ( 'transient' === $cache_mode ) { |
| 437 | $value = get_transient( $group_key . $key ); |
| 438 | } elseif ( 'site-transient' === $cache_mode ) { |
| 439 | $value = get_site_transient( $group_key . $key ); |
| 440 | } elseif ( 'cache' === $cache_mode && $object_cache_enabled ) { |
| 441 | $value = wp_cache_get( $key, ( empty( $group ) ? 'pods_view' : $group ) ); |
| 442 | } elseif ( 'option-cache' === $cache_mode ) { |
| 443 | $pre = apply_filters( "pre_transient_{$key}", false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 444 | |
| 445 | if ( false !== $pre ) { |
| 446 | $value = $pre; |
| 447 | } elseif ( $external_object_cache ) { |
| 448 | $cache_found = false; |
| 449 | |
| 450 | $value = wp_cache_get( $key, ( empty( $group ) ? 'pods_option_cache' : $group ), false, $cache_found ); |
| 451 | |
| 452 | if ( false === $value || ! $cache_found ) { |
| 453 | if ( is_callable( $callback ) ) { |
| 454 | // Callback function should do it's own set/update for cache |
| 455 | $callback_value = call_user_func( $callback, $original_key, $group, $cache_mode ); |
| 456 | |
| 457 | if ( null !== $callback_value && false !== $callback_value ) { |
| 458 | $value = $callback_value; |
| 459 | } |
| 460 | |
| 461 | $called = true; |
| 462 | } |
| 463 | } |
| 464 | } else { |
| 465 | $transient_option = '_pods_option_' . $key; |
| 466 | $transient_timeout = '_pods_option_timeout_' . $key; |
| 467 | |
| 468 | $value = get_option( $transient_option ); |
| 469 | $timeout = get_option( $transient_timeout ); |
| 470 | |
| 471 | if ( ! empty( $timeout ) && $timeout < time() ) { |
| 472 | if ( is_callable( $callback ) ) { |
| 473 | // Callback function should do it's own set/update for cache |
| 474 | $callback_value = call_user_func( $callback, $original_key, $group, $cache_mode ); |
| 475 | |
| 476 | if ( null !== $callback_value && false !== $callback_value ) { |
| 477 | $value = $callback_value; |
| 478 | } |
| 479 | |
| 480 | $called = true; |
| 481 | } else { |
| 482 | $value = false; |
| 483 | |
| 484 | delete_option( $transient_option ); |
| 485 | delete_option( $transient_timeout ); |
| 486 | } |
| 487 | } |
| 488 | }//end if |
| 489 | |
| 490 | if ( false !== $value ) { |
| 491 | $value = apply_filters( "transient_{$key}", $value ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 492 | } |
| 493 | } elseif ( 'static-cache' === $cache_mode ) { |
| 494 | $static_cache = pods_container( Static_Cache::class ); |
| 495 | |
| 496 | if ( $static_cache ) { |
| 497 | $value = $static_cache->get( $key, ( empty( $group ) ? 'pods_view' : $group ) ); |
| 498 | } else { |
| 499 | $value = false; |
| 500 | } |
| 501 | } else { |
| 502 | $value = false; |
| 503 | }//end if |
| 504 | } else { |
| 505 | $value = false; |
| 506 | }//end if |
| 507 | |
| 508 | if ( false === $value && is_callable( $callback ) && ! $called ) { |
| 509 | // Callback function should do it's own set/update for cache |
| 510 | $callback_value = call_user_func( $callback, $original_key, $group, $cache_mode ); |
| 511 | |
| 512 | if ( null !== $callback_value && false !== $callback_value ) { |
| 513 | $value = $callback_value; |
| 514 | } |
| 515 | } |
| 516 | |
| 517 | $value = apply_filters( "pods_view_get_{$cache_mode}", $value, $original_key, $group ); |
| 518 | |
| 519 | return $value; |
| 520 | } |
| 521 | |
| 522 | /** |
| 523 | * @static |
| 524 | * |
| 525 | * Set a cached value |
| 526 | * |
| 527 | * @param string $key Key for the cache |
| 528 | * @param mixed $value Value to add to the cache |
| 529 | * @param int $expires (optional) Time in seconds for the cache to expire, if 0 no expiration. |
| 530 | * @param string $cache_mode (optional) Decides the caching method to use for the view. |
| 531 | * @param string $group (optional) Set the group of the value. |
| 532 | * |
| 533 | * @return bool|mixed|null|string|void |
| 534 | * |
| 535 | * @since 2.0.0 |
| 536 | */ |
| 537 | public static function set( $key, $value, $expires = 0, $cache_mode = null, $group = '' ) { |
| 538 | $external_object_cache = wp_using_ext_object_cache(); |
| 539 | |
| 540 | $object_cache_enabled = ( |
| 541 | ( |
| 542 | isset( $GLOBALS['wp_object_cache'] ) |
| 543 | && is_object( $GLOBALS['wp_object_cache'] ) |
| 544 | ) |
| 545 | || $external_object_cache |
| 546 | ); |
| 547 | |
| 548 | // Advanced $expires handling |
| 549 | $expires = self::expires( $expires, $cache_mode ); |
| 550 | |
| 551 | if ( ! self::is_cache_mode_valid( $cache_mode ) ) { |
| 552 | $cache_mode = 'cache'; |
| 553 | } |
| 554 | |
| 555 | $group_key = 'pods_'; |
| 556 | |
| 557 | if ( ! empty( $group ) ) { |
| 558 | $group_key = $group . '_'; |
| 559 | } |
| 560 | |
| 561 | $original_key = $key; |
| 562 | |
| 563 | // Get proper cache key |
| 564 | $key = self::get_key( $key, $group_key ); |
| 565 | |
| 566 | if ( apply_filters( 'pods_view_cache_alt_set', false, $cache_mode, $group_key . $key, $original_key, $value, $expires, $group ) ) { |
| 567 | self::add_cached_key( $cache_mode, $group_key . $key, null, $original_key ); |
| 568 | |
| 569 | return $value; |
| 570 | } elseif ( 'transient' === $cache_mode ) { |
| 571 | self::add_cached_key( $cache_mode, $group_key . $key, null, $original_key ); |
| 572 | |
| 573 | set_transient( $group_key . $key, $value, $expires ); |
| 574 | } elseif ( 'site-transient' === $cache_mode ) { |
| 575 | self::add_cached_key( $cache_mode, $group_key . $key, null, $original_key ); |
| 576 | |
| 577 | set_site_transient( $group_key . $key, $value, $expires ); |
| 578 | } elseif ( 'cache' === $cache_mode && $object_cache_enabled ) { |
| 579 | $group = ( empty( $group ) ? 'pods_view' : $group ); |
| 580 | $key = ( empty( $key ) ? 'pods_view' : $key ); |
| 581 | |
| 582 | self::add_cached_key( $cache_mode, $key, $group, $original_key ); |
| 583 | |
| 584 | wp_cache_set( $key, $value, $group, $expires ); |
| 585 | } elseif ( 'option-cache' === $cache_mode ) { |
| 586 | $group = ( empty( $group ) ? 'pods_option_cache' : $group ); |
| 587 | |
| 588 | $value = apply_filters( "pre_set_transient_{$key}", $value ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 589 | |
| 590 | if ( $external_object_cache ) { |
| 591 | $result = wp_cache_set( $key, $value, $group, $expires ); |
| 592 | } else { |
| 593 | $transient_timeout = '_pods_option_timeout_' . $key; |
| 594 | $key = '_pods_option_' . $key; |
| 595 | |
| 596 | if ( false === get_option( $key ) ) { |
| 597 | if ( $expires ) { |
| 598 | add_option( $transient_timeout, time() + $expires, '', 'no' ); |
| 599 | } |
| 600 | |
| 601 | $result = add_option( $key, $value, '', 'no' ); |
| 602 | } else { |
| 603 | if ( $expires ) { |
| 604 | update_option( $transient_timeout, time() + $expires ); |
| 605 | } |
| 606 | |
| 607 | $result = update_option( $key, $value ); |
| 608 | } |
| 609 | }//end if |
| 610 | |
| 611 | self::add_cached_key( $cache_mode, $key, $group, $original_key ); |
| 612 | |
| 613 | if ( $result ) { |
| 614 | do_action( "set_transient_{$key}", $value, $expires ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 615 | do_action( 'setted_transient', $key, $value, $expires ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 616 | } |
| 617 | } elseif ( 'static-cache' === $cache_mode ) { |
| 618 | $static_cache = pods_container( Static_Cache::class ); |
| 619 | |
| 620 | if ( $static_cache ) { |
| 621 | $group = ( empty( $group ) ? __CLASS__ : $group ); |
| 622 | |
| 623 | self::add_cached_key( $cache_mode, $key, $group, $original_key ); |
| 624 | |
| 625 | $static_cache->set( $key, $value, $group ); |
| 626 | } |
| 627 | }//end if |
| 628 | |
| 629 | do_action( "pods_view_set_{$cache_mode}", $original_key, $value, $expires, $group ); |
| 630 | |
| 631 | return $value; |
| 632 | } |
| 633 | |
| 634 | /** |
| 635 | * @static |
| 636 | * |
| 637 | * Clear a cached value |
| 638 | * |
| 639 | * @param string|bool $key Key for the cache |
| 640 | * @param string $cache_mode (optional) Decides the caching method to use for the view. |
| 641 | * @param string $group (optional) Set the group. |
| 642 | * |
| 643 | * @return bool |
| 644 | * |
| 645 | * @since 2.0.0 |
| 646 | */ |
| 647 | public static function clear( $key = true, $cache_mode = null, $group = '' ) { |
| 648 | $external_object_cache = wp_using_ext_object_cache(); |
| 649 | |
| 650 | $object_cache_enabled = ( |
| 651 | ( |
| 652 | isset( $GLOBALS['wp_object_cache'] ) |
| 653 | && is_object( $GLOBALS['wp_object_cache'] ) |
| 654 | ) |
| 655 | || $external_object_cache |
| 656 | ); |
| 657 | |
| 658 | global $wpdb; |
| 659 | |
| 660 | if ( ! self::is_cache_mode_valid( $cache_mode ) ) { |
| 661 | $cache_mode = 'cache'; |
| 662 | } |
| 663 | |
| 664 | $group_key = 'pods_'; |
| 665 | |
| 666 | if ( ! empty( $group ) ) { |
| 667 | $group_key = $group . '_'; |
| 668 | } |
| 669 | |
| 670 | $full_key = $key; |
| 671 | $original_key = $key; |
| 672 | |
| 673 | if ( true !== $key ) { |
| 674 | // Get proper cache key |
| 675 | $key = self::get_key( $key, $group_key ); |
| 676 | |
| 677 | $full_key = $group_key . $key; |
| 678 | } |
| 679 | |
| 680 | if ( apply_filters( 'pods_view_cache_alt_set', false, $cache_mode, $full_key, $original_key, '', 0, $group ) ) { |
| 681 | self::remove_cached_key( $cache_mode, $full_key ); |
| 682 | |
| 683 | return true; |
| 684 | } elseif ( 'transient' === $cache_mode ) { |
| 685 | if ( true === $key ) { |
| 686 | $wpdb->query( |
| 687 | $wpdb->prepare( |
| 688 | "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE %s", |
| 689 | '_transient_' . $wpdb->esc_like( $group_key ) . '%' |
| 690 | ) |
| 691 | ); |
| 692 | |
| 693 | if ( $object_cache_enabled ) { |
| 694 | if ( $group && function_exists( 'wp_cache_flush_group' ) && wp_cache_supports( 'flush_group' ) ) { |
| 695 | wp_cache_flush_group( $group ); |
| 696 | } else { |
| 697 | wp_cache_flush(); |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | self::reset_cached_keys( $cache_mode ); |
| 702 | } else { |
| 703 | delete_transient( $group_key . $key ); |
| 704 | |
| 705 | self::remove_cached_key( $cache_mode, $group_key . $key ); |
| 706 | } |
| 707 | } elseif ( 'site-transient' === $cache_mode ) { |
| 708 | if ( true === $key ) { |
| 709 | $wpdb->query( |
| 710 | $wpdb->prepare( |
| 711 | "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE %s", |
| 712 | '_site_transient_' . $wpdb->esc_like( $group_key ) . '%' |
| 713 | ) |
| 714 | ); |
| 715 | |
| 716 | if ( $object_cache_enabled ) { |
| 717 | if ( $group && function_exists( 'wp_cache_flush_group' ) && wp_cache_supports( 'flush_group' ) ) { |
| 718 | wp_cache_flush_group( $group ); |
| 719 | } else { |
| 720 | wp_cache_flush(); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | self::reset_cached_keys( $cache_mode ); |
| 725 | } else { |
| 726 | delete_site_transient( $group_key . $key ); |
| 727 | |
| 728 | self::remove_cached_key( $cache_mode, $group_key . $key ); |
| 729 | } |
| 730 | } elseif ( 'cache' === $cache_mode && $object_cache_enabled ) { |
| 731 | if ( true === $key ) { |
| 732 | if ( $group && function_exists( 'wp_cache_flush_group' ) && wp_cache_supports( 'flush_group' ) ) { |
| 733 | wp_cache_flush_group( $group ); |
| 734 | |
| 735 | self::reset_cached_keys( $cache_mode, $group ); |
| 736 | } else { |
| 737 | wp_cache_flush(); |
| 738 | |
| 739 | self::reset_cached_keys( $cache_mode ); |
| 740 | } |
| 741 | } else { |
| 742 | $group = ( empty( $group ) ? 'pods_view' : $group ); |
| 743 | $key = ( empty( $key ) ? 'pods_view' : $key ); |
| 744 | |
| 745 | wp_cache_delete( $key, $group ); |
| 746 | |
| 747 | self::remove_cached_key( $cache_mode, $key, $group ); |
| 748 | } |
| 749 | } elseif ( 'option-cache' === $cache_mode ) { |
| 750 | do_action( "delete_transient_{$key}", $key ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 751 | |
| 752 | $group = ( empty( $group ) ? 'pods_option_cache' : $group ); |
| 753 | |
| 754 | if ( $external_object_cache ) { |
| 755 | $result = wp_cache_delete( $key, $group ); |
| 756 | |
| 757 | wp_cache_delete( '_timeout_' . $key, $group ); |
| 758 | } else { |
| 759 | $option_timeout = '_pods_option_timeout_' . $key; |
| 760 | $option = '_pods_option_' . $key; |
| 761 | |
| 762 | $result = delete_option( $option ); |
| 763 | |
| 764 | if ( $result ) { |
| 765 | delete_option( $option_timeout ); |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | self::remove_cached_key( $cache_mode, $key, $group ); |
| 770 | |
| 771 | if ( $result ) { |
| 772 | do_action( 'deleted_transient', $key ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 773 | } |
| 774 | } elseif ( 'static-cache' === $cache_mode ) { |
| 775 | $static_cache = pods_container( Static_Cache::class ); |
| 776 | |
| 777 | if ( $static_cache ) { |
| 778 | $group = ( empty( $group ) ? __CLASS__ : $group ); |
| 779 | |
| 780 | if ( true === $key ) { |
| 781 | $static_cache->flush( $group ); |
| 782 | |
| 783 | self::reset_cached_keys( $cache_mode, $group ); |
| 784 | } else { |
| 785 | $static_cache->delete( $key, $group ); |
| 786 | |
| 787 | self::remove_cached_key( $cache_mode, $key, $group ); |
| 788 | } |
| 789 | } |
| 790 | }//end if |
| 791 | |
| 792 | do_action( "pods_view_clear_{$cache_mode}", $original_key, $group ); |
| 793 | |
| 794 | return true; |
| 795 | } |
| 796 | |
| 797 | /** |
| 798 | * @static |
| 799 | * |
| 800 | * @param $_view |
| 801 | * @param null|array $_data |
| 802 | * @param bool $limited (optional) Whether to limit the view to only the theme directory, defaults to false |
| 803 | * |
| 804 | * @return bool|mixed|string|void |
| 805 | */ |
| 806 | public static function get_template_part( $_view, $_data = null, $limited = false ) { |
| 807 | |
| 808 | /* |
| 809 | To be reviewed later, should have more checks and restrictions like a whitelist etc. |
| 810 | |
| 811 | if ( 0 === strpos( $_view, 'http://' ) || 0 === strpos( $_view, 'https://' ) ) { |
| 812 | $_view = apply_filters( 'pods_view_url_include', $_view ); |
| 813 | |
| 814 | if ( empty( $_view ) || ( defined( 'PODS_REMOTE_VIEWS' ) && PODS_REMOTE_VIEWS ) ) |
| 815 | return ''; |
| 816 | |
| 817 | $response = wp_remote_get( $_view ); |
| 818 | |
| 819 | return wp_remote_retrieve_body( $response ); |
| 820 | } |
| 821 | */ |
| 822 | |
| 823 | $_view = self::locate_template( $_view, $limited ); |
| 824 | |
| 825 | if ( empty( $_view ) ) { |
| 826 | return $_view; |
| 827 | } |
| 828 | |
| 829 | if ( ! empty( $_data ) && is_array( $_data ) ) { |
| 830 | // Only expose array entries whose keys are valid PHP variable names as template variables (EXTR_SKIP prevents overwriting existing scope), to help prevent security issues. |
| 831 | $_safe_data = array_filter( |
| 832 | $_data, |
| 833 | static function ( $key ) { |
| 834 | return is_string( $key ) && preg_match( '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $key ); |
| 835 | }, |
| 836 | ARRAY_FILTER_USE_KEY |
| 837 | ); |
| 838 | |
| 839 | extract( $_safe_data, EXTR_SKIP ); |
| 840 | } |
| 841 | |
| 842 | ob_start(); |
| 843 | require $_view; |
| 844 | $output = ob_get_clean(); |
| 845 | |
| 846 | return $output; |
| 847 | } |
| 848 | |
| 849 | /** |
| 850 | * @static |
| 851 | * |
| 852 | * @param array|string $_view |
| 853 | * @param bool $limited (optional) Whether to limit the view to only the theme directory, defaults to false |
| 854 | * |
| 855 | * @return bool|mixed|string|void |
| 856 | */ |
| 857 | private static function locate_template( $_view, $limited = false ) { |
| 858 | if ( is_array( $_view ) ) { |
| 859 | $_views = []; |
| 860 | |
| 861 | if ( isset( $_view[0] ) && false === strpos( (string) $_view[0], '.php' ) ) { |
| 862 | $_view_count = count( $_view ); |
| 863 | |
| 864 | for ( $_view_x = $_view_count; 0 < $_view_x; $_view_x -- ) { |
| 865 | $_view_v = array_slice( $_view, 0, $_view_x ); |
| 866 | |
| 867 | $_views[] = implode( '-', $_view_v ) . '.php'; |
| 868 | } |
| 869 | } else { |
| 870 | $_views = $_view; |
| 871 | } |
| 872 | |
| 873 | $_view = false; |
| 874 | |
| 875 | foreach ( $_views as $_view_check ) { |
| 876 | $_view = self::locate_template( $_view_check ); |
| 877 | |
| 878 | if ( ! empty( $_view ) ) { |
| 879 | break; |
| 880 | } |
| 881 | } |
| 882 | |
| 883 | return $_view; |
| 884 | }//end if |
| 885 | |
| 886 | $paths_to_check = [ 'plugins', 'pods', 'theme' ]; |
| 887 | |
| 888 | if ( $limited ) { |
| 889 | $paths_to_check = [ 'theme' ]; |
| 890 | } |
| 891 | |
| 892 | // Is the view's file somewhere within the plugin directory tree? |
| 893 | // Note: we include PODS_DIR for the case of symlinks (see issue #2945). |
| 894 | $located = pods_validate_safe_path( $_view, $paths_to_check ); |
| 895 | |
| 896 | /** |
| 897 | * Allow filtering the validated view file path to use. |
| 898 | * |
| 899 | * @since unknown |
| 900 | * |
| 901 | * @param string|false $located The validated view file path to use, or false if it was not valid. |
| 902 | * @param string $_view The original view file path to use. |
| 903 | */ |
| 904 | $located = apply_filters( 'pods_view_locate_template', $located, $_view ); |
| 905 | |
| 906 | if ( ! $located ) { |
| 907 | return false; |
| 908 | } |
| 909 | |
| 910 | return $located; |
| 911 | |
| 912 | } |
| 913 | |
| 914 | /** |
| 915 | * Advanced $expires handling |
| 916 | * |
| 917 | * @param array|bool|int $expires |
| 918 | * @param string $cache_mode |
| 919 | * |
| 920 | * @return bool|int |
| 921 | * |
| 922 | * @since 2.7.0 |
| 923 | * @static |
| 924 | */ |
| 925 | public static function expires( $expires, $cache_mode = 'cache' ) { |
| 926 | |
| 927 | // Different $expires if user is anonymous or logged in or specific capability |
| 928 | if ( is_array( $expires ) ) { |
| 929 | if ( ( isset( $expires['anonymous'] ) || isset( $expires['user_with_access'] ) ) && isset( $expires['user'] ) ) { |
| 930 | if ( isset( $expires['user_with_access'] ) ) { |
| 931 | $expires = array( |
| 932 | pods_v( 'anonymous', $expires, false ), |
| 933 | pods_v( 'user', $expires, false ), |
| 934 | pods_v( 'user_with_access', $expires, false ), |
| 935 | pods_v( 'capability', $expires, null, null, true ), |
| 936 | ); |
| 937 | } elseif ( isset( $expires['anonymous'] ) ) { |
| 938 | $expires = array( |
| 939 | pods_v( 'anonymous', $expires, false ), |
| 940 | pods_v( 'user', $expires, false ), |
| 941 | pods_v( 'capability', $expires, null, null, true ), |
| 942 | ); |
| 943 | } |
| 944 | } else { |
| 945 | $expires = array_values( $expires ); |
| 946 | } |
| 947 | |
| 948 | if ( 4 === count( $expires ) ) { |
| 949 | if ( ! is_user_logged_in() ) { |
| 950 | $expires = pods_v( 0, $expires, false ); |
| 951 | } else { |
| 952 | $user_no_access = pods_v( 1, $expires, false ); |
| 953 | $user_with_access = pods_v( 2, $expires, false ); |
| 954 | $capability = pods_v( 3, $expires, null, true ); |
| 955 | |
| 956 | $expires = pods_var_user( $user_no_access, $user_with_access, $capability ); |
| 957 | } |
| 958 | } else { |
| 959 | $anon = pods_v( 0, $expires, false ); |
| 960 | $user = pods_v( 1, $expires, false ); |
| 961 | $capability = pods_v( 2, $expires, null, true ); |
| 962 | |
| 963 | $expires = pods_var_user( $anon, $user, $capability ); |
| 964 | } |
| 965 | }//end if |
| 966 | |
| 967 | if ( 'none' === $cache_mode ) { |
| 968 | $expires = false; |
| 969 | } elseif ( false !== $expires ) { |
| 970 | $expires = (int) $expires; |
| 971 | |
| 972 | if ( $expires < 1 ) { |
| 973 | $expires = 0; |
| 974 | } |
| 975 | } |
| 976 | |
| 977 | return $expires; |
| 978 | |
| 979 | } |
| 980 | |
| 981 | /** |
| 982 | * Determine whether the cache mode is valid. |
| 983 | * |
| 984 | * @since 2.9.14 |
| 985 | * |
| 986 | * @param string|mixed $cache_mode The cache mode. |
| 987 | * |
| 988 | * @return bool Whether the cache mode is valid. |
| 989 | */ |
| 990 | public static function is_cache_mode_valid( $cache_mode ) { |
| 991 | return ( |
| 992 | $cache_mode |
| 993 | && is_string( $cache_mode ) |
| 994 | && isset( self::$cache_modes[ $cache_mode ] ) |
| 995 | ); |
| 996 | } |
| 997 | |
| 998 | } |
| 999 |