TYPO3 CMS  TYPO3_6-2
OpcodeCacheUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility;
3 
26 
31  static protected $supportedCaches = NULL;
32 
37  static protected $activeCaches = NULL;
38 
42  static protected function initialize() {
43  $apcVersion = phpversion('apc');
44  $xcVersion = phpversion('xcache');
45 
46  static::$supportedCaches = array(
47  // The ZendOpcache aka OPcache since PHP 5.5
48  // http://php.net/manual/de/book.opcache.php
49  'OPcache' => array(
50  'active' => extension_loaded('Zend OPcache') && ini_get('opcache.enable') === '1',
51  'version' => phpversion('Zend OPcache'),
52  'canReset' => TRUE, // opcache_reset() ... it seems that it doesn't reset for current run.
53  // From documentation this function exists since first version (7.0.0) but from Changelog
54  // this function exists since 7.0.2
55  // http://pecl.php.net/package-changelog.php?package=ZendOpcache&release=7.0.2
56  'canInvalidate' => function_exists('opcache_invalidate'),
57  'error' => FALSE,
58  'clearCallback' => function ($fileAbsPath) {
59  if ($fileAbsPath !== NULL && function_exists('opcache_invalidate')) {
60  opcache_invalidate($fileAbsPath);
61  } else {
62  opcache_reset();
63  }
64  }
65  ),
66 
67  // The Alternative PHP Cache aka APC
68  // http://www.php.net/manual/de/book.apc.php
69  'APC' => array(
70  // Currently APCu identifies itself both as "apcu" and "apc" (for compatibility) although it doesn't
71  // provide the APC-opcache functionality
72  'active' => extension_loaded('apc') && !extension_loaded('apcu') && ini_get('apc.enabled') === '1',
73  'version' => $apcVersion,
74  // apc_clear_cache() since APC 2.0.0 so default yes. In cli it do not clear the http cache.
75  'canReset' => TRUE,
76  'canInvalidate' => self::canApcInvalidate(),
77  // Versions lower then 3.1.7 are known as malfunction
78  'error' => $apcVersion && VersionNumberUtility::convertVersionNumberToInteger($apcVersion) < 3001007,
79  'clearCallback' => function ($fileAbsPath) {
80  if ($fileAbsPath !== NULL && OpcodeCacheUtility::getCanInvalidate('APC')) {
81  if (is_dir($fileAbsPath)) {
82  // APC can't flush whole directories directly by just givin' the directory path.
83  // Instead retrieve all matching cache entries using an ApcIterator.
84  $quotedPath = preg_quote(rtrim($fileAbsPath, '/') . '/', '/');
85  $iterator = new \APCIterator('file', '/^'. $quotedPath . '.*/');
86  apc_delete_file($iterator);
87  } else {
88  apc_delete_file($fileAbsPath);
89  }
90  } else {
91  // Clear whole cache at once.
92  apc_clear_cache('opcode');
93  }
94  }
95  ),
96 
97  // http://www.php.net/manual/de/book.wincache.php
98  'WinCache' => array(
99  'active' => extension_loaded('wincache') && ini_get('wincache.ocenabled') === '1'
100  && version_compare(phpversion('wincache'), '2.0.0.0', '<'),
101  'version' => phpversion('wincache'),
102  'canReset' => FALSE,
103  'canInvalidate' => TRUE, // wincache_refresh_if_changed()
104  'error' => FALSE,
105  'clearCallback' => function ($fileAbsPath) {
106  if ($fileAbsPath !== NULL) {
107  wincache_refresh_if_changed(array($fileAbsPath));
108  } else {
109  // No argument means refreshing all.
110  wincache_refresh_if_changed();
111  }
112  }
113  ),
114 
115  // http://xcache.lighttpd.net/
116  'XCache' => array(
117  'active' => extension_loaded('xcache'),
118  'version' => $xcVersion,
119  'canReset' => TRUE, // xcache_clear_cache()
120  'canInvalidate' => FALSE,
121  'error' => FALSE,
122  // API changed with XCache 3.0.0
123  // http://xcache.lighttpd.net/wiki/XcacheApi?action=diff&version=23&old_version=22
124  'clearCallback' => (
125  $xcVersion && VersionNumberUtility::convertVersionNumberToInteger($xcVersion) < 3000000 ?
126  function ($fileAbsPath) {
127  if (!ini_get('xcache.admin.enable_auth')) {
128  xcache_clear_cache(XC_TYPE_PHP, 0);
129  }
130  }
131  :
132  function ($fileAbsPath) {
133  if (!ini_get('xcache.admin.enable_auth')) {
134  xcache_clear_cache(XC_TYPE_PHP);
135  }
136  }
137  )
138  ),
139 
140  // https://github.com/eaccelerator/eaccelerator
141  //
142  // @see https://github.com/eaccelerator/eaccelerator/blob/master/doc/php/info.php
143  // Only possible if we are in eaccelerator.admin_allowed_path and we can only remove data
144  // "that isn't used in the current requests"
145  'eAccelerator' => array(
146  'active' => extension_loaded('eAccelerator'),
147  'version' => phpversion('eaccelerator'),
148  'canReset' => FALSE,
149  'canInvalidate' => FALSE,
150  'error' => TRUE, // eAccelerator is more or less out of date and not functional for what we need.
151  'clearCallback' => function ($fileAbsPath) {
152  eaccelerator_clear();
153  }
154  ),
155 
156  // https://github.com/zendtech/ZendOptimizerPlus
157  // http://files.zend.com/help/Zend-Server/zend-server.htm#zendoptimizerplus.html
158  'ZendOptimizerPlus' => array(
159  'active' => extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable') === '1',
160  'version' => phpversion('Zend Optimizer+'),
161  'canReset' => TRUE, // accelerator_reset()
162  'canInvalidate' => FALSE,
163  'error' => FALSE,
164  'clearCallback' => function ($fileAbsPath) {
165  accelerator_reset();
166  }
167  ),
168  );
169 
170  static::$activeCaches = array();
171  // Cache the active ones
172  foreach (static::$supportedCaches as $opcodeCache => $properties) {
173  if ($properties['active']) {
174  static::$activeCaches[$opcodeCache] = $properties;
175  }
176  }
177  }
178 
187  static public function getCanInvalidate($system) {
188  return isset(static::$supportedCaches[$system])
189  ? static::$supportedCaches[$system]['canInvalidate']
190  : FALSE;
191  }
192 
200  static public function clearAllActive($fileAbsPath = NULL) {
201  foreach (static::getAllActive() as $properties) {
202  $callback = $properties['clearCallback'];
203  $callback($fileAbsPath);
204  }
205  }
206 
212  static public function getAllActive() {
213  if (static::$activeCaches === NULL) {
214  static::initialize();
215  }
216  return static::$activeCaches;
217  }
218 
225  static public function canApcInvalidate() {
226  // apc_delete_file() should exists since APC 3.1.1 but you never know so default is no
227  $canInvalidate = FALSE;
228 
229  if (function_exists('apc_delete_file')) {
230  // Deleting files from cache depends on generating the cache key.
231  // This cache key generation depends on unnecessary configuration options
232  // http://git.php.net/?p=pecl/caching/apc.git;a=blob;f=apc_cache.c;h=d15cf8c1b4b9d09b9bac75b16c062c8b40458dda;hb=HEAD#l931
233 
234  // If stat=0 then canonicalized path may be used
235  $stat = (int)ini_get('apc.stat');
236  // If canonicalize (default = 1) then file_update_protection isn't checked
237  $canonicalize = (int)ini_get('apc.canonicalize');
238  // If file file_update_protection is checked, then we will fail, 'cause we generated the file and then try to
239  // remove it. But the file is not older than file_update_protection and therefore hash generation will stop with error.
240  $protection = (int)ini_get('apc.file_update_protection');
241 
242  if ($protection === 0 || ($stat === 0 && $canonicalize === 1)) {
243  $canInvalidate = TRUE;
244  }
245  }
246 
247  return $canInvalidate;
248  }
249 }