Completed
Push — master ( 13a71b...6fac6b )
by David
03:46
created

Ttl_Cache::get_path()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Wordlift\Cache;
4
5
use Wordlift_Log_Service;
6
7
/**
8
 * Define an a time lived cache.
9
 *
10
 * Cache has a ttl set by default to 900 seconds. Cached responses are stored in the temp
11
 * folder returned by WordPress' {@link get_temp_dir} function.
12
 *
13
 * Currently the class doesn't cleanup stale cache files.
14
 *
15
 * @since 3.21.2
16
 */
17
// @@todo: add a hook to clear the cached files now and then.
18
class Ttl_Cache {
19
20
	/**
21
	 * The cache name.
22
	 *
23
	 * @var string $name The cache name.
24
	 * @access private
25
	 * @since 3.21.2
26
	 */
27
	private $name;
28
29
	/**
30
	 * The TTL of cached responses in seconds.
31
	 *
32
	 * @var int $ttl The TTL in seconds.
33
	 * @access private
34
	 * @since 3.21.2
35
	 */
36
	private $ttl;
37
38
	/**
39
	 * The cache dir where the cached data is written.
40
	 *
41
	 * @since 3.21.2
42
	 * @access private
43
	 * @var string $cache_dir The cache dir where the cached responses are written.
44
	 */
45
	private $cache_dir;
46
47
	/**
48
	 * A {@link Wordlift_Log_Service} instance.
49
	 *
50
	 * @var Wordlift_Log_Service $log A {@link Wordlift_Log_Service} instance.
51
	 * @access private
52
	 * @since 3.21.2
53
	 */
54
	private $log;
55
56
	/**
57
	 * @var array
58
	 */
59
	private static $caches = array();
60
61
	/**
62
	 * Create a {@link Ttl_Cache} with the specified TTL, default 900 secs.
63
	 *
64
	 * @param string $name The cache name.
65
	 * @param int    $ttl The cache TTL, default 900 secs.
66
	 *
67
	 * @since 3.21.2
68
	 */
69
	public function __construct( $name, $ttl = 900 ) {
70
71
		$this->log = Wordlift_Log_Service::get_logger( get_class() );
72
73
		$this->name = $name;
74
		$this->ttl  = $ttl;
75
76
		// Get the temp dir and add the directory separator if missing.
77
		$temp_dir = get_temp_dir();
78
		if ( DIRECTORY_SEPARATOR !== substr( $temp_dir, - strlen( DIRECTORY_SEPARATOR ) ) ) {
79
			$temp_dir .= DIRECTORY_SEPARATOR;
80
		}
81
		$this->cache_dir = $temp_dir . 'wl.cache' . DIRECTORY_SEPARATOR . md5( $name );
82
83
		$this->log->trace( "Creating the cache folder {$this->cache_dir}..." );
84
		wp_mkdir_p( $this->cache_dir );
85
86
		self::$caches[ $name ] = $this;
87
88
	}
89
90
	/**
91
	 * Get the cached data for the specified key.
92
	 *
93
	 * @param mixed $key A serializable key.
94
	 *
95
	 * @return mixed|null
96
	 * @since 3.21.2
97
	 */
98
	public function get( $key ) {
99
100
		$filename = $this->get_filename( $key );
101
102
		// If the cache file exists and it's not too old, then return it.
103
		if ( file_exists( $filename ) && $this->ttl >= time() - filemtime( $filename ) ) {
104
			$this->log->trace( "Cache HIT.\n" );
105
106
			return json_decode( file_get_contents( $filename ), true );
107
		}
108
109
		$this->log->trace( "Cache MISS, filename $filename.\n" );
110
111
		return null;
112
	}
113
114
	public function put( $key, $data ) {
115
116
		$filename = $this->get_filename( $key );
117
118
		// Cache.
119
		@unlink( $filename );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
120
		@file_put_contents( $filename, wp_json_encode( $data ) );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
121
122
	}
123
124
	public function flush() {
125
126
		$files = glob( $this->cache_dir . DIRECTORY_SEPARATOR . '*' );
127
		foreach ( $files as $file ) { // iterate files
128
			if ( is_file( $file ) ) {
129
				@unlink( $file );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
130
			}
131
		}
132
133
	}
134
135
	public static function flush_all() {
136
137
		/** @var Ttl_Cache $cache */
138
		foreach ( self::$caches as $cache ) {
139
			$cache->flush();
140
		}
141
142
	}
143
144
	/**
145
	 * Get the full path for the given `$hash`. The file is not checked for its existence.
146
	 *
147
	 * @param string $hash A file hash.
148
	 *
149
	 * @return string The full path to the file.
150
	 * @since 3.21.2
151
	 */
152
	private function get_path( $hash ) {
153
154
		return $this->cache_dir . DIRECTORY_SEPARATOR . $hash;
155
	}
156
157
	private function get_filename( $key ) {
158
159
		// Create a hash and a path to the cache file.
160
		$hash     = md5( serialize( $key ) );
161
		$filename = $this->get_path( $hash );
162
163
		return $filename;
164
	}
165
166
}
167