Have you ever wondered how PHP can use Redis as a cache?

  1. Front and back modules share Model layer;
  2. However, you can’t cache every Model class, which would be a waste of Redis resources;
  3. The front and back modules can freely decide whether to read data from the database or from the cache;
  4. No redundant code;
  5. Easy to use. Here we show the final result of the implementation.

The final code and instructions can be found at Github: github.com/yeszao/php-… .

Install now using command:

$ composer install yeszao/cache
Copy the code

This works with a simple configuration, as shown in Github’s README instructions.

1 Final Effect

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; }}Copy the code

When caching is added, neither the way the method is called nor the data structure returned should change.

So, hopefully, the end result should be something like this:

(new Book)->getById(100); // The raw, cache-free call is the same as before, usually to read data from the database. (new Book)->getByIdCache(100); App_models_book: getByID: + MD5 (parameter list) (new Book)->getByIdClear(100); // Delete the cache (new Book)->getByIdFlush(); // Remove all caches corresponding to the getById() method, i.e. remove app_models_book: getById :*. This method requires no arguments.Copy the code

In this way, we can clearly understand what we are doing, and at the same time know the source function of the data, and be quoted in a completely uniform way, it can be described as three birds with one arrow.

In fact, the implementation is relatively simple, is the use of PHP magic method __call() method.

2 the __call () method

Here’s a quick explanation of what the __call method does.

In PHP, when we access a nonexistent class method, the class’s __call() method is called.

(PHP will report an error if the class method does not exist and no __call() method is written.)

Suppose we have a Book class:

Class Book {public function __call($name, $arguments) {echo 'class Book ', $name, PHP_EOL; } public function getById($id, PHP_EOL) {$id, PHP_EOL; }}Copy the code

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

If getAge() does not exist, the program will execute into the __call() method of class A, which will print: getAge does not exist in class Book.

This is how __call works.

3 Implementation Details

Next we take advantage of this feature of the __call() method to implement a caching strategy.

From the example above, we see that when the __call() method is called, two arguments are passed.

Name: name of the method you want to call; arguments: parameter list we can work with arguments.

Using the Book class as an example, we assume that the original structure is as follows:

Class Book {public function __call($name, $arguments) {public function getById($id) {return ['id' => $id, $id]; }}Copy the code

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

class Common { private static $redis = null; Public static function redis() {if (self::$redis === null) {self::$redis = new \ redis('127.0.0.1'); self::$redis->connect('redis'); } return self::$redis; }Copy the code

We then start filling in the __call() method code, as described in the comment:

Class Book {public function __call($name, $arguments) { If (strlen($name) < 5) {exit('Method does not exist. } $name = 'cache', 'clear', or 'flush'; $method = substr($name, 0, -5); $action = substr($name, -5); $class = get_class(); $class = get_class(); / / generated cache keys, $the arguments later plus $key = sprintf (' % s: % s: 'str_replace (' \ \', '_', $class), $method); $key = strtolower($key); Switch ($action) {case 'Cache': $arguments $key = $key. md5(json_encode($arguments)); $data = Common:: Redis ()->get($key); // If ($data! == false) { $decodeData = json_decode($data, JSON_UNESCAPED_UNICODE); $decodeData === null? $decodeData === null? $data : $decodeData; If (method_exists($this, $method) === false) {exit(' method does not exist. $data = call_user_func_array([$this, $method], $arguments); Common:: Redis ()->set($key, json_encode($data, 3600); Return $data; break; Case 'Clear': $arguments $key = $key. md5(json_encode($arguments)); return Common::redis()->del($key); break; case 'Flush': $key = $key . '*'; $key = Common::redis()->keys($key); return Common::redis()->del($keys); break; default: exit('Method does not exist.'); }} // Other methods}Copy the code

So that’s what we started with.

4 In actual use

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

We then reference this class in the base class of the Model layer, passing in a Redis handle, class object, method name, and parameter,

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

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

I hope the above content can help you. Many PHPer will encounter some problems and bottlenecks when they are advanced, and they have no sense of direction when writing too many business codes. I have sorted out some information, including but not limited to: Distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, Laravel, YII2, Redis, Swoft, Kafka, Mysql optimization, shell scripting, Docker, microservices, Nginx, etc. Many knowledge points can be free to share with you