├── HierarchicalPoolInterface.php ├── Changelog.md ├── LICENSE ├── composer.json ├── README.md └── HierarchicalCachePoolTrait.php /HierarchicalPoolInterface.php: -------------------------------------------------------------------------------- 1 | , Tobias Nyholm 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Cache\Hierarchy; 13 | 14 | use Psr\Cache\CacheItemPoolInterface; 15 | 16 | /** 17 | * Let you use hierarchy if you start your tag key with the HIERARCHY_SEPARATOR. 18 | * 19 | * @author Tobias Nyholm 20 | */ 21 | interface HierarchicalPoolInterface extends CacheItemPoolInterface 22 | { 23 | const HIERARCHY_SEPARATOR = '|'; 24 | } 25 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release. 4 | 5 | ## UNRELEASED 6 | 7 | ## 1.2.0 8 | 9 | * Support for PHP 8.1 10 | * Drop support for PHP < 7.4 11 | * Allow psr/cache: ^1.0 || ^2.0 12 | 13 | ## 1.1.0 14 | 15 | ### Added 16 | 17 | * Support for PHP 8 18 | 19 | ## 1.0.0 20 | 21 | * No changes since 0.4.0 22 | 23 | ## 0.4.0 24 | 25 | ### Changed 26 | 27 | * `HierarchicalCachePoolTrait::getValueFormStore` was renamed to `HierarchicalCachePoolTrait::getDirectValue` 28 | 29 | ### Removed 30 | 31 | * Dependency to `cache/taggable-cache`. 32 | 33 | ## 0.3.0 34 | 35 | ### Changed 36 | 37 | * The `HierarchicalPoolInterface` extends `CacheItemPoolInterface` 38 | 39 | ## 0.2.1 40 | 41 | * No changelog before this version 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Aaron Scherer, Tobias Nyholm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cache/hierarchical-cache", 3 | "description": "A helper trait and interface to your PSR-6 cache to support hierarchical keys.", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "cache", 8 | "psr-6", 9 | "hierarchy", 10 | "hierarchical" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Aaron Scherer", 15 | "email": "aequasi@gmail.com", 16 | "homepage": "https://github.com/aequasi" 17 | }, 18 | { 19 | "name": "Tobias Nyholm", 20 | "email": "tobias.nyholm@gmail.com", 21 | "homepage": "https://github.com/nyholm" 22 | } 23 | ], 24 | "homepage": "http://www.php-cache.com/en/latest/", 25 | "require": { 26 | "php": ">=7.4", 27 | "cache/adapter-common": "^1.0", 28 | "psr/cache": "^1.0 || ^2.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^7.5.20 || ^9.5.10" 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true, 35 | "autoload": { 36 | "psr-4": { 37 | "Cache\\Hierarchy\\": "" 38 | }, 39 | "exclude-from-classmap": [ 40 | "/Tests/" 41 | ] 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "1.1-dev" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hierarchical PSR-6 cache pool 2 | [![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 3 | [![Latest Stable Version](https://poser.pugx.org/cache/hierarchical-cache/v/stable)](https://packagist.org/packages/cache/hierarchical-cache) 4 | [![codecov.io](https://codecov.io/github/php-cache/hierarchical-cache/coverage.svg?branch=master)](https://codecov.io/github/php-cache/hierarchical-cache?branch=master) 5 | [![Total Downloads](https://poser.pugx.org/cache/hierarchical-cache/downloads)](https://packagist.org/packages/cache/hierarchical-cache) 6 | [![Monthly Downloads](https://poser.pugx.org/cache/hierarchical-cache/d/monthly.png)](https://packagist.org/packages/cache/hierarchical-cache) 7 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 8 | 9 | This is an implementation for the PSR-6 for an hierarchical cache architecture. 10 | 11 | If you have a cache key like `|users|:uid|followers|:fid|likes` where `:uid` and `:fid` are arbitrary integers. You 12 | may flush all followers by flushing `|users|:uid|followers`. 13 | 14 | It is a part of the PHP Cache organisation. To read about features like tagging and hierarchy support please read 15 | the shared documentation at [www.php-cache.com](http://www.php-cache.com). 16 | 17 | ### Install 18 | 19 | ```bash 20 | composer require cache/hierarchical-cache 21 | ``` 22 | 23 | ### Use 24 | 25 | Read the [documentation on usage](http://www.php-cache.com/en/latest/hierarchy/). 26 | 27 | ### Implement 28 | 29 | Read the [documentation on implementation](http://www.php-cache.com/en/latest/implementing-cache-pools/hierarchy/). 30 | 31 | ### Contribute 32 | 33 | Contributions are very welcome! Send a pull request to the [main repository](https://github.com/php-cache/cache) or 34 | report any issues you find on the [issue tracker](http://issues.php-cache.com). 35 | 36 | -------------------------------------------------------------------------------- /HierarchicalCachePoolTrait.php: -------------------------------------------------------------------------------- 1 | , Tobias Nyholm 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Cache\Hierarchy; 13 | 14 | use Cache\Adapter\Common\AbstractCachePool; 15 | 16 | /** 17 | * @author Tobias Nyholm 18 | */ 19 | trait HierarchicalCachePoolTrait 20 | { 21 | /** 22 | * A temporary cache for keys. 23 | * 24 | * @type array 25 | */ 26 | private $keyCache = []; 27 | 28 | /** 29 | * Get a value from the storage. 30 | * 31 | * @param string $name 32 | * 33 | * @return mixed 34 | */ 35 | abstract public function getDirectValue($name); 36 | 37 | /** 38 | * Get a key to use with the hierarchy. If the key does not start with HierarchicalPoolInterface::SEPARATOR 39 | * this will return an unalterered key. This function supports a tagged key. Ie "foo:bar". 40 | * 41 | * @param string $key The original key 42 | * @param string &$pathKey A cache key for the path. If this key is changed everything beyond that path is changed. 43 | * 44 | * @return string|array 45 | */ 46 | protected function getHierarchyKey($key, &$pathKey = null) 47 | { 48 | if (!$this->isHierarchyKey($key)) { 49 | return $key; 50 | } 51 | 52 | $key = $this->explodeKey($key); 53 | 54 | $keyString = ''; 55 | // The comments below is for a $key = ["foo!tagHash", "bar!tagHash"] 56 | foreach ($key as $name) { 57 | // 1) $keyString = "foo!tagHash" 58 | // 2) $keyString = "foo!tagHash![foo_index]!bar!tagHash" 59 | $keyString .= $name; 60 | $pathKey = sha1('path'.AbstractCachePool::SEPARATOR_TAG.$keyString); 61 | 62 | if (isset($this->keyCache[$pathKey])) { 63 | $index = $this->keyCache[$pathKey]; 64 | } else { 65 | $index = $this->getDirectValue($pathKey); 66 | $this->keyCache[$pathKey] = $index; 67 | } 68 | 69 | // 1) $keyString = "foo!tagHash![foo_index]!" 70 | // 2) $keyString = "foo!tagHash![foo_index]!bar!tagHash![bar_index]!" 71 | $keyString .= AbstractCachePool::SEPARATOR_TAG.$index.AbstractCachePool::SEPARATOR_TAG; 72 | } 73 | 74 | // Assert: $pathKey = "path!foo!tagHash![foo_index]!bar!tagHash" 75 | // Assert: $keyString = "foo!tagHash![foo_index]!bar!tagHash![bar_index]!" 76 | 77 | // Make sure we do not get awfully long (>250 chars) keys 78 | return sha1($keyString); 79 | } 80 | 81 | /** 82 | * Clear the cache for the keys. 83 | */ 84 | protected function clearHierarchyKeyCache() 85 | { 86 | $this->keyCache = []; 87 | } 88 | 89 | /** 90 | * A hierarchy key MUST begin with the separator. 91 | * 92 | * @param string $key 93 | * 94 | * @return bool 95 | */ 96 | private function isHierarchyKey($key) 97 | { 98 | return substr($key, 0, 1) === HierarchicalPoolInterface::HIERARCHY_SEPARATOR; 99 | } 100 | 101 | /** 102 | * This will take a hierarchy key ("|foo|bar") with tags ("|foo|bar!tagHash") and return an array with 103 | * each level in the hierarchy appended with the tags. ["foo!tagHash", "bar!tagHash"]. 104 | * 105 | * @param string $string 106 | * 107 | * @return array 108 | */ 109 | private function explodeKey($string) 110 | { 111 | list($key, $tag) = explode(AbstractCachePool::SEPARATOR_TAG, $string.AbstractCachePool::SEPARATOR_TAG); 112 | 113 | if ($key === HierarchicalPoolInterface::HIERARCHY_SEPARATOR) { 114 | $parts = ['root']; 115 | } else { 116 | $parts = explode(HierarchicalPoolInterface::HIERARCHY_SEPARATOR, $key); 117 | // remove first element since it is always empty and replace it with 'root' 118 | $parts[0] = 'root'; 119 | } 120 | 121 | return array_map(function ($level) use ($tag) { 122 | return $level.AbstractCachePool::SEPARATOR_TAG.$tag; 123 | }, $parts); 124 | } 125 | } 126 | --------------------------------------------------------------------------------