Code Monkey home page Code Monkey logo

kirby3-lapse's Introduction

Kirby3 Lapse

Release Downloads Build Status Coverage Status Maintainability Twitter

Cache any data until set expiration time (with automatic keys).

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 Patreon Buy Me a Coffee Paypal dontation Hire me

Installation

  • unzip master.zip as folder site/plugins/kirby3-lapse or
  • git submodule add https://github.com/bnomei/kirby3-lapse.git site/plugins/kirby3-lapse or
  • composer require bnomei/kirby3-lapse

Usecase

The Kirby Pages-Cache can cache the output of Page Templates. It devalidates all cache files if any Object in the Panel is changed. This is a good choice if you do not make changes often. But if you do make changes often you need a cache that knows what has been modified and which caches to devalidate and which not.

Sometimes you can not cache the complete Page since...

  • it contains a form with a csrf hash,
  • you use content security headers with nouces,
  • build and cache data for a logicless templating system like handlebars or
  • you want to cache data from an external source like an API.

Lapse was build to do exactly that: Cache any data until set expiration time.

Usage Examples

Example 1: get/set

$key = hash('xxh3', $page->url()); // unique key
// to delay data creation until need use a callback. do not use a plain array or object.
$data = function () {
    return [1, 2, 3];
};
$data = lapse($key, $data);

Example 2: with custom expiration time

$key = hash('xxh3', $page->url()); // unique key
$expires = 5; // in minutes. default: 0 aka infinite
$data = function () {
    return [1, 2, 3];
};
$data = lapse($key, $data, $expires);

Example 3: page object

$data = lapse(hash('xxh3', $page->url()), function () use ($kirby, $site, $page) {
    // create some data
    return [
        'author' => site()->author(),
        'title' => $page->title(),
        'text' => $page->text()->kirbytext(),
        'url' => $page->url(),
    ];
});

Remove by Key

$key = hash('xxh3', $page->url()); // unique key
$wasRemoved = \Bnomei\Lapse::rm($key);

Static Cache helper - lapseStatic()

Sometimes you need to cache things only for the current request like when reusing Kirbys Collections . The lapseStatic()-helper makes things like that a bit easier. The closure to generate the data will only be called once, set its return value to a static cache and every recurring call to the collection will get the cached collection back from static memory array.

site/plugins/example/index.php

<?php

Kirby::plugin('bnomei/example', [
  'collections' => [
    // collections have to be a closure that is why it is wrapped in a fn
    'recent-courses' => fn() => lapseStatic(
        'recent-courses', // key 
        function () { // value
            return page('courses')->children()->listed()->sortBy('name')->limit(10);
        }
    )
]);

Clever keys

Unique but not modified

Caches use a string value as key to store and later retrieve the data. The key is usually a hash of the objects id plus some meta data like the contents language. Storing data related to a Page using the $key = hash('xxh3', $page->url()); will work just fine. It takes care of the language if you use a multi-language setup since the language is included in the url. But it will expire only if you provide a fixed time or devalidate it yourself.

Modified

The solution is to include the modification timestamp of every object related to the data. So if you store the result of a Page Object with Images being rendered you need to include the modification timestamp of all of these. That will cause the creation of a new cache every time your source changes.

Basic

$keys = [ $page->url().$page->modified() ];
foreach($page->images() as $image) {
    $keys[] = $image->id().$image->modified();
}
$key = hash('xxh3', implode($keys));

Objects

Since version 2 of this plugin you can also forward any of these and the key will be magically created for you.

  • Page-Objects,
  • File-Objects and FileVersion-Objects (aka Thumbs),
  • Collections or
  • the Site-Object
$objects = [$page, $page->images()];
$data = lapse($objects, ...)

Multi-language support

The keys created by the plugin are tagged with the current language. You will get a different cache value for each language.

AutoID or BoostID

If you use the AutoID plugin or Boost plugin the modification timestamps will be retrieved at almost zero-cpu-cost and not causing the file to be checked on disk.

FAQ

Infinite cache duration by default

Unless you set an expiration when using lapse() the cache file will never devalidate. This is because the plugin is intended to be used with keys defining the expiration like $key = hash('xxh3', $page->id().$page->modified());.

$expires = 5; // in minutes. default: 0 aka infinite
$data = lapse($key, $data, $expires);

When using Memcache or APCu you need to limit the maximum number of caches created since you have a very limited amount if memory of 64MB at default. You can set a limit at bnomei.lapse.indexLimit to something like 300. But be aware that this makes writing to the cache a tiny bit slower since the plugins internal index must be updated.

No cache when debugging

When Kirbys global debug config is set to true the complete plugin cache will be flushed and no caches will be created. This will make you live easier – trust me.

Kirby Field-Objects and serialization

The plugin uses the default Kirby serialization of objects and since memory references are lost anyway all Kirby Field-Objects are stored by calling their ->value() method. The File-Cache uses a json format.

Migrating from v1 of this plugin

  • $force param has been removed: use proper keys.
  • all settings have been removed: they are not needed anymore like explained above.

Performance

Use crc32 to generate the hash

xxh3 is the fastest non cryptographic hashing hashing algorithm in PHP 8.1 . The keys for lapse do not need to be encrypted.

Cache Driver

For best performance set the global cache driver to one using the servers memory not files on the harddisk (even on SSDs). Memcache or ApcuCache can be activated on most hosting environments but rarely are by default. Also see bnomei.lapse.indexLimit setting explained above. My Redis Cache Driver and SQLite Cache Driver are faster than other cache drivers and have no memory limit. In all other cases use the very fast PHP Cache Driver or APCu.

return [
  'cache' => [
    'driver' => 'apcu', // php, redis, sqlite
  ],
];

Examples

// lapse v1 could already do this:
// store data until objects are modified with optional expire
$data = lapse(
    $page->id().$page->modified(),
    ['some', 'kind', 'of', 'data'],
    60*24*7 // one week
);

// now it can create magic cache keys from kirby objects
$data = lapse(
    $page, // key with modification date created by lapse based on object 
    function () use ($page) {
        return [
           'title' => $page->title(),
       ];
    }
);

// or from an collection of pages or files
$collection = collection('mycollection');
$data = lapse(
    $collection, // this will turn into a key taking modification date of all objects into consideration
    function () use ($collection) {
        return [ /*...*/ ];
    }
);

// or from an array combining all these
$data = lapse(
    ['myhash', $page, $page->children()->images(), $collection, $site], // will create key from array of objects
    function () use ($site, $page, $collection) {
        return [
            // will not break serialization => automatically store ->value() of fields
            'author' => $site->author(),
            'title' => $page->title(),
            'hero' => $page->children()->images()->first()->srcset()->html(),
            'count' => $collection->count(),
        ];
    }
);

// remove by dynamic key
$wasRemoved = \Bnomei\Lapse::rm(
    ['myhash', $page, $page->children()->images(), $collection, $site]
);

🏎️ if you use autoid or boost the modified lookups will be at almost zero-cpu cost.

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.

License

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.

Credits

based on V2 version of

kirby3-lapse's People

Contributors

bnomei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

kirby3-lapse's Issues

Flood Protection

There is no way in preventing flooding the cache if setup wrong, right?
This is a dumb example, but should get across the point

$key = crc32($feedurl.uniqid());

This would fill the cache with each request.
And there is not really a way to flush the cache for that specific "domain", once the $key is not known anymore

Here is another example that illustrates it better:

// there are better ways to check if remote content changed and it's questionable if HEAD is really faster, than a single full GET
$request = Remote::request($feedurl, ['method' => 'HEAD']);
// get content length to determent if content changed
$content_length = A::get($request->headers(), 'content-length');

$pages = lapse(crc32($feedurl . $content_length), function () use ($results, $pages, $feedurl) {
      $request = Remote::get($feedurl);
      $results = Xml::parse($request->content());
	  if (count($results) > 0) {
		    foreach ($results as $item) {
			    $item = $item->toArray();
			    $pages[] = [];
		    }
	  }
	  return $pages;
});
Pages::factory($pages, $this);

Now if I want to clear the cache, I have to delete and rebuild the complete cache,

I've seen caching mechanism that implemented caching groups, in order to have granular control over what to flush.

In my example, I could have a cache group by $feedurl and single entries of content-length or modifiedDate whatever. And then be able to say - flush all cache entries of group $feedurl

here is some pseudocode:

lapse([crc32($feedurl), $content_length], $data)
// flush all with key crc32($feedurl);
$wasRemoved = \Bnomei\Lapse::rm([crc32($feedurl)]);

Would it make sense to implement it that way?

Can you explain how to use Lapse for partly page caching

Hi there,
As I understand this plugin is multifunctional and useful for many tasks.
I have problem with my site.
I have many GeoIP dependent parts that is changed based on IP (geo) from what user visit site.
The best way is to cache all pages with country as a key, so, for example, I will have a page
news.hungary.cache
news.netherlands.cache
that will have different content

or if this isn't possible explain how for example I can create cache for header and footer (that is similar for all countries).
also, I'm using builder plugin and every block also has Geo dependency, so if it possible to cache it depending on country.

Thanks, for help :) and trust that impossible possible)

Php 8.0 support

Hey me again!

Thank you for updating the boost plugin the other day.

I'm using the boost plugin with Lapse and now I'm facing the issue of platform requirement from the Lapse plugin.

Root composer.json requires bnomei/kirby3-lapse ^3.0 -> satisfiable by bnomei/kirby3-lapse[3.0.0, 3.1.0].
    - bnomei/kirby3-lapse[3.0.0, ..., 3.1.0] require php >=8.1.0 -> your php version (8.0.25)

Any advice would be appreciated!

not working

Hey there,
as I'd like to use this plugin, I don't seem to be able to make it work as expected. i got the following setup to try caching some results, fetched from my package.json:

Controller:

<?php

use Bnomei\Lapse;

return function ($kirby, $page) {

    $key = crc32($page->url());

    $expires = 5;

    $data = function () use ($kirby) {
        $file = F::read($kirby->root('base') . '/package.json');
        $content = json_decode($file, true);

        return $content['dependencies'];
    };

    $data = Lapse::io($key, $data, $expires);

    return compact(
        'data',
    );
};

Template:

<ul>
    <?php foreach ($shit as $library => $version) : ?>
    <li><?= $library ?> is version <?= $version ?></li>
    <?php endforeach ?>
</ul>

.. but changing version numbers immediately gets shown where I expected a cached version to be delivered. Or maybe I got the concept wrong?

Any pointer in the right direction is highly appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.