From e8641d822e520c31eee8e3b7254ad4e0e2d6723e Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Tue, 5 Dec 2023 22:12:13 +0100
Subject: [PATCH 01/12] CacheExtension, FileStorage: checks whether directory
 is absolute

---
 src/Bridges/CacheDI/CacheExtension.php     | 15 ++++++++-------
 src/Caching/Storages/FileStorage.php       |  4 ++--
 tests/Storages/FileStorage.exceptions.phpt |  2 +-
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/Bridges/CacheDI/CacheExtension.php b/src/Bridges/CacheDI/CacheExtension.php
index a221646d..7acb7d26 100644
--- a/src/Bridges/CacheDI/CacheExtension.php
+++ b/src/Bridges/CacheDI/CacheExtension.php
@@ -10,6 +10,7 @@
 namespace Nette\Bridges\CacheDI;
 
 use Nette;
+use Nette\Utils\FileSystem;
 
 
 /**
@@ -17,19 +18,19 @@
  */
 final class CacheExtension extends Nette\DI\CompilerExtension
 {
-	private string $tempDir;
-
-
-	public function __construct(string $tempDir)
-	{
-		$this->tempDir = $tempDir;
+	public function __construct(
+		private string $tempDir,
+	) {
 	}
 
 
 	public function loadConfiguration()
 	{
+		if (!FileSystem::isAbsolute($this->tempDir)) {
+			throw new Nette\InvalidArgumentException("Cache directory must be absolute, '$this->tempDir' given.");
+		}
 		$dir = $this->tempDir . '/cache';
-		Nette\Utils\FileSystem::createDir($dir);
+		FileSystem::createDir($dir);
 		if (!is_writable($dir)) {
 			throw new Nette\InvalidStateException("Make directory '$dir' writable.");
 		}
diff --git a/src/Caching/Storages/FileStorage.php b/src/Caching/Storages/FileStorage.php
index d0e48c6c..2c24d5bb 100644
--- a/src/Caching/Storages/FileStorage.php
+++ b/src/Caching/Storages/FileStorage.php
@@ -57,8 +57,8 @@ class FileStorage implements Nette\Caching\Storage
 
 	public function __construct(string $dir, ?Journal $journal = null)
 	{
-		if (!is_dir($dir)) {
-			throw new Nette\DirectoryNotFoundException("Directory '$dir' not found.");
+		if (!is_dir($dir) || !Nette\Utils\FileSystem::isAbsolute($dir)) {
+			throw new Nette\DirectoryNotFoundException("Directory '$dir' not found or is not absolute.");
 		}
 
 		$this->dir = $dir;
diff --git a/tests/Storages/FileStorage.exceptions.phpt b/tests/Storages/FileStorage.exceptions.phpt
index e3d91059..9af92cd3 100644
--- a/tests/Storages/FileStorage.exceptions.phpt
+++ b/tests/Storages/FileStorage.exceptions.phpt
@@ -17,7 +17,7 @@ require __DIR__ . '/../bootstrap.php';
 Assert::exception(
 	fn() => new FileStorage(getTempDir() . '/missing'),
 	Nette\DirectoryNotFoundException::class,
-	"Directory '%a%' not found.",
+	"Directory '%a%' not found or is not absolute.",
 );
 
 

From 21c2b5f4e71e2d23134789d3fb132fe4be5bde6b Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Wed, 8 Nov 2023 22:39:40 +0100
Subject: [PATCH 02/12] Latte: sets Tag::$node

---
 composer.json                              | 5 ++++-
 src/Bridges/CacheLatte/Nodes/CacheNode.php | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 8b5b7d84..c1bd8179 100644
--- a/composer.json
+++ b/composer.json
@@ -22,10 +22,13 @@
 	"require-dev": {
 		"nette/tester": "^2.4",
 		"nette/di": "^3.1 || ^4.0",
-		"latte/latte": "^2.11 || ^3.0",
+		"latte/latte": "^2.11 || ^3.0.12",
 		"tracy/tracy": "^2.9",
 		"phpstan/phpstan": "^1.0"
 	},
+	"conflict": {
+		"latte/latte": ">=3.0.0 <3.0.12"
+	},
 	"suggest": {
 		"ext-pdo_sqlite": "to use SQLiteStorage or SQLiteJournal"
 	},
diff --git a/src/Bridges/CacheLatte/Nodes/CacheNode.php b/src/Bridges/CacheLatte/Nodes/CacheNode.php
index 02e029ff..5e41a7e1 100644
--- a/src/Bridges/CacheLatte/Nodes/CacheNode.php
+++ b/src/Bridges/CacheLatte/Nodes/CacheNode.php
@@ -30,7 +30,7 @@ class CacheNode extends StatementNode
 	/** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
 	public static function create(Tag $tag): \Generator
 	{
-		$node = new static;
+		$node = $tag->node = new static;
 		$node->args = $tag->parser->parseArguments();
 		[$node->content, $endTag] = yield;
 		$node->endLine = $endTag?->position;

From 4217032412fc6336c3b613b249785f657e984ec8 Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Tue, 29 Aug 2023 13:52:27 +0200
Subject: [PATCH 03/12] removed Nette\SmartObject

---
 src/Caching/Cache.php                     | 2 --
 src/Caching/OutputHelper.php              | 2 --
 src/Caching/Storages/DevNullStorage.php   | 2 --
 src/Caching/Storages/FileStorage.php      | 2 --
 src/Caching/Storages/MemcachedStorage.php | 2 --
 src/Caching/Storages/MemoryStorage.php    | 2 --
 src/Caching/Storages/SQLiteJournal.php    | 2 --
 src/Caching/Storages/SQLiteStorage.php    | 2 --
 8 files changed, 16 deletions(-)

diff --git a/src/Caching/Cache.php b/src/Caching/Cache.php
index b9ee6dba..eda389e3 100644
--- a/src/Caching/Cache.php
+++ b/src/Caching/Cache.php
@@ -17,8 +17,6 @@
  */
 class Cache
 {
-	use Nette\SmartObject;
-
 	/** dependency */
 	public const
 		Priority = 'priority',
diff --git a/src/Caching/OutputHelper.php b/src/Caching/OutputHelper.php
index 5019c24d..2256a9bb 100644
--- a/src/Caching/OutputHelper.php
+++ b/src/Caching/OutputHelper.php
@@ -17,8 +17,6 @@
  */
 class OutputHelper
 {
-	use Nette\SmartObject;
-
 	public array $dependencies = [];
 	private ?Cache $cache;
 	private mixed $key;
diff --git a/src/Caching/Storages/DevNullStorage.php b/src/Caching/Storages/DevNullStorage.php
index 3106a212..aceea163 100644
--- a/src/Caching/Storages/DevNullStorage.php
+++ b/src/Caching/Storages/DevNullStorage.php
@@ -17,8 +17,6 @@
  */
 class DevNullStorage implements Nette\Caching\Storage
 {
-	use Nette\SmartObject;
-
 	public function read(string $key): mixed
 	{
 		return null;
diff --git a/src/Caching/Storages/FileStorage.php b/src/Caching/Storages/FileStorage.php
index 2c24d5bb..f0400135 100644
--- a/src/Caching/Storages/FileStorage.php
+++ b/src/Caching/Storages/FileStorage.php
@@ -18,8 +18,6 @@
  */
 class FileStorage implements Nette\Caching\Storage
 {
-	use Nette\SmartObject;
-
 	/**
 	 * Atomic thread safe logic:
 	 *
diff --git a/src/Caching/Storages/MemcachedStorage.php b/src/Caching/Storages/MemcachedStorage.php
index e59582ee..03af23b3 100644
--- a/src/Caching/Storages/MemcachedStorage.php
+++ b/src/Caching/Storages/MemcachedStorage.php
@@ -18,8 +18,6 @@
  */
 class MemcachedStorage implements Nette\Caching\Storage, Nette\Caching\BulkReader
 {
-	use Nette\SmartObject;
-
 	/** @internal cache structure */
 	private const
 		MetaCallbacks = 'callbacks',
diff --git a/src/Caching/Storages/MemoryStorage.php b/src/Caching/Storages/MemoryStorage.php
index 9f92d772..55abc051 100644
--- a/src/Caching/Storages/MemoryStorage.php
+++ b/src/Caching/Storages/MemoryStorage.php
@@ -17,8 +17,6 @@
  */
 class MemoryStorage implements Nette\Caching\Storage
 {
-	use Nette\SmartObject;
-
 	private array $data = [];
 
 
diff --git a/src/Caching/Storages/SQLiteJournal.php b/src/Caching/Storages/SQLiteJournal.php
index b3be748a..26747834 100644
--- a/src/Caching/Storages/SQLiteJournal.php
+++ b/src/Caching/Storages/SQLiteJournal.php
@@ -18,8 +18,6 @@
  */
 class SQLiteJournal implements Journal
 {
-	use Nette\SmartObject;
-
 	/** @string */
 	private $path;
 	private \PDO $pdo;
diff --git a/src/Caching/Storages/SQLiteStorage.php b/src/Caching/Storages/SQLiteStorage.php
index 4baf770a..5301a4e7 100644
--- a/src/Caching/Storages/SQLiteStorage.php
+++ b/src/Caching/Storages/SQLiteStorage.php
@@ -18,8 +18,6 @@
  */
 class SQLiteStorage implements Nette\Caching\Storage, Nette\Caching\BulkReader
 {
-	use Nette\SmartObject;
-
 	private \PDO $pdo;
 
 

From 559874b7189aec399d6eaa884893d75f4c155b4e Mon Sep 17 00:00:00 2001
From: g-battaglia <battaglia.giacomo@icloud.com>
Date: Wed, 11 Oct 2023 15:46:46 +0200
Subject: [PATCH 04/12] typo

---
 src/Bridges/CacheDI/CacheExtension.php | 2 +-
 src/Caching/Cache.php                  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Bridges/CacheDI/CacheExtension.php b/src/Bridges/CacheDI/CacheExtension.php
index 7acb7d26..05b41ae7 100644
--- a/src/Bridges/CacheDI/CacheExtension.php
+++ b/src/Bridges/CacheDI/CacheExtension.php
@@ -24,7 +24,7 @@ public function __construct(
 	}
 
 
-	public function loadConfiguration()
+	public function loadConfiguration(): void
 	{
 		if (!FileSystem::isAbsolute($this->tempDir)) {
 			throw new Nette\InvalidArgumentException("Cache directory must be absolute, '$this->tempDir' given.");
diff --git a/src/Caching/Cache.php b/src/Caching/Cache.php
index eda389e3..69ff331f 100644
--- a/src/Caching/Cache.php
+++ b/src/Caching/Cache.php
@@ -179,7 +179,7 @@ public function bulkLoad(array $keys, ?callable $generator = null): array
 	 * Writes item into the cache.
 	 * Dependencies are:
 	 * - Cache::Priority => (int) priority
-	 * - Cache::Expire => (timestamp) expiration
+	 * - Cache::Expire => (timestamp) expiration, infinite if null
 	 * - Cache::Sliding => (bool) use sliding expiration?
 	 * - Cache::Tags => (array) tags
 	 * - Cache::Files => (array|string) file names

From 9a09321d468f0851d0a1ed4f7069b508289d53cb Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Mon, 1 Mar 2021 15:27:42 +0100
Subject: [PATCH 05/12] opened 4.0-dev

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index c1bd8179..4f1abbba 100644
--- a/composer.json
+++ b/composer.json
@@ -42,7 +42,7 @@
 	},
 	"extra": {
 		"branch-alias": {
-			"dev-master": "3.2-dev"
+			"dev-master": "4.0-dev"
 		}
 	}
 }

From bf8b8d2c073f48e40020b65725cfd21f999542b4 Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Fri, 13 Jan 2023 04:41:49 +0100
Subject: [PATCH 06/12] composer: updated dependencies

---
 composer.json | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 4f1abbba..546caccc 100644
--- a/composer.json
+++ b/composer.json
@@ -16,8 +16,7 @@
 	],
 	"require": {
 		"php": "8.0 - 8.3",
-		"nette/finder": "^2.4 || ^3.0",
-		"nette/utils": "^3.2 || ~4.0.0"
+		"nette/utils": "^4.0"
 	},
 	"require-dev": {
 		"nette/tester": "^2.4",

From 80ae66acb4141ba848d056b1f4fec1e02b558c6a Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Fri, 13 Jan 2023 04:41:43 +0100
Subject: [PATCH 07/12] Storage::read() added return typehint (BC break)

---
 src/Caching/Storage.php | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/Caching/Storage.php b/src/Caching/Storage.php
index e9565d8b..16b80942 100644
--- a/src/Caching/Storage.php
+++ b/src/Caching/Storage.php
@@ -17,9 +17,8 @@ interface Storage
 {
 	/**
 	 * Read from cache.
-	 * @return mixed
 	 */
-	function read(string $key);
+	function read(string $key): mixed;
 
 	/**
 	 * Prevents item reading and writing. Lock is released by write() or remove().

From 40dfe7a0392c7abd8b48e112d7cb8c094b38d3d2 Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Fri, 13 Jan 2023 04:48:57 +0100
Subject: [PATCH 08/12] deprecated stuff

---
 src/Caching/Cache.php | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/Caching/Cache.php b/src/Caching/Cache.php
index 69ff331f..245836b5 100644
--- a/src/Caching/Cache.php
+++ b/src/Caching/Cache.php
@@ -64,9 +64,7 @@ class Cache
 	public const ALL = self::All;
 
 	/** @internal */
-	public const
-		NamespaceSeparator = "\x00",
-		NAMESPACE_SEPARATOR = self::NamespaceSeparator;
+	public const NamespaceSeparator = "\x00";
 
 	private Storage $storage;
 	private string $namespace;
@@ -346,6 +344,7 @@ public function capture(mixed $key): ?OutputHelper
 	 */
 	public function start($key): ?OutputHelper
 	{
+		trigger_error(__METHOD__ . '() was renamed to capture()', E_USER_DEPRECATED);
 		return $this->capture($key);
 	}
 

From 5080f06f33a96c762df55e1c659f237189c9efe0 Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Mon, 14 Aug 2023 21:27:06 +0200
Subject: [PATCH 09/12] removed support for Latte 2

---
 composer.json                                 |   4 +-
 src/Bridges/CacheLatte/CacheMacro.php         | 168 ------------------
 tests/Bridges.Latte2/CacheMacro.cache.phpt    |  48 -----
 .../CacheMacro.createCache.phpt               |  49 -----
 .../expected/CacheMacro.cache.html            |   8 -
 .../expected/CacheMacro.cache.inc.php         |  12 --
 .../expected/CacheMacro.cache.php             |  28 ---
 tests/Bridges.Latte2/templates/cache.latte    |   9 -
 .../templates/include.cache.latte             |   5 -
 tests/Bridges.Latte3/Runtime.phpt             |   4 -
 tests/Bridges.Latte3/{cache}.phpt             |   4 -
 11 files changed, 2 insertions(+), 337 deletions(-)
 delete mode 100644 src/Bridges/CacheLatte/CacheMacro.php
 delete mode 100644 tests/Bridges.Latte2/CacheMacro.cache.phpt
 delete mode 100644 tests/Bridges.Latte2/CacheMacro.createCache.phpt
 delete mode 100644 tests/Bridges.Latte2/expected/CacheMacro.cache.html
 delete mode 100644 tests/Bridges.Latte2/expected/CacheMacro.cache.inc.php
 delete mode 100644 tests/Bridges.Latte2/expected/CacheMacro.cache.php
 delete mode 100644 tests/Bridges.Latte2/templates/cache.latte
 delete mode 100644 tests/Bridges.Latte2/templates/include.cache.latte

diff --git a/composer.json b/composer.json
index 546caccc..fddef604 100644
--- a/composer.json
+++ b/composer.json
@@ -21,12 +21,12 @@
 	"require-dev": {
 		"nette/tester": "^2.4",
 		"nette/di": "^3.1 || ^4.0",
-		"latte/latte": "^2.11 || ^3.0.12",
+		"latte/latte": "^3.0.12",
 		"tracy/tracy": "^2.9",
 		"phpstan/phpstan": "^1.0"
 	},
 	"conflict": {
-		"latte/latte": ">=3.0.0 <3.0.12"
+		"latte/latte": "<3.0.12"
 	},
 	"suggest": {
 		"ext-pdo_sqlite": "to use SQLiteStorage or SQLiteJournal"
diff --git a/src/Bridges/CacheLatte/CacheMacro.php b/src/Bridges/CacheLatte/CacheMacro.php
deleted file mode 100644
index d6aeb5db..00000000
--- a/src/Bridges/CacheLatte/CacheMacro.php
+++ /dev/null
@@ -1,168 +0,0 @@
-<?php
-
-/**
- * This file is part of the Nette Framework (https://nette.org)
- * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
- */
-
-declare(strict_types=1);
-
-namespace Nette\Bridges\CacheLatte;
-
-use Latte;
-use Nette;
-use Nette\Caching\Cache;
-
-
-/**
- * Macro {cache} ... {/cache} for Latte v2
- */
-final class CacheMacro implements Latte\IMacro
-{
-	use Nette\SmartObject;
-
-	private bool $used;
-
-
-	/**
-	 * Initializes before template parsing.
-	 * @return void
-	 */
-	public function initialize()
-	{
-		$this->used = false;
-	}
-
-
-	/**
-	 * Finishes template parsing.
-	 * @return array(prolog, epilog)
-	 */
-	public function finalize()
-	{
-		if ($this->used) {
-			return ['Nette\Bridges\CacheLatte\CacheMacro::initRuntime($this);'];
-		}
-	}
-
-
-	/**
-	 * New node is found.
-	 * @return bool
-	 */
-	public function nodeOpened(Latte\MacroNode $node)
-	{
-		if ($node->modifiers) {
-			throw new Latte\CompileException('Modifiers are not allowed in ' . $node->getNotation());
-		}
-
-		$this->used = true;
-		$node->empty = false;
-		$node->openingCode = Latte\PhpWriter::using($node)
-			->write(
-				'<?php if (Nette\Bridges\CacheLatte\CacheMacro::createCache($this->global->cacheStorage, %var, $this->global->cacheStack, %node.array?)) /* line %var */ try { ?>',
-				Nette\Utils\Random::generate(),
-				$node->startLine,
-			);
-	}
-
-
-	/**
-	 * Node is closed.
-	 * @return void
-	 */
-	public function nodeClosed(Latte\MacroNode $node)
-	{
-		$node->closingCode = Latte\PhpWriter::using($node)
-			->write(
-				'<?php
-				Nette\Bridges\CacheLatte\CacheMacro::endCache($this->global->cacheStack, %node.array?) /* line %var */;
-				} catch (\Throwable $ʟ_e) {
-					Nette\Bridges\CacheLatte\CacheMacro::rollback($this->global->cacheStack); throw $ʟ_e;
-				} ?>',
-				$node->startLine,
-			);
-	}
-
-
-	/********************* run-time helpers ****************d*g**/
-
-
-	public static function initRuntime(Latte\Runtime\Template $template): void
-	{
-		if (!empty($template->global->cacheStack)) {
-			$file = (new \ReflectionClass($template))->getFileName();
-			if (@is_file($file)) { // @ - may trigger error
-				end($template->global->cacheStack)->dependencies[Cache::Files][] = $file;
-			}
-		}
-	}
-
-
-	/**
-	 * Starts the output cache. Returns Nette\Caching\OutputHelper object if buffering was started.
-	 */
-	public static function createCache(
-		Nette\Caching\Storage $cacheStorage,
-		string $key,
-		?array &$parents,
-		?array $args = null,
-	): Nette\Caching\OutputHelper|\stdClass|null
-	{
-		if ($args) {
-			if (array_key_exists('if', $args) && !$args['if']) {
-				return $parents[] = new \stdClass;
-			}
-
-			$key = array_merge([$key], array_intersect_key($args, range(0, count($args))));
-		}
-
-		if ($parents) {
-			end($parents)->dependencies[Cache::Items][] = $key;
-		}
-
-		$cache = new Cache($cacheStorage, 'Nette.Templating.Cache');
-		if ($helper = $cache->capture($key)) {
-			$parents[] = $helper;
-		}
-
-		return $helper;
-	}
-
-
-	/**
-	 * Ends the output cache.
-	 * @param  Nette\Caching\OutputHelper[]  $parents
-	 */
-	public static function endCache(array &$parents, ?array $args = null): void
-	{
-		$helper = array_pop($parents);
-		if (!$helper instanceof Nette\Caching\OutputHelper) {
-			return;
-		}
-
-		if (isset($args['dependencies'])) {
-			$args += $args['dependencies']();
-		}
-
-		if (isset($args['expire'])) {
-			$args['expiration'] = $args['expire']; // back compatibility
-		}
-
-		$helper->dependencies[Cache::Tags] = $args['tags'] ?? null;
-		$helper->dependencies[Cache::Expire] = $args['expiration'] ?? '+ 7 days';
-		$helper->end();
-	}
-
-
-	/**
-	 * @param  Nette\Caching\OutputHelper[]  $parents
-	 */
-	public static function rollback(array &$parents): void
-	{
-		$helper = array_pop($parents);
-		if ($helper instanceof Nette\Caching\OutputHelper) {
-			$helper->rollback();
-		}
-	}
-}
diff --git a/tests/Bridges.Latte2/CacheMacro.cache.phpt b/tests/Bridges.Latte2/CacheMacro.cache.phpt
deleted file mode 100644
index 8dfa099f..00000000
--- a/tests/Bridges.Latte2/CacheMacro.cache.phpt
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Test: {cache ...}
- */
-
-declare(strict_types=1);
-
-use Nette\Bridges\CacheLatte\CacheMacro;
-use Tester\Assert;
-
-require __DIR__ . '/../bootstrap.php';
-
-if (version_compare(Latte\Engine::VERSION, '3', '>')) {
-	Tester\Environment::skip('Test for Latte 2');
-}
-
-
-$latte = new Latte\Engine;
-$latte->setTempDirectory(getTempDir());
-$latte->addMacro('cache', new CacheMacro($latte->getCompiler()));
-$latte->addProvider('cacheStorage', new Nette\Caching\Storages\MemoryStorage);
-
-$params['title'] = 'Hello';
-$params['id'] = 456;
-
-Assert::matchFile(
-	__DIR__ . '/expected/CacheMacro.cache.php',
-	$latte->compile(__DIR__ . '/templates/cache.latte'),
-);
-Assert::matchFile(
-	__DIR__ . '/expected/CacheMacro.cache.html',
-	$latte->renderToString(
-		__DIR__ . '/templates/cache.latte',
-		$params,
-	),
-);
-Assert::matchFile(
-	__DIR__ . '/expected/CacheMacro.cache.html',
-	$latte->renderToString(
-		__DIR__ . '/templates/cache.latte',
-		$params,
-	),
-);
-Assert::matchFile(
-	__DIR__ . '/expected/CacheMacro.cache.inc.php',
-	file_get_contents($latte->getCacheFile(__DIR__ . strtr('/templates/include.cache.latte', '/', DIRECTORY_SEPARATOR))),
-);
diff --git a/tests/Bridges.Latte2/CacheMacro.createCache.phpt b/tests/Bridges.Latte2/CacheMacro.createCache.phpt
deleted file mode 100644
index 5cd26089..00000000
--- a/tests/Bridges.Latte2/CacheMacro.createCache.phpt
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * Test: Nette\Bridges\CacheLatte\CacheMacro createCache()
- */
-
-declare(strict_types=1);
-
-use Nette\Bridges\CacheLatte\CacheMacro;
-use Nette\Caching\Cache;
-use Nette\Caching\Storages\DevNullStorage;
-use Tester\Assert;
-
-require __DIR__ . '/../bootstrap.php';
-
-if (version_compare(Latte\Engine::VERSION, '3', '>')) {
-	Tester\Environment::skip('Test for Latte 2');
-}
-
-
-test('', function () {
-	$parents = [];
-	$dp = [Cache::Tags => ['rum', 'cola']];
-	$outputHelper = CacheMacro::createCache(new DevNullStorage, 'test', $parents);
-	Assert::type(Nette\Caching\OutputHelper::class, $outputHelper);
-	CacheMacro::endCache($parents, $dp);
-	Assert::same($dp + [Cache::Expire => '+ 7 days'], $outputHelper->dependencies);
-});
-
-test('', function () {
-	$parents = [];
-	$dp = [Cache::Tags => ['rum', 'cola']];
-	$dpFallback = fn() => $dp;
-	$outputHelper = CacheMacro::createCache(new DevNullStorage, 'test', $parents);
-	CacheMacro::endCache($parents, ['dependencies' => $dpFallback]);
-	Assert::same($dp + [Cache::Expire => '+ 7 days'], $outputHelper->dependencies);
-});
-
-test('', function () {
-	$parents = [];
-	$dp = [
-		Cache::Tags => ['rum', 'cola'],
-		Cache::Expire => '+ 1 days',
-	];
-	$dpFallback = fn() => $dp;
-	$outputHelper = CacheMacro::createCache(new DevNullStorage, 'test', $parents);
-	CacheMacro::endCache($parents, ['dependencies' => $dpFallback]);
-	Assert::same($dp, $outputHelper->dependencies);
-});
diff --git a/tests/Bridges.Latte2/expected/CacheMacro.cache.html b/tests/Bridges.Latte2/expected/CacheMacro.cache.html
deleted file mode 100644
index 0fe8eebc..00000000
--- a/tests/Bridges.Latte2/expected/CacheMacro.cache.html
+++ /dev/null
@@ -1,8 +0,0 @@
-Noncached content
-
-
-<h1>HELLO</h1>
-
-<p>Included file (11)</p>
-
-	hello
diff --git a/tests/Bridges.Latte2/expected/CacheMacro.cache.inc.php b/tests/Bridges.Latte2/expected/CacheMacro.cache.inc.php
deleted file mode 100644
index af95b58b..00000000
--- a/tests/Bridges.Latte2/expected/CacheMacro.cache.inc.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-%A%
-		if (Nette\Bridges\CacheLatte\CacheMacro::createCache($this->global->cacheStorage, '%[\w]+%', $this->global->cacheStack)) /* line %d% */ try {
-			echo '	';
-			echo LR\Filters::escapeHtmlText(($this->filters->lower)($title)) /* line %d% */;
-			echo "\n";
-			Nette\Bridges\CacheLatte\CacheMacro::endCache($this->global->cacheStack) /* line %d% */;
-		} catch (\Throwable $ʟ_e) {
-			Nette\Bridges\CacheLatte\CacheMacro::rollback($this->global->cacheStack);
-			throw $ʟ_e;
-		}
-%A%
diff --git a/tests/Bridges.Latte2/expected/CacheMacro.cache.php b/tests/Bridges.Latte2/expected/CacheMacro.cache.php
deleted file mode 100644
index 491297ec..00000000
--- a/tests/Bridges.Latte2/expected/CacheMacro.cache.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-%A%
-		echo 'Noncached content
-
-';
-		if (Nette\Bridges\CacheLatte\CacheMacro::createCache($this->global->cacheStorage, '%[\w]+%', $this->global->cacheStack, [$id, 'tags' => 'mytag'])) /* line %d% */ try {
-			echo '
-<h1>';
-			echo LR\Filters::escapeHtmlText(($this->filters->upper)($title)) /* line %d% */;
-			echo '</h1>
-
-';
-			$this->createTemplate('include.cache.latte', ['localvar' => 11] + $this->params, 'include')->renderToContentType('html') /* line %d% */;
-			echo "\n";
-			Nette\Bridges\CacheLatte\CacheMacro::endCache($this->global->cacheStack, [$id, 'tags' => 'mytag']) /* line %d% */;
-		} catch (\Throwable $ʟ_e) {
-			Nette\Bridges\CacheLatte\CacheMacro::rollback($this->global->cacheStack);
-			throw $ʟ_e;
-		}
-%A%
-	}
-
-
-	public function prepare(): void
-	{
-%A%
-		Nette\Bridges\CacheLatte\CacheMacro::initRuntime($this);
-%A%
diff --git a/tests/Bridges.Latte2/templates/cache.latte b/tests/Bridges.Latte2/templates/cache.latte
deleted file mode 100644
index 5155ee77..00000000
--- a/tests/Bridges.Latte2/templates/cache.latte
+++ /dev/null
@@ -1,9 +0,0 @@
-Noncached content
-
-{cache $id, tags => 'mytag'}
-
-<h1>{$title|upper}</h1>
-
-{include 'include.cache.latte', 'localvar' => 11}
-
-{/cache}
diff --git a/tests/Bridges.Latte2/templates/include.cache.latte b/tests/Bridges.Latte2/templates/include.cache.latte
deleted file mode 100644
index 595329b6..00000000
--- a/tests/Bridges.Latte2/templates/include.cache.latte
+++ /dev/null
@@ -1,5 +0,0 @@
-<p>Included file ({$localvar})</p>
-
-{cache}
-	{$title|lower}
-{/cache}
diff --git a/tests/Bridges.Latte3/Runtime.phpt b/tests/Bridges.Latte3/Runtime.phpt
index 83a5121b..2360125e 100644
--- a/tests/Bridges.Latte3/Runtime.phpt
+++ b/tests/Bridges.Latte3/Runtime.phpt
@@ -9,10 +9,6 @@ use Tester\Assert;
 
 require __DIR__ . '/../bootstrap.php';
 
-if (version_compare(Latte\Engine::VERSION, '3', '<')) {
-	Tester\Environment::skip('Test for Latte 3');
-}
-
 
 test('', function () {
 	$runtime = new Runtime(new DevNullStorage);
diff --git a/tests/Bridges.Latte3/{cache}.phpt b/tests/Bridges.Latte3/{cache}.phpt
index e275b259..cb5ea876 100644
--- a/tests/Bridges.Latte3/{cache}.phpt
+++ b/tests/Bridges.Latte3/{cache}.phpt
@@ -11,10 +11,6 @@ use Tester\Assert;
 
 require __DIR__ . '/../bootstrap.php';
 
-if (version_compare(Latte\Engine::VERSION, '3', '<')) {
-	Tester\Environment::skip('Test for Latte 3');
-}
-
 
 $latte = new Latte\Engine;
 $latte->setTempDirectory(getTempDir());

From 0c542ec90822253d6f7bd7b4ae0482e5b76a3d39 Mon Sep 17 00:00:00 2001
From: David Grudl <david@grudl.com>
Date: Sun, 27 Aug 2023 13:41:56 +0200
Subject: [PATCH 10/12] removed legacy services names

---
 src/Bridges/CacheDI/CacheExtension.php | 8 --------
 tests/Bridges.DI/CacheExtension.phpt   | 4 ----
 2 files changed, 12 deletions(-)

diff --git a/src/Bridges/CacheDI/CacheExtension.php b/src/Bridges/CacheDI/CacheExtension.php
index 05b41ae7..d398de39 100644
--- a/src/Bridges/CacheDI/CacheExtension.php
+++ b/src/Bridges/CacheDI/CacheExtension.php
@@ -46,13 +46,5 @@ public function loadConfiguration(): void
 		$builder->addDefinition($this->prefix('storage'))
 			->setType(Nette\Caching\Storage::class)
 			->setFactory(Nette\Caching\Storages\FileStorage::class, [$dir]);
-
-		if ($this->name === 'cache') {
-			if (extension_loaded('pdo_sqlite')) {
-				$builder->addAlias('nette.cacheJournal', $this->prefix('journal'));
-			}
-
-			$builder->addAlias('cacheStorage', $this->prefix('storage'));
-		}
 	}
 }
diff --git a/tests/Bridges.DI/CacheExtension.phpt b/tests/Bridges.DI/CacheExtension.phpt
index cc735739..9f4c4703 100644
--- a/tests/Bridges.DI/CacheExtension.phpt
+++ b/tests/Bridges.DI/CacheExtension.phpt
@@ -28,8 +28,4 @@ test('', function () {
 
 	$storage = $container->getService('cache.storage');
 	Assert::type(Nette\Caching\Storages\FileStorage::class, $storage);
-
-	// aliases
-	Assert::same($journal, $container->getService('nette.cacheJournal'));
-	Assert::same($storage, $container->getService('cacheStorage'));
 });

From 48ba8dcba5a23a0469038f23ae54f8ef5ee026b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20Km=C3=ADnek?= <m@kminet.eu>
Date: Thu, 4 Jan 2024 11:26:58 +0100
Subject: [PATCH 11/12] feat: Implement ability to drop cache keys using
 combination of OR and AND logic

---
 src/Caching/Cache.php                  |  4 +--
 src/Caching/CacheSelector.php          | 34 ++++++++++++++++++++++++++
 src/Caching/Storages/SQLiteJournal.php | 18 +++++++++++---
 3 files changed, 51 insertions(+), 5 deletions(-)
 create mode 100644 src/Caching/CacheSelector.php

diff --git a/src/Caching/Cache.php b/src/Caching/Cache.php
index 245836b5..4b20a7ad 100644
--- a/src/Caching/Cache.php
+++ b/src/Caching/Cache.php
@@ -277,13 +277,13 @@ public function remove(mixed $key): void
 	 * Removes items from the cache by conditions.
 	 * Conditions are:
 	 * - Cache::Priority => (int) priority
-	 * - Cache::Tags => (array) tags
+	 * - Cache::Tags => (array) tags | CacheSelector
 	 * - Cache::All => true
 	 */
 	public function clean(?array $conditions = null): void
 	{
 		$conditions = (array) $conditions;
-		if (isset($conditions[self::Tags])) {
+		if (isset($conditions[self::Tags]) && !$conditions[self::Tags] instanceof CacheSelector) {
 			$conditions[self::Tags] = array_values((array) $conditions[self::Tags]);
 		}
 
diff --git a/src/Caching/CacheSelector.php b/src/Caching/CacheSelector.php
new file mode 100644
index 00000000..b554c7db
--- /dev/null
+++ b/src/Caching/CacheSelector.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nette\Caching;
+
+class CacheSelector
+{
+	private array $conditions;
+
+
+	/**
+	 * Adds where condition, more calls appends with AND.
+	 * Pass tags as array to append with OR.
+	 *
+	 * Example:
+	 * (new CacheSelector())->where("animal")->where("dog")->where(["brown", "white"])
+	 * Creates condition looking for entities having tags animal and dog and (brown / white). Will not match entity, tagged animal, dog, black.
+	 *
+	 * @param  string|array  $tags  tag names to select
+	 */
+	public function where(string|array $tags): static
+	{
+		$this->conditions[] = $tags;
+
+		return $this;
+	}
+
+
+	public function getConditions(): array
+	{
+		return $this->conditions;
+	}
+}
diff --git a/src/Caching/Storages/SQLiteJournal.php b/src/Caching/Storages/SQLiteJournal.php
index 26747834..d2b9e97b 100644
--- a/src/Caching/Storages/SQLiteJournal.php
+++ b/src/Caching/Storages/SQLiteJournal.php
@@ -11,6 +11,7 @@
 
 use Nette;
 use Nette\Caching\Cache;
+use Nette\Caching\CacheSelector;
 
 
 /**
@@ -109,9 +110,20 @@ public function clean(array $conditions): ?array
 
 		$unions = $args = [];
 		if (!empty($conditions[Cache::Tags])) {
-			$tags = (array) $conditions[Cache::Tags];
-			$unions[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count($tags) - 1) . ')';
-			$args = $tags;
+			if ($conditions[Cache::Tags] instanceof CacheSelector) {
+				$intersects = [];
+				foreach ($conditions[Cache::Tags]->getConditions() as $condition) {
+					$intersects[] = 'SELECT `key` FROM `tags` WHERE `tag` IN (?' . str_repeat(', ?', count((array) $condition) - 1) . ')';
+					is_array($condition)
+						? array_push($args, ...$condition)
+						: array_push($args, $condition);
+				}
+				$unions[] = implode(' INTERSECT ', $intersects);
+			} else {
+				$tags = (array) $conditions[Cache::Tags];
+				$unions[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count($tags) - 1) . ')';
+				$args = $tags;
+			}
 		}
 
 		if (!empty($conditions[Cache::Priority])) {

From c071f017fa15c81ffcdd5de97bfdafdd301764e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20Km=C3=ADnek?= <m@kminet.eu>
Date: Thu, 4 Jan 2024 14:34:37 +0100
Subject: [PATCH 12/12] feat: Add missing DISTINCT statements

---
 src/Caching/Storages/SQLiteJournal.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Caching/Storages/SQLiteJournal.php b/src/Caching/Storages/SQLiteJournal.php
index d2b9e97b..592d39b1 100644
--- a/src/Caching/Storages/SQLiteJournal.php
+++ b/src/Caching/Storages/SQLiteJournal.php
@@ -113,7 +113,7 @@ public function clean(array $conditions): ?array
 			if ($conditions[Cache::Tags] instanceof CacheSelector) {
 				$intersects = [];
 				foreach ($conditions[Cache::Tags]->getConditions() as $condition) {
-					$intersects[] = 'SELECT `key` FROM `tags` WHERE `tag` IN (?' . str_repeat(', ?', count((array) $condition) - 1) . ')';
+					$intersects[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count((array) $condition) - 1) . ')';
 					is_array($condition)
 						? array_push($args, ...$condition)
 						: array_push($args, $condition);