-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeneratedTokenCodec.php
More file actions
134 lines (116 loc) · 3.21 KB
/
Copy pathGeneratedTokenCodec.php
File metadata and controls
134 lines (116 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?php declare(strict_types=1);
namespace uuf6429\PhpCsFixerBlockstring\InterpolationCodec;
use LogicException;
use RuntimeException;
use uuf6429\PhpCsFixerBlockstring\BlockString\InterpolationSegment;
use uuf6429\PhpCsFixerBlockstring\BlockString\StringSegment;
use uuf6429\PhpCsFixerBlockstring\CacheFingerprintableInterface;
final class GeneratedTokenCodec implements CodecInterface
{
/**
* @readonly
*/
private string $tokenPattern;
/**
* @readonly
* @var (callable(InterpolationSegment $segment): ?string)|null
*/
private $tokenFactory;
/**
* @param string $tokenPattern
* @param (callable(InterpolationSegment $segment): ?string)|null $tokenFactory
*/
public function __construct(string $tokenPattern = '__PHP_VAR_%d__', ?callable $tokenFactory = null)
{
$this->tokenPattern = $tokenPattern;
$this->tokenFactory = $tokenFactory;
}
public function encode(array $segments): CodecResult
{
$index = 0;
$mapping = [];
$content = '';
foreach ($segments as $segment) {
if ($segment instanceof StringSegment) {
$content .= $segment->value;
continue;
}
assert($segment instanceof InterpolationSegment);
$token = null;
if ($this->tokenFactory !== null) {
$token = ($this->tokenFactory)($segment);
}
if ($token === null) {
$token = sprintf($this->tokenPattern, ++$index);
}
if ($token === '') {
throw new LogicException('Replacement token cannot be an empty string!');
}
$mapping[$token] = $segment;
$content .= $token;
}
return new CodecResult($mapping, $content);
}
public function decode(CodecResult $result): array
{
$content = $result->content;
$len = strlen($content);
$pos = 0;
$root = TokenTrieNode::fromMapping($result->mapping);
$segments = [];
while ($pos < $len) {
$node = $root;
$curPos = $pos;
$matchPos = 0;
$matchToken = null;
while ($curPos < $len && ($node = $node->children[$content[$curPos] ?? ''] ?? null) !== null) {
$curPos++;
if ($node->token !== null) {
$matchToken = $node->token;
$matchPos = $curPos;
}
}
if ($matchToken !== null) {
$segments[] = $result->mapping[$matchToken];
$pos = $matchPos;
continue;
}
$start = $pos;
$pos++;
while ($pos < $len) {
$node = $root;
$cur = $pos;
while ($cur < $len && isset($node->children[$content[$cur]])) {
$node = $node->children[$content[$cur]];
$cur++;
if ($node->token !== null) {
break 2;
}
}
$pos++;
}
$segments[] = new StringSegment(substr($content, $start, $pos - $start));
}
return $segments;
}
public function getCacheFingerprint()
{
return [self::class, $this->tokenPattern, $this->getTokenFactoryCacheFingerprint()];
}
/**
* @return mixed
*/
private function getTokenFactoryCacheFingerprint()
{
if (!is_object($this->tokenFactory)) {
return $this->tokenFactory;
}
if ($this->tokenFactory instanceof CacheFingerprintableInterface) {
return $this->tokenFactory->getCacheFingerprint();
}
throw new RuntimeException(
'Token factory must implement CacheFingerprintableInterface - it cannot be a simple closure object.'
. ' A possibility is to make an anonymous object implementing that interface and an __invoke method.'
);
}
}