mirror of
https://github.com/erusev/parsedown.git
synced 2023-08-10 21:13:06 +03:00
Merge pull request #768 from aidantwoods/feature/mutable-configurable
Add mutable configurable
This commit is contained in:
@ -43,9 +43,7 @@ final class Reference implements StateUpdatingBlock
|
|||||||
'title' => isset($matches[3]) ? $matches[3] : null,
|
'title' => isset($matches[3]) ? $matches[3] : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$State = $State->setting(
|
$State->get(DefinitionBook::class)->mutatingSet($id, $Data);
|
||||||
$State->get(DefinitionBook::class)->setting($id, $Data)
|
|
||||||
);
|
|
||||||
|
|
||||||
return new self($State);
|
return new self($State);
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
namespace Erusev\Parsedown\Configurables;
|
namespace Erusev\Parsedown\Configurables;
|
||||||
|
|
||||||
use Erusev\Parsedown\Configurable;
|
use Erusev\Parsedown\MutableConfigurable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @psalm-type _Data=array{url: string, title: string|null}
|
* @psalm-type _Data=array{url: string, title: string|null}
|
||||||
*/
|
*/
|
||||||
final class DefinitionBook implements Configurable
|
final class DefinitionBook implements MutableConfigurable
|
||||||
{
|
{
|
||||||
/** @var array<string, _Data> */
|
/** @var array<string, _Data> */
|
||||||
private $book;
|
private $book;
|
||||||
@ -29,14 +29,10 @@ final class DefinitionBook implements Configurable
|
|||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param _Data $data
|
* @param _Data $data
|
||||||
* @return self
|
|
||||||
*/
|
*/
|
||||||
public function setting($id, array $data)
|
public function mutatingSet($id, array $data): void
|
||||||
{
|
{
|
||||||
$book = $this->book;
|
$this->book[$id] = $data;
|
||||||
$book[$id] = $data;
|
|
||||||
|
|
||||||
return new self($book);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,4 +47,9 @@ final class DefinitionBook implements Configurable
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isolatedCopy(): self
|
||||||
|
{
|
||||||
|
return new self($this->book);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
44
src/MutableConfigurable.php
Normal file
44
src/MutableConfigurable.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Erusev\Parsedown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beware that the values of MutableConfigurables are NOT stable. Values SHOULD
|
||||||
|
* be accessed as close to use as possible. Parsing operations sharing the same
|
||||||
|
* State SHOULD NOT be triggered between where values are read and where they
|
||||||
|
* need to be relied upon.
|
||||||
|
*/
|
||||||
|
interface MutableConfigurable extends Configurable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Objects contained in State can generally be regarded as immutable,
|
||||||
|
* however, when mutability is *required* then isolatedCopy (this method)
|
||||||
|
* MUST be implemented to take a reliable copy of the contained state,
|
||||||
|
* which MUST be fully seperable from the current instance. This is
|
||||||
|
* sometimes referred to as a "deep copy".
|
||||||
|
*
|
||||||
|
* The following assumption is made when you implement
|
||||||
|
* MutableConfigurable:
|
||||||
|
*
|
||||||
|
* A shared, (more or less) globally writable, instantaniously updating
|
||||||
|
* (at all parsing levels), single copy of a Configurable is intentional
|
||||||
|
* and desired.
|
||||||
|
*
|
||||||
|
* As such, Parsedown will use the isolatedCopy method to ensure state
|
||||||
|
* isolation between successive parsing calls (which are considered to be
|
||||||
|
* isolated documents).
|
||||||
|
*
|
||||||
|
* You MUST NOT depend on the method `initial` being called when a clean
|
||||||
|
* parsing state is desired, this will not reliably occur; implement
|
||||||
|
* isolatedCopy properly to allow Parsedown to manage this.
|
||||||
|
*
|
||||||
|
* Failing to implement this method properly can result in unintended
|
||||||
|
* side-effects. If possible, you should design your Configurable to be
|
||||||
|
* immutable, which allows a single copy to be shared safely, and mutations
|
||||||
|
* localised to a heirarchy for which the order of operations is easy to
|
||||||
|
* reason about.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function isolatedCopy();
|
||||||
|
}
|
@ -31,7 +31,7 @@ final class Parsedown
|
|||||||
{
|
{
|
||||||
$StateBearer = $StateBearer ?: new State;
|
$StateBearer = $StateBearer ?: new State;
|
||||||
|
|
||||||
$this->State = $StateBearer->state();
|
$this->State = $StateBearer->state()->isolatedCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +42,7 @@ final class Parsedown
|
|||||||
{
|
{
|
||||||
list($StateRenderables, $State) = self::lines(
|
list($StateRenderables, $State) = self::lines(
|
||||||
Lines::fromTextLines($markdown, 0),
|
Lines::fromTextLines($markdown, 0),
|
||||||
$this->State
|
$this->State->isolatedCopy()
|
||||||
);
|
);
|
||||||
|
|
||||||
$Renderables = $State->applyTo($StateRenderables);
|
$Renderables = $State->applyTo($StateRenderables);
|
||||||
|
@ -15,7 +15,7 @@ final class State implements StateBearer
|
|||||||
/**
|
/**
|
||||||
* @var array<class-string<Configurable>, Configurable>
|
* @var array<class-string<Configurable>, Configurable>
|
||||||
*/
|
*/
|
||||||
private static $initialCache;
|
private static $initialCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Configurable[] $Configurables
|
* @param Configurable[] $Configurables
|
||||||
@ -56,6 +56,22 @@ final class State implements StateBearer
|
|||||||
*/
|
*/
|
||||||
public function get($className)
|
public function get($className)
|
||||||
{
|
{
|
||||||
|
if (
|
||||||
|
! isset($this->state[$className])
|
||||||
|
&& \is_subclass_of($className, MutableConfigurable::class, true)
|
||||||
|
) {
|
||||||
|
if (! isset(self::$initialCache[$className])) {
|
||||||
|
/** @var T */
|
||||||
|
self::$initialCache[$className] = $className::initial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var T
|
||||||
|
* @psalm-suppress PossiblyUndefinedMethod
|
||||||
|
*/
|
||||||
|
$this->state[$className] = self::$initialCache[$className]->isolatedCopy();
|
||||||
|
}
|
||||||
|
|
||||||
/** @var T */
|
/** @var T */
|
||||||
return (
|
return (
|
||||||
$this->state[$className]
|
$this->state[$className]
|
||||||
@ -93,4 +109,14 @@ final class State implements StateBearer
|
|||||||
{
|
{
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isolatedCopy(): self
|
||||||
|
{
|
||||||
|
return new self(\array_map(
|
||||||
|
function ($C) {
|
||||||
|
return $C instanceof MutableConfigurable ? $C->isolatedCopy() : $C;
|
||||||
|
},
|
||||||
|
$this->state
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user