Have you thought about how PHP can use redis as a cache?
- Front and background modules share the Model layer;
- However, not every Model class can be cached, which is a waste of Redis resources;
- The front and back modules are free to decide whether to read data from the database or from the cache.
- No redundant code;
- 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 } 7 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 } 7 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; 4 5 public static function redis() 6 { 7 if (self::$redis === null) { 8 self::$redis = new \Redis('127.0.0.1'); 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 } 10 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); 16 17 // The class name of the current call, including the name of the namespace 18 $class = get_class(); 19 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); 24 25 switch ($action) { 26 case 'Cache': 27 // Cache key name plus $arguments 28 $key = $key . md5(json_encode($arguments)); 29 30 // from Redis Read Data 31 $data = Common::redis()->get($key); 32 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 } 39 40 // If Redis Continue without data in 41 42 // If the original method does not exist 43 if (method_exists($this, $method) === false) { 44 exit('Method does not exist.'); 45 } 46 47 // Call original method to get data 48 $data = call_user_func_array([$this, $method], $arguments); 49 50 // Save data to Redis Medium for next use 51 Common::redis()->set($key, json_encode($data), 3600); 52 53 // End execution and return data 54 return $data; 55 break; 56 57 case 'Clear': 58 // Cache key name plus $arguments 59 $key = $key . md5(json_encode($arguments)); 60 return Common::redis()->del($key); 61 break; 62 63 case 'Flush': 64 $key = $key . '*'; 65 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; 70 71 default: 72 exit('Method does not exist.'); 73 } 74 } 75 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