Apcu::set()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware API: Caching provider storing data in PHP's APCu extension
4
 *
5
 * @link http://www.egroupware.org
6
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
7
 * @package api
8
 * @subpackage cache
9
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
10
 * @copyright (c) 2010-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
11
 * @version $Id$
12
 */
13
14
namespace EGroupware\Api\Cache;
15
16
/**
17
 * Caching provider storing data in PHP's APCu extension / shared memory.
18
 *
19
 * The provider concats all $keys with '::' to get a single string.
20
 *
21
 * This provider is used by default, if it is available or explicit enabled in your header.inc.php:
22
 * $GLOBALS['egw_info']['server']['cache_provider_instance'] = array('EGroupware\Api\Cache\Apc');
23
 * and optional also $GLOBALS['egw_info']['server']['cache_provider_tree'] (defaults to instance)
24
 *
25
 * APC(u) and CLI:
26
 * --------------
27
 * APC(u) is not enabled by default for CLI (apc.enable_cli), nor would it access same shared memory!
28
 * It makes no sense to fall back to files cache, as this is probably quite outdated,
29
 * if PHP via Webserver uses APC. Better to use no cache at all.
30
 * Api\Cache::get*() will return NULL for not found and Api\Cache::[un]set*()
31
 * false for not being able to (un)set anything.
32
 * It also does not make sense to report failure by throwing an Exception and filling
33
 * up cron logs.
34
 * --> if APC(u) is available for Webserver, we report availability for CLI too,
35
 *     but use no cache at all!
36
 */
37
class Apcu extends Base implements Provider
38
{
39
	/**
40
	 * Constructor, eg. opens the connection to the backend
41
	 *
42
	 * @throws Exception if connection to backend could not be established
43
	 * @param array $params eg. array('localhost'[,'localhost:11211',...])
44
	 */
45
	function __construct(array $params)
46
	{
47
		if (!function_exists('apcu_fetch'))	// apc >= 3.0
48
		{
49
			throw new Exception (__METHOD__.'('.array2string($params).") No function apcu_fetch()!");
50
		}
51
	}
52
53
	/**
54
	 * Check if APC is available for caching user data
55
	 *
56
	 * Default shared memory size of 32M, which is used only for user data in APCu.
57
	 * Unlike APC which shares the total memory with it's opcode cache 32M is ok
58
	 * for a small install.
59
	 *
60
	 * @return boolean true: apc available, false: not
61
	 */
62
	public static function available()
63
	{
64
		if (($available = (bool)ini_get('apc.enabled') && function_exists('apcu_fetch')))
65
		{
66
			$size = ini_get('apc.shm_size');
67
68
			switch(strtoupper(substr($size, -1)))
69
			{
70
				case 'G':
71
					$size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
72
				case 'M':
73
					$size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
74
				case 'K':
75
					$size *= 1024;
76
			}
77
			$size *= ini_get('apc.shm_segments');
78
79
			// only cache in APCu, if we have at least 32M available (default is 32M)
80
			$available = $size >= 33554432;
81
		}
82
		//error_log(__METHOD__."() size=$size returning ".array2string($available));
83
		return $available;
84
	}
85
86
	/**
87
	 * Stores some data in the cache, if it does NOT already exists there
88
	 *
89
	 * @param array $keys eg. array($level,$app,$location)
90
	 * @param mixed $data
91
	 * @param int $expiration =0
92
	 * @return boolean true on success, false on error, incl. key already exists in cache
93
	 */
94
	function add(array $keys,$data,$expiration=0)
95
	{
96
		return apcu_add(self::key($keys),$data,$expiration);
97
	}
98
99
	/**
100
	 * Stores some data in the cache
101
	 *
102
	 * @param array $keys eg. array($level,$app,$location)
103
	 * @param mixed $data
104
	 * @param int $expiration =0
105
	 * @return boolean true on success, false on error
106
	 */
107
	function set(array $keys,$data,$expiration=0)
108
	{
109
		return apcu_store(self::key($keys),$data,$expiration);
110
	}
111
112
	/**
113
	 * Get some data from the cache
114
	 *
115
	 * @param array $keys eg. array($level,$app,$location)
116
	 * @return mixed data stored or NULL if not found in cache
117
	 */
118
	function get(array $keys)
119
	{
120
		$success = null;
121
		$data = apcu_fetch($key=self::key($keys),$success);
122
123
		if (!$success)
124
		{
125
			//error_log(__METHOD__."(".array2string($keys).") key='$key' NOT found!");
126
			return null;
127
		}
128
		//error_log(__METHOD__."(".array2string($keys).") key='$key' found ".bytes(serialize($data))." bytes).");
129
		return $data;
130
	}
131
132
	/**
133
	 * Delete some data from the cache
134
	 *
135
	 * @param array $keys eg. array($level,$app,$location)
136
	 * @return boolean true on success, false on error (eg. $key not set)
137
	 */
138
	function delete(array $keys)
139
	{
140
		return apcu_delete(self::key($keys));
141
	}
142
143
	/**
144
	 * Delete all data under given keys
145
	 *
146
	 * If no keys are given whole APCu cache is cleared, which should allways
147
	 * work and can not run out of memory as the iterator sometimes does.
148
	 *
149
	 * @param array $keys eg. array($level,$app,$location) or array() to clear whole cache
150
	 * @return boolean true on success, false on error (eg. on iterator available)
151
	 */
152
	function flush(array $keys)
153
	{
154
		// do NOT try instanciating APCuIterator, if APCu is not enabled, as it gives a PHP Fatal Error
155
		if (!ini_get('apc.enabled') || php_sapi_name() === 'cli' && !ini_get('apc.cli_enabled'))
156
		{
157
			return false;
158
		}
159
		if (!$keys && function_exists('apcu_clear_cache'))
0 ignored issues
show
Bug Best Practice introduced by
The expression $keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
160
		{
161
			apcu_clear_cache();
162
163
			return true;
164
		}
165
		// APCu > 5 has APCUIterator
166
		if (class_exists('APCUIterator'))
167
		{
168
			$iterator = new \APCUIterator($preg='/^'.preg_quote(self::key($keys).'/'));
169
		}
170
		// APC >= 3.1.1, but also seems to be missing if apc is disabled eg. for cli
171
		elseif(class_exists('APCIterator'))
172
		{
173
			$iterator = new \APCIterator('user', $preg='/^'.preg_quote(self::key($keys).'/'));
174
		}
175
		else
176
		{
177
			if (function_exists('apcu_clear_cache')) apcu_clear_cache();
178
179
			return false;
180
		}
181
		foreach($iterator as $item)
182
		{
183
			//error_log(__METHOD__."(".array2string($keys).") preg='$preg': calling apcu_delete('$item[key]')");
184
			apcu_delete($item['key']);
185
		}
186
		return true;
187
	}
188
189
	/**
190
	 * Create a single key from $keys
191
	 *
192
	 * @param array $keys
193
	 * @return string
194
	 */
195
	private static function key(array $keys)
196
	{
197
		return implode('::',$keys);
198
	}
199
}
200