Efficient PHP Redis caching technology, refer to the next steps

Keywords: PHP Redis github Database

Have you thought about how PHP can use redis as a cache?

  1. Front and background modules share the Model layer;
  2. However, not every Model class can be cached, which is a waste of Redis resources;
  3. The front and back modules are free to decide whether to read data from the database or from the cache.
  4. No redundant code;
  5. Easy to use.
    Here we first show the final results achieved.

Step through Github for the final code and usage instructions: https://github.com/yeszao/php-redis-cache.

Install usage commands now:

$ composer install yeszao/cache


You can use it with a simple configuration, see Github's README instructions.

1 Final result

Suppose that in the MVC framework, the model layer has a Book class and a getById method, as follows:

class Book
    public function getById($id)
        return $id;


With the addition of caching technology, neither the way the original method was called nor the data structure returned should be changed.

So we hope that the end result should be as follows:

1 (new Book)->getById(100);           // The original, non-cached, or original way of calling is typically to read data from the database.
2 (new Book)->getByIdCache(100);      // Using cached invocation, the cache key name is: app_models_book:getbyid: + md5(parameter list)
3 (new Book)->getByIdClear(100);      // Delete this cache
4 (new Book)->getByIdFlush();         // delete getById() Method corresponds to all caches, that is, delete app_models_book:getbyid:*. This method does not require parameters.


In this way, we can clearly understand what we are doing, and at the same time know the source function of the data, and the way they are referenced is completely unified, which is called "three arrows with one arrow".

In fact, it is easy to implement, that is, to use the magic method of PHP u call() method.

2 u call() method

Here is a brief explanation of the u call method.

In PHP, when we access a class method that does not exist, the u call() method of that class is called.

(PHP will directly error if class methods do not exist and u call() methods are not written)

Suppose we have a Book class:

 1 class Book
 2 {
 3     public function __call($name, $arguments)
 4     {
 5         echo 'class Book No method exists', $name, PHP_EOL;
 6     }
 8     public function getById($id)
 9     {
10         echo 'My ID yes', $id, PHP_EOL;
11     }
12 }


When calling the existing getById(50) method, the program prints: My ID is 50.

If a nonexistent getAge() method is called, the program executes inside the u call() method of class A, which prints: the getAge method does not exist for class Book.

This is how u call works.

3 Implementation Details

Next, we take advantage of this feature of the u call() method to implement the caching strategy.

From the example above, we can see that when the u call() method is called, two parameters are passed in.

Name: the method name you want to call arguments: parameter list
So we can write about the parameters.

Or take the Book class as an example, and assume its original structure is as follows:

 1 class Book
 2 {
 3     public function __call($name, $arguments)
 4     {
 5         // Content to be filled
 6     }
 8     public function getById($id)
 9     {
10         return ['id' => $id, 'title' => 'PHP Caching Technology' . $id];
11     }
12 }


Before we start, we also confirm the Redis connection, which is required for caching. Here we write a simple singleton class:

 1 class Common
 2 {
 3     private static $redis = null;
 5     public static function redis()
 6     {
 7         if (self::$redis === null) {
 8             self::$redis = new \Redis('');
 9             self::$redis->connect('redis');
10         }
11         return self::$redis;
12 }


Then we start populating the u call() method code, which can be explained in the comments:

 1 class Book
 2 {
 3     public function __call($name, $arguments)
 4     {
 5         // Because we mainly decide what to do based on the suffix of the method name.
 6         // So if you pass in $name Length less than 5, direct error can be reported
 7         if (strlen($name) < 5) {
 8             exit('Method does not exist.');
 9         }
11         // Next, we intercept $name,Get the original method and the action to execute,
12         // yes cache,clear still flush,Here's a clever move
13         // The names are all five characters, which makes interception very efficient.
14         $method = substr($name, 0, -5);
15         $action = substr($name, -5);
17         // The class name of the current call, including the name of the namespace
18         $class = get_class();
20         // Generate cache key name, $arguments Add later
21         $key = sprintf('%s:%s:', str_replace('\\', '_', $class), $method);
22         // All in lowercase
23         $key = strtolower($key);
25         switch ($action) {
26             case 'Cache':
27                 // Cache key name plus $arguments
28                 $key = $key . md5(json_encode($arguments));
30                 // from Redis Read Data
31                 $data = Common::redis()->get($key);
33                 // If Redis Has data
34                 if ($data !== false) {
35                     $decodeData = json_decode($data, JSON_UNESCAPED_UNICODE);
36                     // If not JSON Format data, returned directly, otherwise returned json Parsed data
37                     return $decodeData === null ? $data : $decodeData;
38                 }
40                 // If Redis Continue without data in
42                 // If the original method does not exist
43                 if (method_exists($this, $method) === false) {
44                     exit('Method does not exist.');
45                 }
47                 // Call original method to get data
48                 $data = call_user_func_array([$this, $method], $arguments);
50                 // Save data to Redis Medium for next use
51                 Common::redis()->set($key, json_encode($data), 3600);
53                 // End execution and return data
54                 return $data;
55                 break;
57             case 'Clear':
58                 // Cache key name plus $arguments
59                 $key = $key . md5(json_encode($arguments));
60                 return Common::redis()->del($key);
61                 break;
63             case 'Flush':
64                 $key = $key . '*';
66                 // Get All Compliance $class:$method:* Rule's Cache Key Name 
67                 $keys = Common::redis()->keys($key);
68                 return Common::redis()->del($keys);
69                 break;
71             default:
72                 exit('Method does not exist.');
73         }
74     }
76     // Other methods
77 }


This achieves what we started with.

4 Actual use

In practice, we need to make some changes to put this code into a class.

Then reference this class in the base class of the model layer, and pass in the Redis handle, class object, method name, and parameters.

This reduces code coupling and makes it more flexible to use.

The complete code is already on Github, see the reference address at the beginning of the article.


Recommended reading:

Common methods for PHP to operate Redis databases

Summary of Redis's interview questions, necessary for job-hopping

Use PHP+Redis for delayed tasks and automatic cancellation of orders

PHP implements lightweight delayed queues based on Redis

php+redis for registration, deletion, editing, paging, login, attention and other functions

PHP Interviewer: What are Redis's expiration strategies?

Posted by grungefreak on Mon, 02 Dec 2019 13:14:20 -0800