diff --git a/framework/core/src/Extend/Formatter.php b/framework/core/src/Extend/Formatter.php
index 4e8ca7b5c..f1d742cc0 100644
--- a/framework/core/src/Extend/Formatter.php
+++ b/framework/core/src/Extend/Formatter.php
@@ -18,6 +18,7 @@ class Formatter implements ExtenderInterface, LifecycleInterface
{
private $configurationCallbacks = [];
private $parsingCallbacks = [];
+ private $unparsingCallbacks = [];
private $renderingCallbacks = [];
/**
@@ -58,6 +59,28 @@ class Formatter implements ExtenderInterface, LifecycleInterface
return $this;
}
+ /**
+ * Prepare the system for unparsing. This can be used to modify the text that was parsed.
+ * Please note that the parsed text must be returned, regardless of whether it's changed.
+ *
+ * @param callable|string $callback
+ *
+ * The callback can be a closure or invokable class, and should accept:
+ * - mixed $context
+ * - string $xml: The parsed text.
+ *
+ * The callback should return:
+ * - string $xml: The text to be unparsed.
+ *
+ * @return self
+ */
+ public function unparse($callback)
+ {
+ $this->unparsingCallbacks[] = $callback;
+
+ return $this;
+ }
+
/**
* Prepare the system for rendering. This can be used to modify the xml that will be rendered, or to modify the renderer.
* Please note that the xml to be rendered must be returned, regardless of whether it's changed.
@@ -91,6 +114,10 @@ class Formatter implements ExtenderInterface, LifecycleInterface
$formatter->addParsingCallback(ContainerUtil::wrapCallback($callback, $container));
}
+ foreach ($this->unparsingCallbacks as $callback) {
+ $formatter->addUnparsingCallback(ContainerUtil::wrapCallback($callback, $container));
+ }
+
foreach ($this->renderingCallbacks as $callback) {
$formatter->addRenderingCallback(ContainerUtil::wrapCallback($callback, $container));
}
diff --git a/framework/core/src/Formatter/Formatter.php b/framework/core/src/Formatter/Formatter.php
index 80683ff2d..44776a2ac 100644
--- a/framework/core/src/Formatter/Formatter.php
+++ b/framework/core/src/Formatter/Formatter.php
@@ -20,6 +20,8 @@ class Formatter
protected $parsingCallbacks = [];
+ protected $unparsingCallbacks = [];
+
protected $renderingCallbacks = [];
/**
@@ -52,6 +54,11 @@ class Formatter
$this->parsingCallbacks[] = $callback;
}
+ public function addUnparsingCallback($callback)
+ {
+ $this->unparsingCallbacks[] = $callback;
+ }
+
public function addRenderingCallback($callback)
{
$this->renderingCallbacks[] = $callback;
@@ -98,10 +105,15 @@ class Formatter
* Unparse XML.
*
* @param string $xml
+ * @param mixed $context
* @return string
*/
- public function unparse($xml)
+ public function unparse($xml, $context = null)
{
+ foreach ($this->unparsingCallbacks as $callback) {
+ $xml = $callback($context, $xml);
+ }
+
return Unparser::unparse($xml);
}
diff --git a/framework/core/src/Post/CommentPost.php b/framework/core/src/Post/CommentPost.php
index d49bed207..6e89e79ea 100644
--- a/framework/core/src/Post/CommentPost.php
+++ b/framework/core/src/Post/CommentPost.php
@@ -128,7 +128,7 @@ class CommentPost extends Post
*/
public function getContentAttribute($value)
{
- return static::$formatter->unparse($value);
+ return static::$formatter->unparse($value, $this);
}
/**
diff --git a/framework/core/tests/integration/extenders/FormatterTest.php b/framework/core/tests/integration/extenders/FormatterTest.php
index 7a4e12fdf..2dfd66554 100644
--- a/framework/core/tests/integration/extenders/FormatterTest.php
+++ b/framework/core/tests/integration/extenders/FormatterTest.php
@@ -89,6 +89,36 @@ class FormatterTest extends TestCase
$this->assertEquals('ReplacedText<a>', $this->getFormatter()->parse('Text'));
}
+ /**
+ * @test
+ */
+ public function custom_formatter_unparsing_doesnt_work_by_default()
+ {
+ $this->assertEquals('Text', $this->getFormatter()->unparse('Text<a>'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_unparsing_works_if_added_with_closure()
+ {
+ $this->extend((new Extend\Formatter)->unparse(function ($context, $xml) {
+ return 'ReplacedText<a>';
+ }));
+
+ $this->assertEquals('ReplacedText', $this->getFormatter()->unparse('Text<a>'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_unparsing_works_if_added_with_invokable_class()
+ {
+ $this->extend((new Extend\Formatter)->unparse(InvokableUnparsing::class));
+
+ $this->assertEquals('ReplacedText', $this->getFormatter()->unparse('Text<a>'));
+ }
+
/**
* @test
*/
@@ -136,6 +166,14 @@ class InvokableParsing
}
}
+class InvokableUnparsing
+{
+ public function __invoke($context, $xml)
+ {
+ return 'ReplacedText<a>';
+ }
+}
+
class InvokableRendering
{
public function __invoke($renderer, $context, $xml, $request)