From 2fb493d58d579e61e78e1a624dc44b6a5329dfa5 Mon Sep 17 00:00:00 2001
From: Claude
Date: Sun, 14 Dec 2025 09:04:36 +0000
Subject: [PATCH 1/8] Add Parsedown support with optional dependency
Add ParsedownExtraAdapter, ParsedownFilter and ParsedownExtension
to support markdown parsing via erusev/parsedown-extra package.
The dependency is optional (suggested in composer.json).
---
composer.json | 3 +-
src/DI/ParsedownExtension.php | 58 +++++++++++++++++++++++++++
src/Filters/ParsedownExtraAdapter.php | 43 ++++++++++++++++++++
src/Filters/ParsedownFilter.php | 28 +++++++++++++
4 files changed, 131 insertions(+), 1 deletion(-)
create mode 100644 src/DI/ParsedownExtension.php
create mode 100644 src/Filters/ParsedownExtraAdapter.php
create mode 100644 src/Filters/ParsedownFilter.php
diff --git a/composer.json b/composer.json
index 483b9f0..08b94de 100644
--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,8 @@
"contributte/phpstan": "^0.1"
},
"suggest": {
- "nette/di": "to use VersionExtension[CompilerExtension]"
+ "nette/di": "to use VersionExtension[CompilerExtension]",
+ "erusev/parsedown-extra": "to use ParsedownExtension[CompilerExtension]"
},
"autoload": {
"psr-4": {
diff --git a/src/DI/ParsedownExtension.php b/src/DI/ParsedownExtension.php
new file mode 100644
index 0000000..7dc4670
--- /dev/null
+++ b/src/DI/ParsedownExtension.php
@@ -0,0 +1,58 @@
+ Expect::string('parsedown'),
+ ]);
+ }
+
+ public function loadConfiguration(): void
+ {
+ if (!class_exists('ParsedownExtra')) {
+ throw new LogicalException('ParsedownExtra class not found. Install erusev/parsedown-extra package.');
+ }
+
+ $builder = $this->getContainerBuilder();
+
+ $builder->addDefinition($this->prefix('adapter'))
+ ->setFactory(ParsedownExtraAdapter::class);
+ }
+
+ public function beforeCompile(): void
+ {
+ $builder = $this->getContainerBuilder();
+ $config = $this->config;
+
+ if ($builder->getByType(LatteFactory::class) === null) {
+ throw new LogicalException('You have to register LatteFactory first.');
+ }
+
+ $factoryDefinition = $builder->getDefinitionByType(LatteFactory::class);
+ assert($factoryDefinition instanceof FactoryDefinition);
+
+ $factoryDefinition
+ ->getResultDefinition()
+ ->addSetup('addFilter', [$config->filter, [new Statement(ParsedownFilter::class), 'apply']]);
+ }
+
+}
diff --git a/src/Filters/ParsedownExtraAdapter.php b/src/Filters/ParsedownExtraAdapter.php
new file mode 100644
index 0000000..8981ea9
--- /dev/null
+++ b/src/Filters/ParsedownExtraAdapter.php
@@ -0,0 +1,43 @@
+parsedown = $parsedown ?? new ParsedownExtra();
+ }
+
+ public function process(mixed $text): mixed
+ {
+ foreach ($this->onProcess as $callback) {
+ $callback($text, $this);
+ }
+
+ return $this->parsedown->parse($text);
+ }
+
+ public function processLine(mixed $line): string
+ {
+ foreach ($this->onProcess as $callback) {
+ $callback($line, $this);
+ }
+
+ return $this->parsedown->line($line);
+ }
+
+}
diff --git a/src/Filters/ParsedownFilter.php b/src/Filters/ParsedownFilter.php
new file mode 100644
index 0000000..d782124
--- /dev/null
+++ b/src/Filters/ParsedownFilter.php
@@ -0,0 +1,28 @@
+adapter = $adapter;
+ }
+
+ public function apply(FilterInfo $info, mixed $text): mixed
+ {
+ if ($info->contentType !== ContentType::Html) {
+ throw new LogicalException('Filter |parsedown used in incompatible content type.');
+ }
+
+ return $this->adapter->process($text);
+ }
+
+}
From 701e75c589fc3f4a32847d22baf4d84d3a0b9f1b Mon Sep 17 00:00:00 2001
From: Claude
Date: Sun, 14 Dec 2025 19:21:21 +0000
Subject: [PATCH 2/8] Add tests and documentation for Parsedown support
- Add unit tests for ParsedownExtraAdapter, ParsedownFilter and ParsedownExtension
- Add FakeParsedownExtra fixture for testing
- Fix ParsedownFilter to handle Text content type and set output to Html
- Add documentation for ParsedownExtension in .docs/README.md
---
.docs/README.md | 54 +++++++++++++
src/Filters/ParsedownFilter.php | 4 +-
tests/Cases/DI/ParsedownExtension.phpt | 76 +++++++++++++++++++
.../Cases/Filters/ParsedownExtraAdapter.phpt | 64 ++++++++++++++++
tests/Cases/Filters/ParsedownFilter.phpt | 59 ++++++++++++++
tests/Fixtures/FakeParsedownExtra.php | 26 +++++++
6 files changed, 282 insertions(+), 1 deletion(-)
create mode 100644 tests/Cases/DI/ParsedownExtension.phpt
create mode 100644 tests/Cases/Filters/ParsedownExtraAdapter.phpt
create mode 100644 tests/Cases/Filters/ParsedownFilter.phpt
create mode 100644 tests/Fixtures/FakeParsedownExtra.php
diff --git a/.docs/README.md b/.docs/README.md
index fa625ae..b6cf611 100644
--- a/.docs/README.md
+++ b/.docs/README.md
@@ -7,6 +7,7 @@ Extra contribution to [`nette/latte`](https://github.com/nette/latte).
- [Setup](#setup)
- [VersionExtension - revision macros for assets](#versions-extension)
- [CdnExtension - CDN support for assets](#cdn-extension)
+- [ParsedownExtension - markdown parsing support](#parsedown-extension)
- [FiltersExtension - install filters easily](#filters-extension)
- [RuntimeFilters - collection of prepared filters](#runtimefilters)
- [Formatters - collection of prepared formatters](#formatters)
@@ -92,6 +93,59 @@ cdn:
https://cdn.example.com/assets/style.css?time=123456789
```
+## Parsedown Extension
+
+This extension provides markdown parsing support via the `|parsedown` filter using [ParsedownExtra](https://github.com/erusev/parsedown-extra).
+
+### Requirements
+
+The `erusev/parsedown-extra` package is an optional dependency. Install it first:
+
+```bash
+composer require erusev/parsedown-extra
+```
+
+### Install
+
+```neon
+extensions:
+ parsedown: Contributte\Latte\DI\ParsedownExtension
+```
+
+### Configuration
+
+```neon
+parsedown:
+ filter: parsedown # default filter name, can be changed to e.g. "markdown"
+```
+
+### Usage
+
+```latte
+{* Filter syntax *}
+{$markdownContent|parsedown}
+
+{* Block syntax *}
+{block|parsedown}
+# Hello World
+
+This is **markdown** content.
+{/block}
+```
+
+### Advanced Usage
+
+You can use the `ParsedownExtraAdapter` directly with callbacks for custom processing:
+
+```php
+use Contributte\Latte\Filters\ParsedownExtraAdapter;
+
+$adapter = $container->getByType(ParsedownExtraAdapter::class);
+$adapter->onProcess[] = function (string $text, ParsedownExtraAdapter $adapter): void {
+ // Custom processing before markdown parsing
+};
+```
+
## Filters Extension
Install filters by single extension and simple `FiltersProvider` implementation.
diff --git a/src/Filters/ParsedownFilter.php b/src/Filters/ParsedownFilter.php
index d782124..c9cca17 100644
--- a/src/Filters/ParsedownFilter.php
+++ b/src/Filters/ParsedownFilter.php
@@ -18,10 +18,12 @@ public function __construct(ParsedownExtraAdapter $adapter)
public function apply(FilterInfo $info, mixed $text): mixed
{
- if ($info->contentType !== ContentType::Html) {
+ if ($info->contentType !== null && $info->contentType !== ContentType::Html && $info->contentType !== ContentType::Text) {
throw new LogicalException('Filter |parsedown used in incompatible content type.');
}
+ $info->contentType = ContentType::Html;
+
return $this->adapter->process($text);
}
diff --git a/tests/Cases/DI/ParsedownExtension.phpt b/tests/Cases/DI/ParsedownExtension.phpt
new file mode 100644
index 0000000..5a63ec2
--- /dev/null
+++ b/tests/Cases/DI/ParsedownExtension.phpt
@@ -0,0 +1,76 @@
+load(function (Compiler $compiler): void {
+ $compiler->addExtension('latte', new LatteExtension(Environment::getTestDir()));
+ $compiler->addExtension('parsedown', new ParsedownExtension());
+ }, 1);
+
+ /** @var Container $container */
+ $container = new $class();
+
+ // Test adapter is registered
+ $adapter = $container->getByType(ParsedownExtraAdapter::class);
+ Assert::type(ParsedownExtraAdapter::class, $adapter);
+
+ // Test filter is registered
+ /** @var ILatteFactory $latteFactory */
+ $latteFactory = $container->getByType(ILatteFactory::class);
+ $result = $latteFactory->create()->renderToString(FileMock::create('{="Hello World"|parsedown}', 'latte'));
+ Assert::equal('Hello World
', $result);
+});
+
+// Test DI extension with custom filter name
+Toolkit::test(function (): void {
+ $loader = new ContainerLoader(Environment::getTestDir(), true);
+ $class = $loader->load(function (Compiler $compiler): void {
+ $compiler->addExtension('latte', new LatteExtension(Environment::getTestDir()));
+ $compiler->addExtension('parsedown', new ParsedownExtension());
+ $compiler->loadConfig(FileMock::create('
+ parsedown:
+ filter: markdown
+ ', 'neon'));
+ }, 2);
+
+ /** @var Container $container */
+ $container = new $class();
+
+ /** @var ILatteFactory $latteFactory */
+ $latteFactory = $container->getByType(ILatteFactory::class);
+ $result = $latteFactory->create()->renderToString(FileMock::create('{="Hello World"|markdown}', 'latte'));
+ Assert::equal('Hello World
', $result);
+});
+
+// Test DI extension with block syntax
+Toolkit::test(function (): void {
+ $loader = new ContainerLoader(Environment::getTestDir(), true);
+ $class = $loader->load(function (Compiler $compiler): void {
+ $compiler->addExtension('latte', new LatteExtension(Environment::getTestDir()));
+ $compiler->addExtension('parsedown', new ParsedownExtension());
+ }, 3);
+
+ /** @var Container $container */
+ $container = new $class();
+
+ /** @var ILatteFactory $latteFactory */
+ $latteFactory = $container->getByType(ILatteFactory::class);
+ $result = $latteFactory->create()->renderToString(FileMock::create('{block|parsedown}Hello World{/block}', 'latte'));
+ Assert::equal('Hello World
', $result);
+});
diff --git a/tests/Cases/Filters/ParsedownExtraAdapter.phpt b/tests/Cases/Filters/ParsedownExtraAdapter.phpt
new file mode 100644
index 0000000..8b489b5
--- /dev/null
+++ b/tests/Cases/Filters/ParsedownExtraAdapter.phpt
@@ -0,0 +1,64 @@
+process('Hello World');
+ Assert::equal('Hello World
', $result);
+});
+
+// Test line processing
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $result = $adapter->processLine('Hello World');
+ Assert::equal('Hello World', $result);
+});
+
+// Test onProcess callback
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $called = false;
+ $adapter->onProcess[] = function (string $text, ParsedownExtraAdapter $a) use (&$called): void {
+ $called = true;
+ Assert::equal('Test', $text);
+ };
+
+ $adapter->process('Test');
+ Assert::true($called);
+});
+
+// Test onProcess callback for line
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $called = false;
+ $adapter->onProcess[] = function (string $line, ParsedownExtraAdapter $a) use (&$called): void {
+ $called = true;
+ Assert::equal('Test Line', $line);
+ };
+
+ $adapter->processLine('Test Line');
+ Assert::true($called);
+});
+
+// Test multiple onProcess callbacks
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $callCount = 0;
+ $adapter->onProcess[] = function () use (&$callCount): void {
+ $callCount++;
+ };
+ $adapter->onProcess[] = function () use (&$callCount): void {
+ $callCount++;
+ };
+
+ $adapter->process('Test');
+ Assert::equal(2, $callCount);
+});
diff --git a/tests/Cases/Filters/ParsedownFilter.phpt b/tests/Cases/Filters/ParsedownFilter.phpt
new file mode 100644
index 0000000..ca8c162
--- /dev/null
+++ b/tests/Cases/Filters/ParsedownFilter.phpt
@@ -0,0 +1,59 @@
+apply($info, 'Hello World');
+
+ Assert::equal('Hello World
', $result);
+});
+
+// Test filter works with Text content type and converts to Html
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $filter = new ParsedownFilter($adapter);
+
+ $info = new FilterInfo(ContentType::Text);
+ $result = $filter->apply($info, 'Hello World');
+
+ Assert::equal('Hello World
', $result);
+ Assert::equal(ContentType::Html, $info->contentType);
+});
+
+// Test filter throws exception for incompatible content type (e.g., JavaScript)
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $filter = new ParsedownFilter($adapter);
+
+ $info = new FilterInfo(ContentType::JavaScript);
+
+ Assert::exception(function () use ($filter, $info): void {
+ $filter->apply($info, 'Hello World');
+ }, LogicalException::class, 'Filter |parsedown used in incompatible content type.');
+});
+
+// Test filter with markdown-like content
+Toolkit::test(function (): void {
+ $adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
+ $filter = new ParsedownFilter($adapter);
+
+ $info = new FilterInfo(ContentType::Html);
+ $result = $filter->apply($info, '# Heading');
+
+ Assert::equal('# Heading
', $result);
+});
diff --git a/tests/Fixtures/FakeParsedownExtra.php b/tests/Fixtures/FakeParsedownExtra.php
new file mode 100644
index 0000000..3c8ad9a
--- /dev/null
+++ b/tests/Fixtures/FakeParsedownExtra.php
@@ -0,0 +1,26 @@
+' . $text . '
';
+ }
+
+ public function line(string $line): string
+ {
+ return $line;
+ }
+
+}
+
+// Register as ParsedownExtra if the real one is not available
+if (!class_exists('ParsedownExtra')) {
+ class_alias(FakeParsedownExtra::class, 'ParsedownExtra');
+}
From 5383272778aac084f2b1f6ccc491189f469dc4ac Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:45:30 +0000
Subject: [PATCH 3/8] Modernize build tools and static analysis configuration
- Consolidate .PHONY declarations in Makefile
- Update phpstan.neon to PHP 8.2 and add ignoreErrors for optional ParsedownExtra dependency
- Update ruleset.xml to use contributte/qa ruleset-8.2.xml
---
Makefile | 15 +++++----------
phpstan.neon | 6 +++++-
ruleset.xml | 2 +-
3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/Makefile b/Makefile
index 33bc117..d53bea0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,31 +1,26 @@
-.PHONY: install
+.PHONY: install qa cs csf phpstan tests coverage
+
install:
composer update
-.PHONY: qa
qa: phpstan cs
-.PHONY: cs
cs:
ifdef GITHUB_ACTION
- vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp -q --report=checkstyle src tests | cs2pr
+ vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --colors -nsp --extensions=php,phpt -q --report=checkstyle src tests | cs2pr
else
- vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests
+ vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --colors -nsp --extensions=php,phpt src tests
endif
-.PHONY: csf
csf:
- vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests
+ vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --colors -nsp --extensions=php,phpt src tests
-.PHONY: phpstan
phpstan:
vendor/bin/phpstan analyse -c phpstan.neon
-.PHONY: tests
tests:
vendor/bin/tester -s -p php --colors 1 -C tests/Cases
-.PHONY: coverage
coverage:
ifdef GITHUB_ACTION
vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage coverage.xml --coverage-src src tests/Cases
diff --git a/phpstan.neon b/phpstan.neon
index 562ab05..19f0196 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -3,7 +3,7 @@ includes:
parameters:
level: 9
- phpVersion: 80100
+ phpVersion: 80200
scanDirectories:
- src
@@ -16,3 +16,7 @@ parameters:
- .docs
ignoreErrors:
+ # ParsedownExtra is an optional dependency
+ - '#unknown class ParsedownExtra#i'
+ - '#invalid type ParsedownExtra#i'
+ - '#Instantiated class ParsedownExtra not found#'
diff --git a/ruleset.xml b/ruleset.xml
index 629d95e..6485eb5 100644
--- a/ruleset.xml
+++ b/ruleset.xml
@@ -1,7 +1,7 @@
-
+
From 28ec18e0142d72720ac088fd764d47d237c1c0de Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:45:39 +0000
Subject: [PATCH 4/8] Update CI workflows to PHP 8.3
- Update phpstan.yml to use PHP 8.3
- Update codesniffer.yml to use PHP 8.3
- Update coverage.yml to use PHP 8.3
---
.github/workflows/codesniffer.yml | 2 +-
.github/workflows/coverage.yml | 2 +-
.github/workflows/phpstan.yml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codesniffer.yml b/.github/workflows/codesniffer.yml
index a58ac4f..d5ff803 100644
--- a/.github/workflows/codesniffer.yml
+++ b/.github/workflows/codesniffer.yml
@@ -15,4 +15,4 @@ jobs:
name: "Codesniffer"
uses: contributte/.github/.github/workflows/codesniffer.yml@master
with:
- php: "8.2"
+ php: "8.3"
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 860c47e..85f1300 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -15,4 +15,4 @@ jobs:
name: "Nette Tester"
uses: contributte/.github/.github/workflows/nette-tester-coverage-v2.yml@master
with:
- php: "8.2"
+ php: "8.3"
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
index eb916bf..9827fdd 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/phpstan.yml
@@ -15,4 +15,4 @@ jobs:
name: "Phpstan"
uses: contributte/.github/.github/workflows/phpstan.yml@master
with:
- php: "8.2"
+ php: "8.3"
From 5ece3536496b2bd844680b70e305249bf10139ea Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:45:48 +0000
Subject: [PATCH 5/8] Update README.md with correct PHP version requirements
- Fix PHP version in versions table (dev requires PHP 8.2+)
- Align table formatting
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 59c7e4d..640d875 100644
--- a/README.md
+++ b/README.md
@@ -32,10 +32,10 @@ For details on how to use this package, check out our [documentation](.docs).
## Versions
-| State | Version | Branch | Nette | PHP |
-|-------------|---------|----------|-------|----------|
-| dev | `^0.7` | `master` | 3.2+ | `>=8.1+` |
-| stable | `^0.6` | `master` | 3.2+ | `>=8.1+` |
+| State | Version | Branch | Nette | PHP |
+|-------------|---------|----------|-------|---------|
+| dev | `^0.7` | `master` | 3.2+ | `>=8.2` |
+| stable | `^0.6` | `master` | 3.2+ | `>=8.1` |
## Development
From cf929a0ac5dca3dd4d29adbc5bcbfd2c4e3785b0 Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:51:22 +0000
Subject: [PATCH 6/8] Fix CdnNode for Latte 3.1 compatibility
Replace LR\Filters::escapeHtmlAttr with LR\HtmlHelpers::escapeAttr
as the Filters class was restructured in Latte 3.1.
---
src/Extensions/Node/CdnNode.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Extensions/Node/CdnNode.php b/src/Extensions/Node/CdnNode.php
index c5cb34c..2e0bb7b 100644
--- a/src/Extensions/Node/CdnNode.php
+++ b/src/Extensions/Node/CdnNode.php
@@ -24,7 +24,7 @@ public static function create(Tag $tag): self
public function print(PrintContext $context): string
{
return $context->format(
- 'echo LR\Filters::escapeHtmlAttr(call_user_func($this->global->cdnBuilder, %0.node)) %1.line;',
+ 'echo LR\HtmlHelpers::escapeAttr(call_user_func($this->global->cdnBuilder, %0.node)) %1.line;',
$this->path,
$this->position,
);
From 6f502b081450470a39503433d8e417c536a048c3 Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:51:31 +0000
Subject: [PATCH 7/8] Fix code style in ParsedownExtraAdapter tests
Use stdClass objects instead of reference variables
to comply with coding standard rules.
---
.../Cases/Filters/ParsedownExtraAdapter.phpt | 31 ++++++++++---------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/tests/Cases/Filters/ParsedownExtraAdapter.phpt b/tests/Cases/Filters/ParsedownExtraAdapter.phpt
index 8b489b5..93eda9b 100644
--- a/tests/Cases/Filters/ParsedownExtraAdapter.phpt
+++ b/tests/Cases/Filters/ParsedownExtraAdapter.phpt
@@ -25,40 +25,43 @@ Toolkit::test(function (): void {
// Test onProcess callback
Toolkit::test(function (): void {
$adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
- $called = false;
- $adapter->onProcess[] = function (string $text, ParsedownExtraAdapter $a) use (&$called): void {
- $called = true;
+ $state = new stdClass();
+ $state->called = false;
+ $adapter->onProcess[] = function (string $text, ParsedownExtraAdapter $a) use ($state): void {
+ $state->called = true;
Assert::equal('Test', $text);
};
$adapter->process('Test');
- Assert::true($called);
+ Assert::true($state->called);
});
// Test onProcess callback for line
Toolkit::test(function (): void {
$adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
- $called = false;
- $adapter->onProcess[] = function (string $line, ParsedownExtraAdapter $a) use (&$called): void {
- $called = true;
+ $state = new stdClass();
+ $state->called = false;
+ $adapter->onProcess[] = function (string $line, ParsedownExtraAdapter $a) use ($state): void {
+ $state->called = true;
Assert::equal('Test Line', $line);
};
$adapter->processLine('Test Line');
- Assert::true($called);
+ Assert::true($state->called);
});
// Test multiple onProcess callbacks
Toolkit::test(function (): void {
$adapter = new ParsedownExtraAdapter(new FakeParsedownExtra());
- $callCount = 0;
- $adapter->onProcess[] = function () use (&$callCount): void {
- $callCount++;
+ $state = new stdClass();
+ $state->callCount = 0;
+ $adapter->onProcess[] = function () use ($state): void {
+ $state->callCount++;
};
- $adapter->onProcess[] = function () use (&$callCount): void {
- $callCount++;
+ $adapter->onProcess[] = function () use ($state): void {
+ $state->callCount++;
};
$adapter->process('Test');
- Assert::equal(2, $callCount);
+ Assert::equal(2, $state->callCount);
});
From 7183051d477ee27eb8af667b87dd920e3d7254f7 Mon Sep 17 00:00:00 2001
From: Felix
Date: Sun, 14 Dec 2025 19:55:37 +0000
Subject: [PATCH 8/8] Bump minimum latte/latte version to 3.1.0
Required for LR\HtmlHelpers::escapeAttr which replaced
LR\Filters::escapeHtmlAttr in Latte 3.1.
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 08b94de..4a04ee7 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": ">=8.2",
- "latte/latte": "^3.0.12"
+ "latte/latte": "^3.1.0"
},
"require-dev": {
"nette/application": "^3.1.14",