2017-12-20 4 views
1

Ich möchte PHP OpCache als Userland Cache (wie APCu, Redis, Memcache) als Fallback verwenden, wo bessere Caching-Lösungen nicht verfügbar sind.PHP Userland Cache mit OpCache (funktioniert nicht wie erwartet)

Die Idee ist, speichern Sie die Daten in php-Dateien zur Laufzeit cache-Dateien gespeichert und lesen Sie die Daten mit include. Auf diese Weise sollte OpCache die kompilierte Datei im Speicher zwischenspeichern und das Ergebnis ist ein Speichercache.

<?php 

/** 
* Simple php cache using php generated files and opcache 
*/ 
class DiskCache { 

    const DEFAULT_TTL = 3600; 

    /** 
    * @var callable 
    */ 
    private static $emptyErrorHandler; 

    /** 
    * @var string 
    */ 
    protected $cacheDir; 

    /** 
    * @var int 
    */ 
    protected $defaultTtl; 


    /** 
    * Constructor 
    * @param string $cacheDir where to store cache files 
    * @param integer $ttl  time to live 
    */ 
    public function __construct($cacheDir = null, $ttl = self::DEFAULT_TTL) { 

     if(empty($cacheDir)){ 
      $cacheDir = sys_get_temp_dir(); 
     } 

     $cacheDir = realpath(rtrim($cacheDir, DIRECTORY_SEPARATOR)); 

     if(!is_dir($cacheDir)) { 
      throw new InvalidArgumentException('Provided cache dir is not a directory'); 
     } 

     if(!(is_readable($cacheDir) && is_writable($cacheDir))) { 
      throw new InvalidArgumentException('Provided cache dir is not writable and readable'); 
     } 

     $this->cacheDir = $cacheDir; 
     $this->defaultTtl = (int) $ttl; 

     self::$emptyErrorHandler = function(){}; 
    } 

    /** 
    * Read cache 
    * @param string $key the key 
    * @return mixed|false cached data 
    */ 
    public function read($key) { 

     $fileName = $this->getCacheFilename($key); 

     set_error_handler(self::$emptyErrorHandler); 

     $cached = include $fileName; 

     restore_error_handler(); 

     if($cached && isset($cached['timestamp'], $cached['ttl'], $cached['data'])) { 
      if((time() - $cached['timestamp']) < $cached['ttl']){ 
       return $cached['data']; 
      } 
     } 

     if($cached) { 
      $this->delete($key); 
     } 

     return false; 
    } 

    /** 
    * Write cache 
    * @param string $key the key 
    * @param mixed $data the data 
    * @param integer $ttl time to live 
    * @return boolean 
    */ 
    public function write($key, $data, $ttl = null) { 

     $ttl = $ttl > 0 ? (int) $ttl : $this->defaultTtl; 
     $fileName = $this->getCacheFilename($key); 
     $code = null; 
     $result = false; 

     $value = array(
      'timestamp' => time(), 
      'ttl'  => $ttl, 
      'data'  => $data 
     ); 

     if (is_object($data) && method_exists($data, '__set_state')) { 
      $value = var_export($value, true); 
      $code = sprintf('<?php return %s;', $value); 
     } else { 
      $value = var_export(serialize($value), true); 
      $code = sprintf('<?php return unserialize(%s);', $value); 
     } 

     if($code){ 
      $result = @file_put_contents($fileName, $code, LOCK_EX); 
     } 

     return (boolean) $result; 
    } 

    /** 
    * Delete cache 
    * @param string $key 
    * @return boolean 
    */ 
    public function delete($key) { 
     $fileName = $this->getCacheFilename($key); 
     return @unlink($fileName); 
    } 

    /** 
    * Return the cache filename 
    * @param string $key 
    * @throws InvalidArgumentException 
    * @return string 
    */ 
    public function getCacheFilename($key){ 
     if(empty($key)) { 
      throw new InvalidArgumentException('key is empty'); 
     } 
     return $this->cacheDir . DIRECTORY_SEPARATOR . md5($key). '.php'; 
    } 
} 

i auf diese Weise getestet haben, in einer test.php Seite:

<?php 
$cache = new DiskCache(__DIR__); 
echo PHP_EOL; 
var_dump($cache->write('test', array('a', 'b', 'c'))); 
echo PHP_EOL; 
var_dump($cache->read('test')); 
echo PHP_EOL; 
print_r(opcache_get_status()); 

dies ist die Ausgabe:

bool(true) 

array(3) { 
    [0]=> 
    string(1) "a" 
    [1]=> 
    string(1) "b" 
    [2]=> 
    string(1) "c" 
} 

Array 
(
    [opcache_enabled] => 1 
    [cache_full] => 
    [restart_pending] => 
    [restart_in_progress] => 
    [memory_usage] => Array 
     (
      [used_memory] => 123832 
      [free_memory] => 66748632 
      [wasted_memory] => 236400 
      [current_wasted_percentage] => 0.35226345062256 
     ) 

    [opcache_statistics] => Array 
     (
      [num_cached_scripts] => 1 
      [num_cached_keys] => 2 
      [max_cached_keys] => 3907 
      [hits] => 17 
      [start_time] => 1513796280 
      [last_restart_time] => 0 
      [oom_restarts] => 0 
      [hash_restarts] => 0 
      [manual_restarts] => 0 
      [misses] => 190 
      [blacklist_misses] => 0 
      [blacklist_miss_ratio] => 0 
      [opcache_hit_rate] => 8.2125603864734 
     ) 

    [scripts] => Array 
     (
      [C:\DevEnv\htdocs\test.php] => Array 
       (
        [full_path] => C:\DevEnv\htdocs\test.php 
        [hits] => 1 
        [memory_consumption] => 12704 
        [last_used] => Wed Dec 20 20:49:08 2017 
        [last_used_timestamp] => 1513799348 
        [timestamp] => 1513799344 
       ) 

     ) 

) 

OpCache scheinen nicht die PHP-Dateien in den Cache erstellt auf Laufzeit. Der einzige, der Cache-Datei ist test.php finden Sie unter:

[scripts] => Array 
      (
       [C:\DevEnv\htdocs\test.php] => Array(..) 
      ) 

in php.ini opcache ist

aktiviert
[opcache] 
zend_extension=C:\DevEnv\PHP\5.6.24\ext\php_opcache.dll 
; Determines if Zend OPCache is enabled 
opcache.enable=1 

; Determines if Zend OPCache is enabled for the CLI version of PHP 
opcache.enable_cli=0 

; The OPcache shared memory storage size. 
;opcache.memory_consumption=64 

; The amount of memory for interned strings in Mbytes. 
;opcache.interned_strings_buffer=4 

; The maximum number of keys (scripts) in the OPcache hash table. 
; Only numbers between 200 and 100000 are allowed. 
;opcache.max_accelerated_files=2000 

; The maximum percentage of "wasted" memory until a restart is scheduled. 
;opcache.max_wasted_percentage=5 

; When this directive is enabled, the OPcache appends the current working 
; directory to the script key, thus eliminating possible collisions between 
; files with the same name (basename). Disabling the directive improves 
; performance, but may break existing applications. 
opcache.use_cwd=1 

; When disabled, you must reset the OPcache manually or restart the 
; webserver for changes to the filesystem to take effect. 
opcache.validate_timestamps=1 

; How often (in seconds) to check file timestamps for changes to the shared 
; memory storage allocation. ("1" means validate once per second, but only 
; once per request. "0" means always validate) 
opcache.revalidate_freq=1 

; Enables or disables file search in include_path optimization 
;opcache.revalidate_path=0 

; If disabled, all PHPDoc comments are dropped from the code to reduce the 
; size of the optimized code. 
;opcache.save_comments=1 

; If disabled, PHPDoc comments are not loaded from SHM, so "Doc Comments" 
; may be always stored (save_comments=1), but not loaded by applications 
; that don't need them anyway. 
;opcache.load_comments=1 

; If enabled, a fast shutdown sequence is used for the accelerated code 
;opcache.fast_shutdown=0 

; Allow file existence override (file_exists, etc.) performance feature. 
;opcache.enable_file_override=0 

; A bitmask, where each bit enables or disables the appropriate OPcache 
; passes 
;opcache.optimization_level=0xffffffff 

;opcache.inherited_hack=1 
;opcache.dups_fix=0 

; The location of the OPcache blacklist file (wildcards allowed). 
; Each OPcache blacklist file is a text file that holds the names of files 
; that should not be accelerated. The file format is to add each filename 
; to a new line. The filename may be a full path or just a file prefix 
; (i.e., /var/www/x blacklists all the files and directories in /var/www 
; that start with 'x'). Line starting with a ; are ignored (comments). 
;opcache.blacklist_filename= 

; Allows exclusion of large files from being cached. By default all files 
; are cached. 
;opcache.max_file_size=0 

; Check the cache checksum each N requests. 
; The default value of "0" means that the checks are disabled. 
;opcache.consistency_checks=0 

; How long to wait (in seconds) for a scheduled restart to begin if the cache 
; is not being accessed. 
;opcache.force_restart_timeout=180 

; OPcache error_log file name. Empty string assumes "stderr". 
;opcache.error_log= 

; All OPcache errors go to the Web server log. 
; By default, only fatal errors (level 0) or errors (level 1) are logged. 
; You can also enable warnings (level 2), info messages (level 3) or 
; debug messages (level 4). 
;opcache.log_verbosity_level=1 

; Preferred Shared Memory back-end. Leave empty and let the system decide. 
;opcache.preferred_memory_model= 

; Protect the shared memory from unexpected writing during script execution. 
; Useful for internal debugging only. 
;opcache.protect_memory=0 

, was ich falsch mache?

Antwort

2

Leider glaube ich nicht, dass das funktionieren wird. Es gibt einen Grund, warum APCu existiert.

Der PHP-Opcode-Cache verwendet Dateizeitstempel, um festzustellen, ob eine Datei seit dem Zwischenspeichern geändert wurde, und diese Zeitstempel haben nur eine Granularität von 1 Sekunde. Wenn eine Datei mehrmals innerhalb einer Sekunde geändert wird, werden die Änderungen verpasst.

Verwandte Themen