├── roave-bc-check.yaml
├── CHANGELOG.md
├── infection.json.dist
├── CONTRIBUTING.md
├── psalm_autoload.php
├── src
├── CacheArgumentException.php
├── SupportTrait.php
├── Item.php
├── SimpleCache.php
└── Pool.php
├── composer-unused.php
├── psalm.xml
├── .php-cs-fixer.dist.php
├── LICENSE
├── SECURITY.md
├── composer.json
├── README.md
├── deptrac.yaml
└── rector.php
/roave-bc-check.yaml:
--------------------------------------------------------------------------------
1 | parameters:
2 | ignoreErrors:
3 | - '#\[BC\] SKIPPED: .+ could not be found in the located source#'
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 |
4 |
5 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
6 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src/"
5 | ],
6 | "excludes": [
7 | "Config",
8 | "Database/Migrations",
9 | "Views"
10 | ]
11 | },
12 | "logs": {
13 | "text": "build/infection.log"
14 | },
15 | "mutators": {
16 | "@default": true
17 | },
18 | "bootstrap": "vendor/codeigniter4/framework/system/Test/bootstrap.php"
19 | }
20 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to CodeIgniter4
2 |
3 | CodeIgniter is a community driven project and accepts contributions of
4 | code and documentation from the community.
5 |
6 | If you'd like to contribute, please read [Contributing to CodeIgniter](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/README.md)
7 | in the [main repository](https://github.com/codeigniter4/CodeIgniter4).
8 |
9 | If you are going to contribute to this repository, please report bugs or send PRs
10 | to this repository instead of the main repository.
11 |
--------------------------------------------------------------------------------
/psalm_autoload.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace CodeIgniter\Psr\Cache;
13 |
14 | use InvalidArgumentException;
15 | use Psr\Cache\InvalidArgumentException as CacheException;
16 | use Psr\SimpleCache\InvalidArgumentException as SimpleCacheException;
17 |
18 | final class CacheArgumentException extends InvalidArgumentException implements CacheException, SimpleCacheException
19 | {
20 | }
21 |
--------------------------------------------------------------------------------
/composer-unused.php:
--------------------------------------------------------------------------------
1 | addNamedFilter(NamedFilter::fromString('symfony/config'))
13 | // ->addPatternFilter(PatternFilter::fromString('/symfony-.*/'))
14 | ->setAdditionalFilesFor('codeigniter4/framework', [
15 | ...Glob::glob(__DIR__ . '/vendor/codeigniter4/framework/system/Helpers/*.php'),
16 | ]);
17 | };
18 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | files()
9 | ->in([
10 | __DIR__ . '/src/',
11 | __DIR__ . '/tests/',
12 | ])
13 | ->exclude([
14 | 'build',
15 | 'Views',
16 | ])
17 | ->append([
18 | __FILE__,
19 | __DIR__ . '/rector.php',
20 | ]);
21 |
22 | $overrides = [
23 | // 'declare_strict_types' => true,
24 | // 'void_return' => true,
25 | ];
26 |
27 | $options = [
28 | 'finder' => $finder,
29 | 'cacheFile' => 'build/.php-cs-fixer.cache',
30 | ];
31 |
32 | return Factory::create(new CodeIgniter4(), $overrides, $options)->forProjects();
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 CodeIgniter Foundation
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | The development team and community take all security issues seriously. **Please do not make public any uncovered flaws.**
4 |
5 | ## Reporting a Vulnerability
6 |
7 | Thank you for improving the security of our code! Any assistance in removing security flaws will be acknowledged.
8 |
9 | **Please report security flaws by emailing the development team directly: security@codeigniter.com**.
10 |
11 | The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating
12 | the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the
13 | progress towards a fix and full announcement, and may ask for additional information or guidance.
14 |
15 | ## Disclosure Policy
16 |
17 | When the security team receives a security bug report, they will assign it to a primary handler.
18 | This person will coordinate the fix and release process, involving the following steps:
19 |
20 | - Confirm the problem and determine the affected versions.
21 | - Audit code to find any potential similar problems.
22 | - Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
23 |
24 | ## Comments on this Policy
25 |
26 | If you have suggestions on how this process could be improved please submit a Pull Request.
27 |
--------------------------------------------------------------------------------
/src/SupportTrait.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace CodeIgniter\Psr\Cache;
13 |
14 | use CodeIgniter\Cache\CacheInterface;
15 | use Config\Cache;
16 |
17 | /**
18 | * Cache Support Trait
19 | *
20 | * Provides methods common to both
21 | * PSR-6 and PSR-16 drivers.
22 | */
23 | trait SupportTrait
24 | {
25 | /**
26 | * The adapter to use.
27 | *
28 | * @var CacheInterface
29 | */
30 | private $adapter;
31 |
32 | /**
33 | * Initializes the underlying adapter
34 | * from an existing instance or from the
35 | * Cache Service (with optional config).
36 | *
37 | * @param object|null $object
38 | *
39 | * @throws CacheArgumentException
40 | */
41 | public function __construct($object = null)
42 | {
43 | if (null === $object) {
44 | $this->adapter = service('cache');
45 | } elseif ($object instanceof Cache) {
46 | $this->adapter = service('cache', $object, false);
47 | } elseif ($object instanceof CacheInterface) {
48 | $this->adapter = $object;
49 | } else {
50 | throw new CacheArgumentException(self::class . ' constructor only accepts an adapter or configuration');
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codeigniter4/cache",
3 | "description": "PSR-6 and PSR-16 Cache Adapters for CodeIgniter 4",
4 | "license": "MIT",
5 | "type": "library",
6 | "keywords": [
7 | "codeigniter",
8 | "codeigniter4",
9 | "cache",
10 | "pool",
11 | "simplecache",
12 | "psr-6",
13 | "psr-16"
14 | ],
15 | "authors": [
16 | {
17 | "name": "Matthew Gatner",
18 | "email": "mgatner@tattersoftware.com",
19 | "homepage": "https://tattersoftware.com",
20 | "role": "Developer"
21 | }
22 | ],
23 | "homepage": "https://github.com/codeigniter4/cache",
24 | "require": {
25 | "php": "^7.4 || ^8.0",
26 | "psr/cache": "^1.0",
27 | "psr/simple-cache": "^1.0"
28 | },
29 | "require-dev": {
30 | "cache/integration-tests": "^0.17.0",
31 | "codeigniter4/devkit": "^1.0",
32 | "codeigniter4/framework": "^4.1",
33 | "phpunit/phpunit": "^9.6",
34 | "rector/rector": "1.2.8"
35 | },
36 | "provide": {
37 | "psr/cache-implementation": "^1.0",
38 | "psr/simple-cache-implementation": "^1.0 || ^3.0"
39 | },
40 | "minimum-stability": "dev",
41 | "prefer-stable": true,
42 | "autoload": {
43 | "psr-4": {
44 | "CodeIgniter\\Psr\\Cache\\": "src"
45 | },
46 | "exclude-from-classmap": [
47 | "**/Database/Migrations/**"
48 | ]
49 | },
50 | "autoload-dev": {
51 | "psr-4": {
52 | "Tests\\Support\\": "tests/_support"
53 | }
54 | },
55 | "config": {
56 | "allow-plugins": {
57 | "phpstan/extension-installer": true
58 | }
59 | },
60 | "scripts": {
61 | "analyze": [
62 | "Composer\\Config::disableProcessTimeout",
63 | "phpstan analyze",
64 | "psalm",
65 | "rector process --dry-run"
66 | ],
67 | "sa": "@analyze",
68 | "ci": [
69 | "Composer\\Config::disableProcessTimeout",
70 | "@cs",
71 | "@deduplicate",
72 | "@inspect",
73 | "@analyze",
74 | "@test"
75 | ],
76 | "cs": "php-cs-fixer fix --ansi --verbose --dry-run --diff",
77 | "cs-fix": "php-cs-fixer fix --ansi --verbose --diff --using-cache=yes",
78 | "style": "@cs-fix",
79 | "deduplicate": "phpcpd src/ tests/",
80 | "inspect": "deptrac analyze --cache-file=build/deptrac.cache",
81 | "test": "phpunit"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodeIgniter 4 PSR Cache
2 |
3 | PSR-6 and PSR-16 Cache Adapters for CodeIgniter 4
4 |
5 | [](https://github.com/codeigniter4/cache/actions/workflows/phpunit.yml)
6 | [](https://github.com/codeigniter4/cache/actions/workflows/phpstan.yml)
7 | [](https://github.com/codeigniter4/cache/actions/workflows/deptrac.yml)
8 | [](https://coveralls.io/github/codeigniter4/cache?branch=develop)
9 |
10 | **Disclaimer: CodeIgniter 4 comes with a fully-functional cache component! This module
11 | is only for integrating third-party packages that rely on the PSR interface provisions.**
12 |
13 | ## Quick Start
14 |
15 | 1. Install with Composer: `> composer require codeigniter4/cache`
16 | 2. Integrate with your favorite packages:
17 |
18 | ```php
19 | use CodeIgniter\Psr\Cache\SimpleCache;
20 | use Kreait\Firebase\Factory;
21 | ...
22 |
23 | $factory = (new Factory)->withVerifierCache(new SimpleCache());
24 | ```
25 |
26 | ## Features
27 |
28 | A set of adapters fully-compliant with PSR-6 and PSR-16 to integrate with CodeIgniter 4's Caching Driver.
29 |
30 | ## Installation
31 |
32 | Install easily via Composer to take advantage of CodeIgniter 4's autoloading capabilities
33 | and always be up-to-date:
34 |
35 | * `> composer require codeigniter4/cache`
36 |
37 | Or, install manually by downloading the source files and adding the directory to
38 | `app/Config/Autoload.php`.
39 |
40 | ## Usage
41 |
42 | This module has adapters for CodeIgniter 4 to supply the following FIG PHP Standards Recommendations (PSR):
43 | * [Caching Interface](https://www.php-fig.org/psr/psr-6)
44 | * [Simple Cache](https://www.php-fig.org/psr/psr-16)
45 |
46 | If you just need a caching agent then you should use the framework's native [Caching Driver](https://codeigniter4.github.io/CodeIgniter4/libraries/caching.html).
47 | These adapters are intended to integrate with any library or project that requires either of the following:
48 |
49 | * [psr/cache-implementation](https://packagist.org/packages/psr/cache/dependents?order_by=downloads)
50 | * [psr/simple-cache-implementation](https://packagist.org/packages/psr/simple-cache/dependents?order_by=downloads)
51 |
52 | The interfaces are provided by the following classes:
53 |
54 | * `Psr\Cache\CacheItemInterface` provided by `CodeIgniter\Psr\Cache\Item`
55 | * `Psr\Cache\CacheItemPoolInterface` provided by `CodeIgniter\Psr\Cache\Pool`
56 | * `Psr\SimpleCache\CacheInterface` provided by `CodeIgniter\Psr\Cache\SimpleCache`
57 |
58 | By default the adapters (`Pool` and `SimpleCache`) will work with the Caching Driver as defined
59 | in you cache configuration (e.g. **app/Config/Cache.php**). You may create either driver explicitly
60 | with an alternative Cache Handler or Config:
61 |
62 | ```php
63 | $sharedCacheServicePool = new \CodeIgniter\Psr\Cache\Pool();
64 |
65 | $fileHandler = new \CodeIgniter\Cache\Handlers\FileHandler(config('Cache'));
66 | $explicitFileHandlerSimpleCache = new \CodeIgniter\Psr\Cache\SimpleCache($fileHandler);
67 |
68 | $config = config('Cache');
69 | $config->prefix = 'banana-';
70 | $alternativeConfigPool = new \CodeIgniter\Psr\Cache\Pool($config);
71 | ```
72 |
73 | ## Testing
74 |
75 | Testing of the underlying Caching Driver is handled in the [CodeIgniter 4 repo](https://github.com/codeigniter4/CodeIgniter4/tree/develop/tests/system/Cache).
76 | These adapters are tested with the [PHP Cache Integration Tests](https://github.com/php-cache/integration-tests), by Aaron Scherer.
77 | You may run the test suite by cloning this repo, installing all dependencies, and running:
78 |
79 | * `> composer test`
80 |
--------------------------------------------------------------------------------
/deptrac.yaml:
--------------------------------------------------------------------------------
1 | parameters:
2 | paths:
3 | - ./src/
4 | - ./vendor/codeigniter4/framework/system/
5 | exclude_files:
6 | - '#.*test.*#i'
7 | layers:
8 | - name: Model
9 | collectors:
10 | - type: bool
11 | must:
12 | - type: className
13 | regex: .*[A-Za-z]+Model$
14 | must_not:
15 | - type: directory
16 | regex: vendor/.*
17 | - name: Vendor Model
18 | collectors:
19 | - type: bool
20 | must:
21 | - type: className
22 | regex: .*[A-Za-z]+Model$
23 | - type: directory
24 | regex: vendor/.*
25 | - name: Controller
26 | collectors:
27 | - type: bool
28 | must:
29 | - type: className
30 | regex: .*\/Controllers\/.*
31 | must_not:
32 | - type: directory
33 | regex: vendor/.*
34 | - name: Vendor Controller
35 | collectors:
36 | - type: bool
37 | must:
38 | - type: className
39 | regex: .*\/Controllers\/.*
40 | - type: directory
41 | regex: vendor/.*
42 | - name: Config
43 | collectors:
44 | - type: bool
45 | must:
46 | - type: directory
47 | regex: src/Config/.*
48 | must_not:
49 | - type: className
50 | regex: .*Services
51 | - type: directory
52 | regex: vendor/.*
53 | - name: Vendor Config
54 | collectors:
55 | - type: bool
56 | must:
57 | - type: directory
58 | regex: vendor/.*/Config/.*
59 | must_not:
60 | - type: className
61 | regex: .*Services
62 | - name: Entity
63 | collectors:
64 | - type: bool
65 | must:
66 | - type: directory
67 | regex: src/Entities/.*
68 | must_not:
69 | - type: directory
70 | regex: vendor/.*
71 | - name: Vendor Entity
72 | collectors:
73 | - type: bool
74 | must:
75 | - type: directory
76 | regex: vendor/.*/Entities/.*
77 | - name: View
78 | collectors:
79 | - type: bool
80 | must:
81 | - type: directory
82 | regex: src/Views/.*
83 | must_not:
84 | - type: directory
85 | regex: vendor/.*
86 | - name: Vendor View
87 | collectors:
88 | - type: bool
89 | must:
90 | - type: directory
91 | regex: vendor/.*/Views/.*
92 | - name: Service
93 | collectors:
94 | - type: className
95 | regex: .*Services.*
96 | ruleset:
97 | Entity:
98 | - Config
99 | - Model
100 | - Service
101 | - Vendor Config
102 | - Vendor Entity
103 | - Vendor Model
104 | Config:
105 | - Service
106 | - Vendor Config
107 | Model:
108 | - Config
109 | - Entity
110 | - Service
111 | - Vendor Config
112 | - Vendor Entity
113 | - Vendor Model
114 | Service:
115 | - Config
116 | - Vendor Config
117 |
118 | # Ignore anything in the Vendor layers
119 | Vendor Model:
120 | - Config
121 | - Service
122 | - Vendor Config
123 | - Vendor Controller
124 | - Vendor Entity
125 | - Vendor Model
126 | - Vendor View
127 | Vendor Controller:
128 | - Service
129 | - Vendor Config
130 | - Vendor Controller
131 | - Vendor Entity
132 | - Vendor Model
133 | - Vendor View
134 | Vendor Config:
135 | - Config
136 | - Service
137 | - Vendor Config
138 | - Vendor Controller
139 | - Vendor Entity
140 | - Vendor Model
141 | - Vendor View
142 | Vendor Entity:
143 | - Service
144 | - Vendor Config
145 | - Vendor Controller
146 | - Vendor Entity
147 | - Vendor Model
148 | - Vendor View
149 | Vendor View:
150 | - Service
151 | - Vendor Config
152 | - Vendor Controller
153 | - Vendor Entity
154 | - Vendor Model
155 | - Vendor View
156 | skip_violations:
157 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | sets([SetList::DEAD_CODE, LevelSetList::UP_TO_PHP_74, PHPUnitSetList::PHPUNIT_80]);
34 | $rectorConfig->parallel();
35 | // The paths to refactor (can also be supplied with CLI arguments)
36 | $rectorConfig->paths([
37 | __DIR__ . '/src/',
38 | __DIR__ . '/tests/',
39 | ]);
40 |
41 | // Include Composer's autoload - required for global execution, remove if running locally
42 | $rectorConfig->autoloadPaths([
43 | __DIR__ . '/vendor/autoload.php',
44 | ]);
45 |
46 | // Do you need to include constants, class aliases, or a custom autoloader?
47 | $rectorConfig->bootstrapFiles([
48 | realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php',
49 | ]);
50 |
51 | if (is_file(__DIR__ . '/phpstan.neon.dist')) {
52 | $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist');
53 | }
54 |
55 | // Set the target version for refactoring
56 | $rectorConfig->phpVersion(PhpVersion::PHP_74);
57 |
58 | // Auto-import fully qualified class names
59 | $rectorConfig->importNames();
60 |
61 | // Are there files or rules you need to skip?
62 | $rectorConfig->skip([
63 | __DIR__ . '/src/Views',
64 |
65 | StringifyStrNeedlesRector::class,
66 |
67 | // Note: requires php 8
68 | RemoveUnusedPromotedPropertyRector::class,
69 |
70 | // May load view files directly when detecting classes
71 | StringClassNameToClassConstantRector::class,
72 | ]);
73 | $rectorConfig->rule(SimplifyUselessVariableRector::class);
74 | $rectorConfig->rule(RemoveAlwaysElseRector::class);
75 | $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class);
76 | $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class);
77 | $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class);
78 | $rectorConfig->rule(SimplifyStrposLowerRector::class);
79 | $rectorConfig->rule(CombineIfRector::class);
80 | $rectorConfig->rule(SimplifyIfReturnBoolRector::class);
81 | $rectorConfig->rule(InlineIfToExplicitIfRector::class);
82 | $rectorConfig->rule(PreparedValueToEarlyReturnRector::class);
83 | $rectorConfig->rule(ShortenElseIfRector::class);
84 | $rectorConfig->rule(SimplifyIfElseToTernaryRector::class);
85 | $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class);
86 | $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class);
87 | $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class);
88 | $rectorConfig->rule(SimplifyRegexPatternRector::class);
89 | $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class);
90 | $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class);
91 | $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class);
92 | $rectorConfig
93 | ->ruleWithConfiguration(TypedPropertyFromAssignsRector::class, [
94 | // Set to false if you use in libraries, or it does create breaking changes.
95 | TypedPropertyFromAssignsRector::INLINE_PUBLIC => true,
96 | ]);
97 | };
98 |
--------------------------------------------------------------------------------
/src/Item.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace CodeIgniter\Psr\Cache;
13 |
14 | use CodeIgniter\Cache\Handlers\BaseHandler;
15 | use CodeIgniter\I18n\Time;
16 | use DateInterval;
17 | use DateTimeInterface;
18 | use InvalidArgumentException;
19 | use Psr\Cache\CacheItemInterface;
20 |
21 | final class Item implements CacheItemInterface
22 | {
23 | /**
24 | * Reserved characters that cannot be used in a key or tag.
25 | *
26 | * @see https://github.com/symfony/cache-contracts/blob/c0446463729b89dd4fa62e9aeecc80287323615d/ItemInterface.php#L43
27 | */
28 | public const RESERVED_CHARACTERS = '{}()/\@:';
29 |
30 | private string $key;
31 |
32 | /**
33 | * @var mixed
34 | */
35 | private $value;
36 |
37 | /**
38 | * Whether this Item was the result
39 | * of a cache hit.
40 | */
41 | private bool $hit;
42 |
43 | /**
44 | * The expiration time
45 | */
46 | private ?Time $expiration = null;
47 |
48 | /**
49 | * Validates a cache key according to PSR-6.
50 | *
51 | * @param mixed $key The key to validate
52 | *
53 | * @throws CacheArgumentException When $key is not valid
54 | */
55 | public static function validateKey($key)
56 | {
57 | // Use the framework's Cache key validation
58 | try {
59 | BaseHandler::validateKey($key);
60 | } catch (InvalidArgumentException $e) {
61 | throw new CacheArgumentException($e->getMessage(), $e->getCode(), $e);
62 | }
63 | }
64 |
65 | /**
66 | * Stores the Item's details.
67 | *
68 | * @param mixed $value
69 | */
70 | public function __construct(string $key, $value, bool $hit)
71 | {
72 | $this->key = $key;
73 | $this->value = $value;
74 | $this->hit = $hit;
75 | }
76 |
77 | /**
78 | * Returns the key for the current cache item.
79 | *
80 | * The key is loaded by the Implementing Library, but should be available to
81 | * the higher level callers when needed.
82 | *
83 | * The key string for this cache item.
84 | */
85 | public function getKey(): string
86 | {
87 | return $this->key;
88 | }
89 |
90 | /**
91 | * Retrieves the value of the item from the cache associated with this object's key.
92 | *
93 | * The value returned must be identical to the value originally stored by set().
94 | *
95 | * If isHit() returns false, this method MUST return null. Note that null
96 | * is a legitimate cached value, so the isHit() method SHOULD be used to
97 | * differentiate between "null value was found" and "no value was found."
98 | *
99 | * @return mixed
100 | * The value corresponding to this cache item's key, or null if not found.
101 | */
102 | public function get()
103 | {
104 | return $this->value;
105 | }
106 |
107 | /**
108 | * Confirms if the cache item lookup resulted in a cache hit.
109 | *
110 | * Note: This method MUST NOT have a race condition between calling isHit()
111 | * and calling get().
112 | *
113 | * True if the request resulted in a cache hit. False otherwise.
114 | */
115 | public function isHit(): bool
116 | {
117 | return $this->hit;
118 | }
119 |
120 | /**
121 | * Sets the value represented by this cache item.
122 | *
123 | * The $value argument may be any item that can be serialized by PHP,
124 | * although the method of serialization is left up to the Implementing
125 | * Library.
126 | *
127 | * @param mixed $value
128 | * The serializable value to be stored.
129 | *
130 | * The invoked object.
131 | */
132 | public function set($value): self
133 | {
134 | $this->value = $value;
135 |
136 | return $this;
137 | }
138 |
139 | /**
140 | * Sets the expiration time for this cache item.
141 | *
142 | * @param DateTimeInterface|null $expiration
143 | * The point in time after which the item MUST be considered expired.
144 | * If null is passed explicitly, a default value MAY be used. If none is set,
145 | * the value should be stored permanently or for as long as the
146 | * implementation allows.
147 | *
148 | * The called object.
149 | */
150 | public function expiresAt($expiration): self
151 | {
152 | if ($expiration === null) {
153 | $this->expiration = null;
154 | } elseif ($expiration instanceof DateTimeInterface) {
155 | $this->expiration = Time::createFromInstance($expiration);
156 | } else {
157 | throw new CacheArgumentException('Expiration date must be a DateTimeInterface or null');
158 | }
159 |
160 | return $this;
161 | }
162 |
163 | /**
164 | * Sets the expiration time for this cache item.
165 | *
166 | * @param DateInterval|int|null $time
167 | * The period of time from the present after which the item MUST be considered
168 | * expired. An integer parameter is understood to be the time in seconds until
169 | * expiration. If null is passed explicitly, a default value MAY be used.
170 | * If none is set, the value should be stored permanently or for as long as the
171 | * implementation allows.
172 | *
173 | * The called object.
174 | */
175 | public function expiresAfter($time): self
176 | {
177 | if ($time === null) {
178 | $this->expiration = null;
179 | } elseif ($time instanceof DateInterval) {
180 | $this->expiration = Time::now()->add($time);
181 | } elseif (is_int($time)) {
182 | $this->expiration = Time::now()->addSeconds($time);
183 | } else {
184 | throw new CacheArgumentException('Expiration date must be an integer, a DateInterval or null');
185 | }
186 |
187 | return $this;
188 | }
189 |
190 | /**
191 | * Returns the expiration Time.
192 | * This method is not a requirement of PSR-6 but is necessary
193 | * to pass "testExpiration".
194 | *
195 | * @see https://groups.google.com/g/php-fig/c/Qr4OxCf7J5Y
196 | */
197 | public function getExpiration(): ?Time
198 | {
199 | return $this->expiration;
200 | }
201 |
202 | /**
203 | * Returns whether or not this Item is expired.
204 | * This method is not a requirement of PSR-6 but is necessary
205 | * to pass "testSavedExpired".
206 | *
207 | * @see https://groups.google.com/g/php-fig/c/Qr4OxCf7J5Y
208 | *
209 | * @return bool True if this Item is expired.
210 | */
211 | public function isExpired(): bool
212 | {
213 | if (isset($this->expiration)) {
214 | $now = Time::now();
215 |
216 | return $this->expiration->isBefore($now) || $this->expiration->sameAs($now);
217 | }
218 |
219 | return false;
220 | }
221 |
222 | /**
223 | * Sets the hit value.
224 | * This method is not a requirement of PSR-6 but is necessary
225 | * to allow deferred items to count as hits.
226 | *
227 | * @return $this
228 | */
229 | public function setHit(bool $hit): self
230 | {
231 | $this->hit = $hit;
232 |
233 | return $this;
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/SimpleCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace CodeIgniter\Psr\Cache;
13 |
14 | use DateInterval;
15 | use Psr\SimpleCache\CacheInterface;
16 | use Traversable;
17 |
18 | final class SimpleCache implements CacheInterface
19 | {
20 | use SupportTrait;
21 |
22 | /**
23 | * Fetches a value from the cache.
24 | *
25 | * @param string $key The unique key of this item in the cache.
26 | * @param mixed $default Default value to return if the key does not exist.
27 | *
28 | * @return mixed The value of the item from the cache, or $default in case of cache miss.
29 | *
30 | * @throws CacheArgumentException
31 | * MUST be thrown if the $key string is not a legal value.
32 | */
33 | public function get($key, $default = null)
34 | {
35 | Item::validateKey($key);
36 |
37 | $meta = $this->adapter->getMetaData($key);
38 |
39 | // If the adapter does not return an array or if the item is expired then it is a miss
40 | if (! is_array($meta) || (is_int($meta['expire']) && $meta['expire'] < time())) {
41 | return $default;
42 | }
43 |
44 | return $this->adapter->get($key);
45 | }
46 |
47 | /**
48 | * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
49 | *
50 | * @param string $key The key of the item to store.
51 | * @param mixed $value The value of the item to store. Must be serializable.
52 | * @param DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and
53 | * the driver supports TTL then the library may set a default value
54 | * for it or let the driver take care of that.
55 | *
56 | * @return bool True on success and false on failure.
57 | *
58 | * @throws CacheArgumentException
59 | * MUST be thrown if the $key string is not a legal value.
60 | */
61 | public function set($key, $value, $ttl = null)
62 | {
63 | Item::validateKey($key);
64 |
65 | // Get TTL as an integer (seconds)
66 | if (null === $ttl) {
67 | $ttl = config('Cache')->ttl ?? 60;
68 | } elseif ($ttl instanceof DateInterval) {
69 | $ttl = $ttl->s;
70 | } elseif (! is_int($ttl)) {
71 | throw new CacheArgumentException('TTL value must be one of: null, integer, DateInterval.');
72 | }
73 |
74 | // Do not save expired items
75 | if ($ttl <= 0) {
76 | $this->delete($key);
77 |
78 | return false;
79 | }
80 |
81 | return $this->adapter->save($key, $value, $ttl);
82 | }
83 |
84 | /**
85 | * Delete an item from the cache by its unique key.
86 | *
87 | * @param string $key The unique cache key of the item to delete.
88 | *
89 | * @return bool True if the item was successfully removed. False if there was an error.
90 | *
91 | * @throws CacheArgumentException
92 | * MUST be thrown if the $key string is not a legal value.
93 | */
94 | public function delete($key)
95 | {
96 | Item::validateKey($key);
97 |
98 | // Nonexistant keys return true
99 | if (! is_array($this->adapter->getMetaData($key))) {
100 | return true;
101 | }
102 |
103 | return $this->adapter->delete($key);
104 | }
105 |
106 | /**
107 | * Wipes clean the entire cache's keys.
108 | *
109 | * @return bool True on success and false on failure.
110 | */
111 | public function clear()
112 | {
113 | return $this->adapter->clean();
114 | }
115 |
116 | /**
117 | * Obtains multiple cache items by their unique keys.
118 | *
119 | * @param iterable $keys A list of keys that can obtained in a single operation.
120 | * @param mixed $default Default value to return for keys that do not exist.
121 | *
122 | * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
123 | *
124 | * @throws CacheArgumentException
125 | * MUST be thrown if $keys is neither an array nor a Traversable,
126 | * or if any of the $keys are not a legal value.
127 | */
128 | public function getMultiple($keys, $default = null)
129 | {
130 | if (! (is_iterable($keys))) {
131 | throw new CacheArgumentException('getMultiple only accepts traversable input.');
132 | }
133 |
134 | // CacheInterface has no spec for multiple item retrieval
135 | // so we have to power through them individually.
136 | $items = [];
137 |
138 | foreach ($keys as $key) {
139 | $items[$key] = $this->get($key, $default);
140 | }
141 |
142 | return $items;
143 | }
144 |
145 | /**
146 | * Persists a set of key => value pairs in the cache, with an optional TTL.
147 | *
148 | * @param iterable $values A list of key => value pairs for a multiple-set operation.
149 | * @param DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and
150 | * the driver supports TTL then the library may set a default value
151 | * for it or let the driver take care of that.
152 | *
153 | * @return bool True on success and false on failure.
154 | *
155 | * @throws CacheArgumentException
156 | * MUST be thrown if $values is neither an array nor a Traversable,
157 | * or if any of the $values are not a legal value.
158 | */
159 | public function setMultiple($values, $ttl = null)
160 | {
161 | if (! (is_iterable($values))) {
162 | throw new CacheArgumentException('setMultiple only accepts traversable input.');
163 | }
164 |
165 | // CacheInterface has no spec for multiple item storage
166 | // so we have to power through them individually.
167 | $return = true;
168 |
169 | foreach ($values as $key => $value) {
170 | if (is_int($key)) {
171 | $key = (string) $key;
172 | }
173 | $result = $this->set($key, $value, $ttl);
174 | $return = $result && $return;
175 | }
176 |
177 | return $return;
178 | }
179 |
180 | /**
181 | * Deletes multiple cache items in a single operation.
182 | *
183 | * @param iterable $keys A list of string-based keys to be deleted.
184 | *
185 | * @return bool True if the items were successfully removed. False if there was an error.
186 | *
187 | * @throws CacheArgumentException
188 | * MUST be thrown if $keys is neither an array nor a Traversable,
189 | * or if any of the $keys are not a legal value.
190 | */
191 | public function deleteMultiple($keys)
192 | {
193 | if (! (is_iterable($keys))) {
194 | throw new CacheArgumentException('deleteMultiple only accepts traversable input.');
195 | }
196 |
197 | // CacheInterface has no spec for multiple item removal
198 | // so we have to power through them individually.
199 | $return = true;
200 |
201 | foreach ($keys as $key) {
202 | $result = $this->delete($key);
203 | $return = $result && $return;
204 | }
205 |
206 | return $return;
207 | }
208 |
209 | /**
210 | * Determines whether an item is present in the cache.
211 | *
212 | * NOTE: It is recommended that has() is only to be used for cache warming type purposes
213 | * and not to be used within your live applications operations for get/set, as this method
214 | * is subject to a race condition where your has() will return true and immediately after,
215 | * another script can remove it, making the state of your app out of date.
216 | *
217 | * @param string $key The cache item key.
218 | *
219 | * @return bool
220 | *
221 | * @throws CacheArgumentException
222 | * MUST be thrown if the $key string is not a legal value.
223 | */
224 | public function has($key)
225 | {
226 | Item::validateKey($key);
227 |
228 | $meta = $this->adapter->getMetaData($key);
229 |
230 | // The adapter must return an array that is not expired
231 | return is_array($meta) && is_int($meta['expire']) && $meta['expire'] > time();
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/src/Pool.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace CodeIgniter\Psr\Cache;
13 |
14 | use CodeIgniter\I18n\Time;
15 | use Psr\Cache\CacheItemInterface;
16 | use Psr\Cache\CacheItemPoolInterface;
17 |
18 | final class Pool implements CacheItemPoolInterface
19 | {
20 | use SupportTrait;
21 |
22 | /**
23 | * Deferred Items to be saved.
24 | *
25 | * @var array
26 | */
27 | private array $deferred = [];
28 |
29 | /**
30 | * Commits any deferred Items.
31 | */
32 | public function __destruct()
33 | {
34 | $this->commit();
35 | }
36 |
37 | /**
38 | * Returns a Cache Item representing the specified key.
39 | *
40 | * This method must always return a CacheItemInterface object, even in case of
41 | * a cache miss. It MUST NOT return null.
42 | *
43 | * @param string $key
44 | * The key for which to return the corresponding Cache Item.
45 | *
46 | * @throws CacheArgumentException
47 | * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
48 | * MUST be thrown.
49 | *
50 | * The corresponding Cache Item.
51 | */
52 | public function getItem($key): CacheItemInterface
53 | {
54 | Item::validateKey($key);
55 |
56 | // First check for a deferred Item
57 | if (array_key_exists($key, $this->deferred) && ! $this->deferred[$key]->isExpired()) {
58 | return (clone $this->deferred[$key])->setHit(true);
59 | }
60 |
61 | $meta = $this->adapter->getMetaData($key);
62 |
63 | // If the adapter does not return an array or if the item is expired then it is a miss
64 | if (! is_array($meta) || (is_int($meta['expire']) && $meta['expire'] < time())) {
65 | return new Item($key, null, false);
66 | }
67 |
68 | // Create the Item with the actual value
69 | $item = new Item($key, $this->adapter->get($key), true);
70 |
71 | // Check for an expiration
72 | if ($meta['expire'] !== null) {
73 | $item->expiresAt(Time::createFromTimestamp($meta['expire']));
74 | }
75 |
76 | return $item;
77 | }
78 |
79 | /**
80 | * Returns a traversable set of cache items.
81 | *
82 | * @param list $keys
83 | * An indexed array of keys of items to retrieve.
84 | *
85 | * @throws CacheArgumentException
86 | * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
87 | * MUST be thrown.
88 | *
89 | * A traversable collection of Cache Items keyed by the cache keys of
90 | * each item. A Cache item will be returned for each key, even if that
91 | * key is not found. However, if no keys are specified then an empty
92 | * traversable MUST be returned instead.
93 | */
94 | public function getItems(array $keys = []): array
95 | {
96 | // CacheInterface has no spec for multiple item retrieval
97 | // so we have to power through them individually.
98 | $items = [];
99 |
100 | foreach ($keys as $key) {
101 | $items[$key] = $this->getItem($key);
102 | }
103 |
104 | return $items;
105 | }
106 |
107 | /**
108 | * Confirms if the cache contains specified cache item.
109 | *
110 | * Note: This method MAY avoid retrieving the cached value for performance reasons.
111 | * This could result in a race condition with CacheItemInterface::get(). To avoid
112 | * such situation use CacheItemInterface::isHit() instead.
113 | *
114 | * @param string $key
115 | * The key for which to check existence.
116 | *
117 | * @throws CacheArgumentException
118 | * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
119 | * MUST be thrown.
120 | *
121 | * True if item exists in the cache, false otherwise.
122 | */
123 | public function hasItem($key): bool
124 | {
125 | Item::validateKey($key);
126 |
127 | // First check for a deferred Item
128 | if (array_key_exists($key, $this->deferred) && ! $this->deferred[$key]->isExpired()) {
129 | return true;
130 | }
131 |
132 | return is_array($this->adapter->getMetaData($key));
133 | }
134 |
135 | /**
136 | * Deletes all items in the pool.
137 | *
138 | * @return bool
139 | * True if the pool was successfully cleared. False if there was an error.
140 | */
141 | public function clear()
142 | {
143 | $this->deferred = [];
144 |
145 | return $this->adapter->clean();
146 | }
147 |
148 | /**
149 | * Removes the item from the pool.
150 | *
151 | * @param string $key
152 | * The key to delete.
153 | *
154 | * @throws CacheArgumentException
155 | * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
156 | * MUST be thrown.
157 | *
158 | * True if the item was successfully removed. False if there was an error.
159 | */
160 | public function deleteItem($key): bool
161 | {
162 | Item::validateKey($key);
163 |
164 | // First check for a deferred Item
165 | if (array_key_exists($key, $this->deferred)) {
166 | unset($this->deferred[$key]);
167 |
168 | return true;
169 | }
170 |
171 | if ($this->hasItem($key)) {
172 | return $this->adapter->delete($key);
173 | }
174 |
175 | return true;
176 | }
177 |
178 | /**
179 | * Removes multiple items from the pool.
180 | *
181 | * @param list $keys
182 | * An array of keys that should be removed from the pool.
183 | *
184 | * @throws CacheArgumentException
185 | * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
186 | * MUST be thrown.
187 | *
188 | * True if the items were successfully removed. False if there was an error.
189 | */
190 | public function deleteItems(array $keys): bool
191 | {
192 | // CacheInterface has no spec for multiple item removal
193 | // so we have to power through them individually.
194 | $return = true;
195 |
196 | foreach ($keys as $key) {
197 | $result = $this->deleteItem($key);
198 | $return = $return && $result;
199 | }
200 |
201 | return $return;
202 | }
203 |
204 | /**
205 | * Persists a cache item immediately.
206 | *
207 | * The cache item to save.
208 | *
209 | * @return bool
210 | * True if the item was successfully persisted. False if there was an error.
211 | */
212 | public function save(CacheItemInterface $item)
213 | {
214 | // Only deal in our Pool's Items
215 | if (! $item instanceof Item) {
216 | return false; // @codeCoverageIgnore
217 | }
218 |
219 | // Do not save expired Items
220 | if ($item->isExpired()) {
221 | $this->deleteItem($item->getKey());
222 |
223 | return false;
224 | }
225 |
226 | // Deteremine TTL
227 | if ($expiration = $item->getExpiration()) {
228 | $ttl = Time::now()->difference($expiration)->getSeconds();
229 | } else {
230 | $ttl = config('Cache')->ttl ?? 60;
231 | }
232 |
233 | return $this->adapter->save($item->getKey(), $item->get(), $ttl);
234 | }
235 |
236 | /**
237 | * Sets a cache item to be persisted later.
238 | *
239 | * The cache item to save.
240 | * False if the item could not be queued or if a commit was attempted and failed. True otherwise.
241 | */
242 | public function saveDeferred(CacheItemInterface $item): bool
243 | {
244 | // Only deal in our Pool's Items
245 | if (! $item instanceof Item) {
246 | return false; // @codeCoverageIgnore
247 | }
248 |
249 | // Do not save expired Items
250 | if ($item->isExpired()) {
251 | return false;
252 | }
253 |
254 | $this->deferred[$item->getKey()] = clone $item;
255 |
256 | return true;
257 | }
258 |
259 | /**
260 | * Persists any deferred cache items.
261 | *
262 | * True if all not-yet-saved items were successfully saved or there were none. False otherwise.
263 | */
264 | public function commit(): bool
265 | {
266 | if ($this->deferred === []) {
267 | return true;
268 | }
269 |
270 | $failed = [];
271 |
272 | foreach ($this->deferred as $item) {
273 | if (! $this->save($item)) {
274 | $failed[$item->getKey()] = $item;
275 | }
276 | }
277 |
278 | if ($failed === []) {
279 | return true;
280 | }
281 |
282 | $this->deferred = $failed;
283 |
284 | return false;
285 | }
286 | }
287 |
--------------------------------------------------------------------------------