Frodik
Frodik

Reputation: 15455

Is there any compatible memory cache for PHP 7?

I would like to use PHP 7. However there seems to be no option for key-value memory caching.

APC is discontinued.

XCache will not be available for PHP 7 for some time.

Is there an available alternative for PHP 7?

Or is there a way to use PHP7's Opcache to implement key-value memory caching?

Upvotes: 36

Views: 41807

Answers (4)

DevWL
DevWL

Reputation: 18840

PHP 7 cache / accelerator list

Listo of dead/outdated PHP accelerators: XCache, APC, memoize, ZendOpcache, chdb, hidef (they are not supporting PHP 7)

We can find a list of PHP Accelerators on PECL website, but as i menthion some of them are discontinued or not up to date.

Currently developed (with support for PHP 7.3) are :

You will find all installation instruction in a downloaded tgz/zip file.

APCu WINDOWS USERS: Download APCu and APCu_bc DLL files that matches your system specification x64 (64 bits) OR x86 (32 bits windows) select TS or UTS version and of course the right PHP version. Paste .DLL into your php/ext directory You can determine thread mode by looking at your php directory. Find at DLL file name (for example: php7ts.dll). Notice the TS or UTS in file name.

php -v will show you your current PHP CLI installation version. Just make sure your PHP version is same with the one you are using in your server. If not change update the windows environment path for PHP.

If you have difficulties read this: How to install apcu in windows https://kapilpatel84.wordpress.com/2016/06/15/install-xdebug-apcu-on-windows-xampp-for-php7/

FOR XAMPP:

1) Down load compatible APCu with following link http://pecl.php.net/package/apcu

2) APCu need backword compatabily with APC so you have to download it with following link. http://pecl.php.net/package/apcu_bc

3) Extact DDL file and move DDL file named : php_apc.dll and php_apcu.dll and copy that files to your PHP ext Directory. i.e C:\xampp\php\ext

4) Open php.ini file and copy the following code at the bottom of the file

[apcu]
extension="C:\xampp\php\ext\php_apcu.dll"
apc.enabled=1
apc.shm_size=32M
apc.ttl=7200
apc.enable_cli=1
apc.serializer=php
extension="C:\xampp\php\ext\php_apc.dll"

When you unzip the file. Copy DLL file to your PHP extenstion folder np: .../php/ext. and configure php.ini (conf instruction is included in the INSTALL text file).

Symfony 4

PS. If by any chance you using Symfony framework dont forget to anable APCu in config>packages>cache.yaml

framework:
    cache:
        app: cache.adapter.apcu

Use builtin server with:

php bin/console server:run

Upvotes: 5

El Yobo
El Yobo

Reputation: 14946

Another approach (which I haven't tried, but sounds very interesting) is to take advantage of the opcache as a key value cache. This graphiq post contains more details but no real code unfortunately (and note the comments from Kerry Schwab).

The gist of it is to ensure that the opcache is enabled and has enough memory allocated for the data that you need to cache and then something along the lines of the following (lifted from the article, check it out in full). Cache expiration (aside from manual deletion) would need to be handled too, but wouldn't be hard to add (e.g. wrap your data in a containing object with an expiry time and check it in your cache_get, deleting and ignoring the record if it's expired).

function cache_set($key, $val) {
   $val = var_export($val, true);
   // HHVM fails at __set_state, so just use object cast for now
   $val = str_replace('stdClass::__set_state', '(object)', $val);
   // Write to temp file first to ensure atomicity
   $tmp = "/tmp/$key." . uniqid('', true) . '.tmp';
   file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);
   rename($tmp, "/tmp/$key");
}

function cache_get($key) {
    @include "/tmp/$key";
    return isset($val) ? $val : false;
}

Because of the opcache this functions as an in memory cache, but it avoids the overhead of serialization and deserialization. I guess that the cache_set should also call opcache_invalidate when writing (and in the cache_delete function which doesn't exist in their examples) but otherwise it seems sound for a cache which doesn't need to be shared between servers.

Edit: An example implementation with cache expiry (only accurate to a second, could use microtime(true) if more accuracy is required) below. Minimal testing actually done, and I dropped the HHVM specific replacement, so YMMV. Suggestions for improvements welcome.

class Cache {                                                                   
    private $root;                                                              
    private $compile;                                                           
    private $ttl;                                                               

    public function __construct($options = []) {                                
        $this->options = array_merge(                                           
            array(                                                              
                'root' => sys_get_temp_dir(),                                   
                'ttl'  => false,                                                
            ),                                                                  
            $options                                                            
        );                                                                      
        $this->root = $this->options['root'];                                   
        $this->ttl = $this->options['ttl'];                                     
    }                                                                           

    public function set($key, $val, $ttl = null) {                              
        $ttl = $ttl === null ? $this->ttl : $ttl;                               
        $file = md5($key);                                                      
        $val = var_export(array(                                                
            'expiry' => $ttl ? time() + $ttl : false,                           
            'data' => $val,                                                     
        ), true);                                                               

        // Write to temp file first to ensure atomicity                         
        $tmp = $this->root . '/' . $file . '.' . uniqid('', true) . '.tmp';     
        file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);         

        $dest = $this->root . '/' . $file;                                      
        rename($tmp, $dest);                                                    
        opcache_invalidate($dest);                                              
    }                                                                           

    public function get($key) {                                                 
        @include $this->root . '/' . md5($key);                                 

        // Not found                                                            
        if (!isset($val)) return null;                                          

        // Found and not expired                                                
        if (!$val['expiry'] || $val['expiry'] > time()) return $val['data'];    

        // Expired, clean up                                                    
        $this->remove($key);                                                    
    }                                                                           

    public function remove($key) {                                              
        $dest = $this->root . '/' . md5($key);                                  
        if (@unlink($dest)) {                                                   
            // Invalidate cache if successfully written                         
            opcache_invalidate($dest);                                          
        }                                                                       
    }                                                                           
}      

Upvotes: 8

Matt Prelude
Matt Prelude

Reputation: 912

I'd suggest using Memcached, especially if you're concerned about performance.

Whilst you are correct that APC(u) is a lot faster than Memcache, you're not taking into the account that by the time you're worrying about these metrics, you will be running across multiple servers and APC(u) cannot be shared across nodes.

You can use a single Memcache instance or cluster to serve as many application servers as you want. Scalability is a greater concern in modern application development than "how much performance can I squeeze out of one server?"

Having said that, your alternative is APCu, which has all of the functionality that you're used to from APC. It is marked as stable with PHP7 but I wouldn't recommend this because of its single-node nature & inability to work correctly with fastcgi.

Upvotes: 39

Alister Bulman
Alister Bulman

Reputation: 35149

APCU is literally APC without the code caching (they took the APC code, removed the byte-code cache and released it as APCU). It's a drop-in replacement. Exactly as APC's user-cache, it keeps the data in the same process as the PHP runtime, and so using the value is like much like retrieving an ordinary variable, hence the speed.

Upvotes: 20

Related Questions