friendship ended with social-app. php is my new best friend
at main 5.7 kB view raw
1<?php 2 3namespace React\Cache; 4 5use React\Promise; 6use React\Promise\PromiseInterface; 7 8class ArrayCache implements CacheInterface 9{ 10 private $limit; 11 private $data = array(); 12 private $expires = array(); 13 private $supportsHighResolution; 14 15 /** 16 * The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface). 17 * 18 * ```php 19 * $cache = new ArrayCache(); 20 * 21 * $cache->set('foo', 'bar'); 22 * ``` 23 * 24 * Its constructor accepts an optional `?int $limit` parameter to limit the 25 * maximum number of entries to store in the LRU cache. If you add more 26 * entries to this instance, it will automatically take care of removing 27 * the one that was least recently used (LRU). 28 * 29 * For example, this snippet will overwrite the first value and only store 30 * the last two entries: 31 * 32 * ```php 33 * $cache = new ArrayCache(2); 34 * 35 * $cache->set('foo', '1'); 36 * $cache->set('bar', '2'); 37 * $cache->set('baz', '3'); 38 * ``` 39 * 40 * This cache implementation is known to rely on wall-clock time to schedule 41 * future cache expiration times when using any version before PHP 7.3, 42 * because a monotonic time source is only available as of PHP 7.3 (`hrtime()`). 43 * While this does not affect many common use cases, this is an important 44 * distinction for programs that rely on a high time precision or on systems 45 * that are subject to discontinuous time adjustments (time jumps). 46 * This means that if you store a cache item with a TTL of 30s on PHP < 7.3 47 * and then adjust your system time forward by 20s, the cache item may 48 * expire in 10s. See also [`set()`](#set) for more details. 49 * 50 * @param int|null $limit maximum number of entries to store in the LRU cache 51 */ 52 public function __construct($limit = null) 53 { 54 $this->limit = $limit; 55 56 // prefer high-resolution timer, available as of PHP 7.3+ 57 $this->supportsHighResolution = \function_exists('hrtime'); 58 } 59 60 public function get($key, $default = null) 61 { 62 // delete key if it is already expired => below will detect this as a cache miss 63 if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { 64 unset($this->data[$key], $this->expires[$key]); 65 } 66 67 if (!\array_key_exists($key, $this->data)) { 68 return Promise\resolve($default); 69 } 70 71 // remove and append to end of array to keep track of LRU info 72 $value = $this->data[$key]; 73 unset($this->data[$key]); 74 $this->data[$key] = $value; 75 76 return Promise\resolve($value); 77 } 78 79 public function set($key, $value, $ttl = null) 80 { 81 // unset before setting to ensure this entry will be added to end of array (LRU info) 82 unset($this->data[$key]); 83 $this->data[$key] = $value; 84 85 // sort expiration times if TTL is given (first will expire first) 86 unset($this->expires[$key]); 87 if ($ttl !== null) { 88 $this->expires[$key] = $this->now() + $ttl; 89 \asort($this->expires); 90 } 91 92 // ensure size limit is not exceeded or remove first entry from array 93 if ($this->limit !== null && \count($this->data) > $this->limit) { 94 // first try to check if there's any expired entry 95 // expiration times are sorted, so we can simply look at the first one 96 \reset($this->expires); 97 $key = \key($this->expires); 98 99 // check to see if the first in the list of expiring keys is already expired 100 // if the first key is not expired, we have to overwrite by using LRU info 101 if ($key === null || $this->now() - $this->expires[$key] < 0) { 102 \reset($this->data); 103 $key = \key($this->data); 104 } 105 unset($this->data[$key], $this->expires[$key]); 106 } 107 108 return Promise\resolve(true); 109 } 110 111 public function delete($key) 112 { 113 unset($this->data[$key], $this->expires[$key]); 114 115 return Promise\resolve(true); 116 } 117 118 public function getMultiple(array $keys, $default = null) 119 { 120 $values = array(); 121 122 foreach ($keys as $key) { 123 $values[$key] = $this->get($key, $default); 124 } 125 126 return Promise\all($values); 127 } 128 129 public function setMultiple(array $values, $ttl = null) 130 { 131 foreach ($values as $key => $value) { 132 $this->set($key, $value, $ttl); 133 } 134 135 return Promise\resolve(true); 136 } 137 138 public function deleteMultiple(array $keys) 139 { 140 foreach ($keys as $key) { 141 unset($this->data[$key], $this->expires[$key]); 142 } 143 144 return Promise\resolve(true); 145 } 146 147 public function clear() 148 { 149 $this->data = array(); 150 $this->expires = array(); 151 152 return Promise\resolve(true); 153 } 154 155 public function has($key) 156 { 157 // delete key if it is already expired 158 if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { 159 unset($this->data[$key], $this->expires[$key]); 160 } 161 162 if (!\array_key_exists($key, $this->data)) { 163 return Promise\resolve(false); 164 } 165 166 // remove and append to end of array to keep track of LRU info 167 $value = $this->data[$key]; 168 unset($this->data[$key]); 169 $this->data[$key] = $value; 170 171 return Promise\resolve(true); 172 } 173 174 /** 175 * @return float 176 */ 177 private function now() 178 { 179 return $this->supportsHighResolution ? \hrtime(true) * 1e-9 : \microtime(true); 180 } 181}