diff --git a/README.md b/README.md
index cf26e21..9a88fe4 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,111 @@
-# kirby-nitro
+# ⛽️Kirby Nitro
-monkey patches to make kirby faster. use at your own risk.
+![Release](https://flat.badgen.net/packagist/v/bnomei/kirby-nitro?color=ae81ff)
+![Downloads](https://flat.badgen.net/packagist/dt/bnomei/kirby-nitro?color=272822)
+[![Build Status](https://flat.badgen.net/travis/bnomei/kirby-nitro)](https://travis-ci.com/bnomei/kirby-nitro)
+[![Maintainability](https://flat.badgen.net/codeclimate/maintainability/bnomei/kirby-nitro)](https://codeclimate.com/github/bnomei/kirby-nitro)
+[![Twitter](https://flat.badgen.net/badge/twitter/bnomei?color=66d9ef)](https://twitter.com/bnomei)
+
+Nitro speeds up the loading of content in your Kirby project.
+
+## Commercial Usage
+
+>
+> Support open source!
+> This plugin is free but if you use it in a commercial project please consider to sponsor me or make a donation.
+> If my work helped you to make some cash it seems fair to me that I might get a little reward as well, right?
+> Be kind. Share a little. Thanks.
+> ‐ Bruno
+>
+
+| M | O | N | E | Y |
+|------------------------------------------------------|---------------------------------------|-------------------------------------------------|-----------------------------------------------------|----------------------------------------------|
+| [Github sponsor](https://github.com/sponsors/bnomei) | [Patreon](https://patreon.com/bnomei) | [Buy Me a Coffee](https://buymeacoff.ee/bnomei) | [Paypal dontation](https://www.paypal.me/bnomei/15) | [Hire me](mailto:b@bnomei.com?subject=Kirby) |
+
+## Installation
+
+- unzip [master.zip](https://github.com/bnomei/kirby-nitro/archive/master.zip) as folder `site/plugins/kirby-nitro`
+ or
+- `git submodule add https://github.com/bnomei/kirby-nitro.git site/plugins/kirby-nitro` or
+- `composer require bnomei/kirby-nitro`
+
+## Setup
+
+For each template you want to be cached you need to use a model to add the content cache logic using a trait.
+
+**site/models/default.php**
+
+```php
+class DefaultPage extends \Kirby\Cms\Page
+{
+ use \Bnomei\ModelWithNitro;
+}
+```
+
+> [!NOTE]
+> You can also use the trait for user models. File models are patched automatically.
+
+## Using the Cache
+
+You can use the single-file-based cache of nitro to store your own key-value pairs, just like with a regular cache in
+Kirby. Since the Nitro cache is fully loaded with every request I would not advise to store too many big chunks of
+data (like HTML output).
+
+```php
+nitro()->cache()->set('mykey', 'value');
+nitro()->cache()->set('mykey', 'value', 1);
+
+$value = nitro()->cache()->get('mykey');
+$value = nitro()->cache()->getOrSet('mykey', fn() => 'value');
+```
+
+## Using the Cache Driver in Kirby
+
+You can also use the singe-file-based cache of Nitro as a **cache driver** for Kirby. This will allow you to use it for
+caching of other extensions in Kirby. I would highly recommend to use it for Kirby's UUID cache.
+
+**site/config/config.php**
+
+```php
+return [
+ // ... other options
+
+ // use nitro as cache driver for storing uuids
+ // instead of the default file-based cache
+ 'cache' => [
+ 'uuid' => [
+ 'type' => 'nitro',
+ ],
+ ],
+
+ // example: in Lapse plugin
+ 'bnomei.lapse.cache' => [
+ 'type' => 'nitro',
+ ],
+];
+```
+
+## Settings
+
+| bnomei.nitro. | Default | Description |
+|-------------------|-----------------------|------------------------------------------------------------------------------|
+| auto-clean-cache | `true` | will clean the cache once before the first get() |
+| patch-dir-class | always on | monkey-patch the \Kirby\Filesystem\Dir class to use Nitro for caching |
+| patch-files-class | `true` | monkey-patch the \Kirby\CMS\Files class to use Nitro for caching its content |
+| max-dirty-cache | `512` | write every N changes or on destruct |
+| json-encode-flags | `JSON_THROW_ON_ERROR` | |
+| model.read | `true` | read from cache for all models that use the ModelWithNitro trait |
+| model.write | `true` | write to cache for all models that use the ModelWithNitro trait |
+
+## Disclaimer
+
+This plugin is provided "as is" with no guarantee. Use it at your own risk and always test it yourself before using it
+in a production environment. If you find any issues,
+please [create a new issue](https://github.com/bnomei/kirby-nitro/issues/new).
+
+## License
+
+[MIT](https://opensource.org/licenses/MIT)
+
+It is discouraged to use this plugin in any project that promotes racism, sexism, homophobia, animal abuse, violence or
+any other form of hate speech.
diff --git a/classes/Nitro.php b/classes/Nitro.php
index d68b038..104f3fc 100644
--- a/classes/Nitro.php
+++ b/classes/Nitro.php
@@ -23,6 +23,7 @@ public function __construct(array $options = [])
'enabled' => true,
'cacheDir' => realpath(__DIR__.'/../').'/cache',
'cacheType' => 'json',
+ 'json-encode-flags' => JSON_THROW_ON_ERROR,
], $options);
foreach ($this->options as $key => $value) {
diff --git a/classes/Nitro/DirInventory.php b/classes/Nitro/DirInventory.php
index bbc9f97..1f1a00d 100644
--- a/classes/Nitro/DirInventory.php
+++ b/classes/Nitro/DirInventory.php
@@ -18,6 +18,7 @@ class DirInventory
public function __construct(array $options = [])
{
+ // can not use option() as it might run before Kirby is loaded
$this->options = $options;
$this->isDirty = false;
$this->data = [];
@@ -39,7 +40,7 @@ public function __destruct()
opcache_invalidate($file);
}
} else {
- F::write($file, json_encode($this->data));
+ F::write($file, json_encode($this->data, $this->options['json-encode-flags']));
}
}
diff --git a/classes/Nitro/SingleFileCache.php b/classes/Nitro/SingleFileCache.php
index 806ab9d..63d0f50 100644
--- a/classes/Nitro/SingleFileCache.php
+++ b/classes/Nitro/SingleFileCache.php
@@ -21,6 +21,7 @@ public function __construct(array $options = [])
$this->options = array_merge([
'auto-clean-cache' => option('bnomei.nitro.auto-clean-cache'),
+ 'json-encode-flags' => option('bnomei.nitro.json-encode-flags'),
'max-dirty-cache' => (int) option('bnomei.nitro.max-dirty-cache'),
'debug' => option('debug'),
], $options);
@@ -56,6 +57,14 @@ public function set(string $key, $value, int $minutes = 0): bool
*/
$key = $this->key($key);
+
+ // flatten kirby fields
+ $value = $this->serialize($value);
+
+ // make sure the value can be stored as json
+ // if not fail here so a trace is more helpful
+ $value = json_decode(json_encode($value, $this->options['json-encode-flags']), true);
+
$this->data[$key] = (new Value($value, $minutes))->toArray();
$this->isDirty++;
if ($this->isDirty > $this->options['max-dirty-cache']) {
@@ -142,14 +151,43 @@ private function file()
return kirby()->cache('bnomei.nitro.sfc')->root().'/single-file-cache.json';
}
- private function write(): bool
+ public function write(): bool
{
if ($this->isDirty === 0) {
return false;
}
- F::write($this->file(), json_encode($this->data));
+ F::write($this->file(), json_encode($this->data, $this->options['json-encode-flags']));
$this->isDirty = 0;
return true;
}
+
+ private static function isCallable($value): bool
+ {
+ // do not call global helpers just methods or closures
+ return ! is_string($value) && is_callable($value);
+ }
+
+ public function serialize($value)
+ {
+ if (! $value) {
+ return null;
+ }
+ $value = self::isCallable($value) ? $value() : $value;
+
+ if (is_array($value)) {
+ $items = [];
+ foreach ($value as $key => $item) {
+ $items[$key] = $this->serialize($item);
+ }
+
+ return $items;
+ }
+
+ if (is_a($value, 'Kirby\Content\Field')) {
+ return $value->value();
+ }
+
+ return $value;
+ }
}
diff --git a/composer.json b/composer.json
index 4e77e0b..5515f7f 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "bnomei/kirby-nitro",
"type": "kirby-plugin",
"version": "1.0.0",
- "description": "",
+ "description": "Nitro speeds up the loading of content in your Kirby project.",
"license": "MIT",
"authors": [
{
@@ -69,5 +69,10 @@
},
"conflict": {
"bnomei/kirby3-boost": "*"
+ },
+ "suggest": {
+ "bnomei/kirby-blueprints": "PHP Class-based Blueprints for Kirby CMS for better type safety and code completion.",
+ "bnomei/kirby3-lapse": "Cache any data until set expiration time (with automatic keys).",
+ "getkirby/staticache": "Static site performance on demand!"
}
}
diff --git a/index.php b/index.php
index 963627e..b183b47 100644
--- a/index.php
+++ b/index.php
@@ -18,6 +18,7 @@ function nitro(): \Bnomei\Nitro
'patch-files-class' => true,
'auto-clean-cache' => true,
'max-dirty-cache' => 512, // write every N changes or on destruct
+ 'json-encode-flags' => JSON_THROW_ON_ERROR, // | JSON_INVALID_UTF8_IGNORE,
'model' => [
'read' => true,
'write' => true,
@@ -32,8 +33,32 @@ function nitro(): \Bnomei\Nitro
},
'page.*:after' => function ($event, $page) {
if ($event->action() !== 'render') {
- \Bnomei\Nitro::singleton()->flush();
+ \Bnomei\Nitro::singleton()->dir()->flush();
}
},
],
+ 'commands' => [
+ 'nitro:index' => [
+ 'description' => 'Run Nitro Index',
+ 'args' => [],
+ 'command' => static function ($cli): void {
+
+ $kirby = $cli->kirby();
+ $kirby->impersonate('kirby');
+
+ $cli->out('Indexing...');
+ $count = nitro()->modelIndex();
+ $cli->out($count.' models indexed.');
+
+ $cli->success('Done.');
+
+ if (function_exists('janitor')) {
+ janitor()->data([
+ 'status' => 200,
+ 'message' => $count.' models indexed.',
+ ]);
+ }
+ },
+ ],
+ ],
]);
diff --git a/tests/NitroTest.php b/tests/NitroTest.php
index 0236f3f..3fa020a 100644
--- a/tests/NitroTest.php
+++ b/tests/NitroTest.php
@@ -19,6 +19,26 @@
expect($value)->toBe('value');
});
+it('will serialize kirby\content\fields to their value', function () {
+ $cache = nitro()->cache();
+ $cache->set('home.title', page('home')->title());
+ $value = $cache->get('home.title');
+
+ expect($value)->toBe('Home');
+});
+
+it('will serialize objects if they support it', function () {
+ $cache = nitro()->cache();
+ $cache->set('object', new \Kirby\Toolkit\Obj([
+ 'hello' => 'world',
+ ]));
+ $value = $cache->get('object');
+
+ expect($value)->toBe([
+ 'hello' => 'world',
+ ]);
+});
+
it('will not use the cache in debug mode', function () {
Nitro::$singleton = null;