Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
63.83% covered (warning)
63.83%
30 / 47
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Transient
63.83% covered (warning)
63.83%
30 / 47
16.67% covered (danger)
16.67%
1 / 6
33.33
0.00% covered (danger)
0.00%
0 / 1
 should_use_transients
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 get
85.71% covered (warning)
85.71%
12 / 14
0.00% covered (danger)
0.00%
0 / 1
6.10
 set
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
4.07
 delete
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 delete_option_cache
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __call
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2namespace NewfoldLabs\WP\Module\Data\Helpers;
3
4/**
5 * Custom Transient class to handle an Options API based fallback
6 */
7class Transient {
8
9    /**
10     * Whether to use transients to store temporary data
11     *
12     * If the site has an object-cache.php drop-in, then we can't reliably
13     * use the transients API. We'll try to fall back to the options API.
14     */
15    protected static function should_use_transients(): bool {
16        require_once constant( 'ABSPATH' ) . '/wp-admin/includes/plugin.php';
17        return ! array_key_exists( 'object-cache.php', get_dropins() )
18            || 'atomic' === \NewfoldLabs\WP\Context\getContext( 'platform' ); // Bluehost Cloud.
19    }
20
21    /**
22     * Wrapper for get_transient() with Options API fallback
23     *
24     * @see \get_transient()
25     * @see \get_option()
26     * @see \delete_option()
27     *
28     * @param string $key The key of the transient to retrieve
29     * @return mixed The value of the transient
30     */
31    public static function get( string $key ) {
32        if ( self::should_use_transients() ) {
33            return \get_transient( $key );
34        }
35
36        /**
37         * Implement the filters as used in {@see get_transient()}.
38         */
39        $pre = apply_filters( "pre_transient_{$key}", false, $key );
40        if ( false !== $pre ) {
41            return $pre;
42        }
43
44        /**
45         * The saved value and the Unix time it expires at.
46         *
47         * @var array{value:mixed, expires_at:int} $data
48         */
49        $data = \get_option( $key );
50        if ( is_array( $data ) && isset( $data['expires_at'], $data['value'] ) ) {
51            if ( $data['expires_at'] > time() ) {
52                $value = $data['value'];
53            } else {
54                \delete_option( $key );
55                self::delete_option_cache( $key );
56                $value = false;
57            }
58        } else {
59            /**
60             * Set $value to false if $data is not a valid array.
61             * This is to prevent PHP notices when trying to access $data['expires_at'].
62             */
63            $value = false;
64        }
65
66        /**
67         * Implement the filters as used in {@see get_transient()}.
68         */
69        return apply_filters( "transient_{$key}", $value, $key );
70    }
71
72    /**
73     * Wrapper for set_transient() with Options API fallback
74     *
75     * @see \set_transient()
76     * @see \update_option()
77     *
78     * @param string  $key        Key to use for storing the transient
79     * @param mixed   $value      Value to be saved
80     * @param integer $expires_in Optional expiration time in seconds from now. Default is 1 hour
81     *
82     * @return bool Whether the value was changed
83     */
84    public static function set( string $key, $value, int $expires_in = 3600 ): bool {
85        if ( self::should_use_transients() ) {
86            return \set_transient( $key, $value, $expires_in );
87        }
88
89        /**
90         * Implement the filters as used in {@see set_transient()}.
91         */
92        $value      = apply_filters( "pre_set_transient_{$key}", $value, $expires_in, $key );
93        $expires_in = apply_filters( "expiration_of_transient_{$key}", $expires_in, $value, $key );
94
95        $data = array(
96            'value'      => $value,
97            'expires_at' => $expires_in + time(),
98        );
99
100        self::delete_option_cache( $key );
101
102        $result = \update_option( $key, $data, false );
103
104        if ( ! $result ) {
105            \delete_option( $key );
106            self::delete_option_cache( $key );
107            $result = \add_option( $key, $data, '', false );
108        }
109
110        if ( $result ) {
111            do_action( "set_transient_{$key}", $value, $expires_in, $key );
112            do_action( 'setted_transient', $key, $value, $expires_in );
113        }
114
115        return $result;
116    }
117
118    /**
119     * Wrapper for delete_transient() with Options API fallback
120     *
121     * @see \delete_transient()
122     * @see \delete_option()
123     *
124     * @param string $key The key of the transient/option to delete
125     * @return bool Whether the value was deleted
126     */
127    public static function delete( $key ): bool {
128        if ( self::should_use_transients() ) {
129            return \delete_transient( $key );
130        }
131
132        /**
133         * Implement the filters as used in {@see set_transient()}.
134         *
135         * @param string $key Transient name.
136         */
137        do_action( "delete_transient_{$key}", $key );
138
139        $result = \delete_option( $key );
140
141        self::delete_option_cache( $key );
142
143        if ( $result ) {
144
145            /**
146             * Implement the filters as used in {@see set_transient()}.
147             *
148             * @param string $transient Deleted transient name.
149             */
150            do_action( 'deleted_transient', $key );
151        }
152
153        return $result;
154    }
155
156    /**
157     * Clear a cached option value from the object cache.
158     *
159     * Object-cache drop-ins can serve stale option values unless the options
160     * cache group is invalidated when using the options-table transient fallback.
161     *
162     * @param string $key Option name.
163     */
164    protected static function delete_option_cache( string $key ): void {
165        \wp_cache_delete( $key, 'options' );
166    }
167
168    /**
169     * Make the static functions callable as instance methods.
170     *
171     * @param string $name The function name being called.
172     * @param array  $arguments The arguments passed to that function.
173     *
174     * @return mixed
175     * @throws \BadMethodCallException If the method does not exist.
176     */
177    public function __call( $name, $arguments ) {
178        if ( ! method_exists( __CLASS__, $name ) ) {
179            throw new \BadMethodCallException( 'Method ' . esc_html( $name ) . ' does not exist' );
180        }
181        return self::$name( ...$arguments );
182    }
183}