• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jojo1981 / json-ast-builder / 17578687754

09 Sep 2025 09:48AM UTC coverage: 35.019%. Remained the same
17578687754

push

github

jojo1981
Fix: Make compatible with PHP versions: ^8.0|^8.1|^8.2|^8.3|^8.4. Closes #1
https://github.com/jojo1981/json-ast-builder/issues/1

161 of 407 new or added lines in 17 files covered. (39.56%)

290 existing lines in 25 files now uncovered.

374 of 1068 relevant lines covered (35.02%)

3.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Visitor/TokenGeneratorVisitor.php
1
<?php
2
/*
3
 * This file is part of the jojo1981/json-ast-builder package
4
 *
5
 * Copyright (c) 2019 Joost Nijhuis <jnijhuis81@gmail.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed in the root of the source code
9
 */
10
declare(strict_types=1);
11

12
namespace Jojo1981\JsonAstBuilder\Visitor;
13

14
use Jojo1981\JsonAstBuilder\Ast\ArrayNode;
15
use Jojo1981\JsonAstBuilder\Ast\BooleanNode;
16
use Jojo1981\JsonAstBuilder\Ast\ElementNode;
17
use Jojo1981\JsonAstBuilder\Ast\IntegerNode;
18
use Jojo1981\JsonAstBuilder\Ast\JsonNode;
19
use Jojo1981\JsonAstBuilder\Ast\KeyNode;
20
use Jojo1981\JsonAstBuilder\Ast\MemberNode;
21
use Jojo1981\JsonAstBuilder\Ast\NullNode;
22
use Jojo1981\JsonAstBuilder\Ast\NumberNode;
23
use Jojo1981\JsonAstBuilder\Ast\ObjectNode;
24
use Jojo1981\JsonAstBuilder\Ast\StringNode;
25
use Jojo1981\JsonAstBuilder\Ast\ValueNode;
26
use Jojo1981\JsonAstBuilder\Lexer\Token;
27
use Jojo1981\JsonAstBuilder\Lexer\TokenType;
28
use UnexpectedValueException;
29
use function array_merge;
30
use function count;
31
use function str_repeat;
32
use function strlen;
33

34
/**
35
 * @package Jojo1981\JsonAstBuilder\Visitor
36
 */
37
final class TokenGeneratorVisitor implements VisitorInterface
38
{
39
    /** @var array */
40
    private array $options;
41

42
    /** @var int */
43
    private int $level = 0;
44

45
    /** @var string */
46
    private string $indent;
47

48
    /** @var int */
49
    private int $position = 0;
50

51
    /** @var int */
52
    private int $linePosition = 1;
53

54
    /** @var int */
55
    private int $lineNumber = 1;
56

57
    /** @var Token[] */
58
    private array $result = [];
59

60
    /**
61
     * @param array $options
62
     */
63
    public function __construct(array $options = [])
64
    {
UNCOV
65
        $defaults = [
×
UNCOV
66
            'useTabs' => false,
×
UNCOV
67
            'pretty' => true,
×
UNCOV
68
            'indentSize' => 2,
×
UNCOV
69
            'spacesBeforeColon' => 0,
×
UNCOV
70
            'spacesAfterColon' => 1,
×
UNCOV
71
            'lineSeparator' => PHP_EOL
×
UNCOV
72
        ];
×
NEW
73
        $this->options = array_merge($defaults, $options);
×
NEW
74
        $this->indent = str_repeat($this->options['useTabs'] ? "\t" : ' ', $this->options['indentSize']);
×
75
    }
76

77
    /**
78
     * @return Token[]
79
     */
80
    public function getResult(): array
81
    {
NEW
82
        return $this->result;
×
83
    }
84

85
    /**
86
     * @param JsonNode $jsonNode
87
     * @return mixed
88
     * @throws UnexpectedValueException
89
     */
90
    public function visitJsonNode(JsonNode $jsonNode): mixed
91
    {
NEW
92
        $result = $jsonNode->getElement()->accept($this);
×
NEW
93
        $this->addToken(TokenType::TOKEN_EOF, null);
×
94

NEW
95
        return $result;
×
96
    }
97

98
    /**
99
     * @param int $tokenType
100
     * @param string|null $lexeme
101
     * @return void
102
     * @throws UnexpectedValueException
103
     */
104
    private function addToken(int $tokenType, ?string $lexeme): void
105
    {
NEW
106
        $tokenName = TokenType::getNameForTokenType($tokenType);
×
NEW
107
        $this->result[] = new Token($tokenType, $tokenName, $this->position, $this->lineNumber, $this->linePosition, $lexeme);
×
NEW
108
        if ($lexeme !== null) {
×
NEW
109
            $this->position += strlen($lexeme);
×
110
        }
111
    }
112

113
    /**
114
     * @param ElementNode $elementNode
115
     * @return mixed
116
     */
117
    public function visitElementNode(ElementNode $elementNode): mixed
118
    {
NEW
119
        return $elementNode->getValue()->accept($this);
×
120
    }
121

122
    /**
123
     * @param ValueNode $valueNode
124
     * @return mixed
125
     */
126
    public function visitValueNode(ValueNode $valueNode): mixed
127
    {
NEW
128
        return $valueNode->getType()->accept($this);
×
129
    }
130

131
    /**
132
     * @param ObjectNode $objectNode
133
     * @return mixed
134
     * @throws UnexpectedValueException
135
     */
136
    public function visitObjectNode(ObjectNode $objectNode): mixed
137
    {
NEW
138
        $this->addToken(TokenType::TOKEN_LEFT_CURLY_BRACKET, '{');
×
NEW
139
        $memberCount = count($objectNode->getMembers());
×
140
        if ($memberCount > 0) {
×
141
            $this->level++;
×
UNCOV
142
            $this->addNewline();
×
UNCOV
143
            $this->addIndent();
×
UNCOV
144
            for ($i = 0; $i < $memberCount; $i++) {
×
UNCOV
145
                $member = $objectNode->getMembers()[$i];
×
UNCOV
146
                $this->addIndent();
×
UNCOV
147
                $member->accept($this);
×
UNCOV
148
                if ($i < $memberCount - 1) {
×
NEW
149
                    $this->addToken(TokenType::TOKEN_COMMA, ',');
×
150

151
                }
152
                $this->addNewline();
×
UNCOV
153
                $this->addIndent();
×
154
            }
UNCOV
155
            $this->level--;
×
156
        }
NEW
157
        $this->addToken(TokenType::TOKEN_RIGHT_CURLY_BRACKET, '}');
×
158

NEW
159
        return null;
×
160
    }
161

162
    /**
163
     * @return void
164
     * @throws UnexpectedValueException
165
     */
166
    private function addNewline(): void
167
    {
NEW
168
        if ($this->options['pretty']) {
×
NEW
169
            $type = TokenType::TOKEN_NEWLINE;
×
NEW
170
            $name = TokenType::getNameForTokenType($type);
×
NEW
171
            $this->result[] = new Token($type, $name, $this->position, $this->lineNumber, $this->linePosition, $this->options['lineSeparator']);
×
NEW
172
            $this->position += strlen($this->options['lineSeparator']);
×
NEW
173
            $this->linePosition = 1;
×
NEW
174
            $this->lineNumber++;
×
175
        }
176
    }
177

178
    /**
179
     * @return void
180
     * @throws UnexpectedValueException
181
     */
182
    private function addIndent(): void
183
    {
NEW
184
        if ($this->options['pretty']) {
×
NEW
185
            $this->addToken(TokenType::TOKEN_WHITE_SPACE, str_repeat($this->indent, $this->level));
×
186
        }
187
    }
188

189
    /**
190
     * @param ArrayNode $arrayNode
191
     * @return mixed
192
     * @throws UnexpectedValueException
193
     * @throws UnexpectedValueException
194
     */
195
    public function visitArrayNode(ArrayNode $arrayNode): mixed
196
    {
NEW
197
        $this->addToken(TokenType::TOKEN_LEFT_CURLY_BRACKET, '[');
×
198

NEW
199
        $elementCount = count($arrayNode->getElements());
×
200
        if ($elementCount > 0) {
×
201
            $this->level++;
×
202
            $this->addNewline();
×
UNCOV
203
            $this->addIndent();
×
UNCOV
204
            for ($i = 0; $i < $elementCount; $i++) {
×
UNCOV
205
                $element = $arrayNode->getElements()[$i];
×
UNCOV
206
                $element->accept($this);
×
UNCOV
207
                if ($i < $elementCount - 1) {
×
NEW
208
                    $this->addToken(TokenType::TOKEN_COMMA, ',');
×
209
                }
UNCOV
210
                $this->addNewline();
×
UNCOV
211
                $this->addIndent();
×
212
            }
213

UNCOV
214
            $this->level--;
×
215
        }
NEW
216
        $this->addToken(TokenType::TOKEN_RIGHT_CURLY_BRACKET, ']');
×
217

NEW
218
        return null;
×
219
    }
220

221
    /**
222
     * @param StringNode $stringNode
223
     * @return mixed
224
     * @throws UnexpectedValueException
225
     */
226
    public function visitStringNode(StringNode $stringNode): mixed
227
    {
NEW
228
        $this->addToken(TokenType::TOKEN_STRING, '"' . $stringNode->getValue() . '"');
×
229

NEW
230
        return null;
×
231
    }
232

233
    /**
234
     * @param KeyNode $keyNode
235
     * @return mixed
236
     * @throws UnexpectedValueException
237
     */
238
    public function visitKeyNode(KeyNode $keyNode): mixed
239
    {
NEW
240
        $this->addToken(TokenType::TOKEN_STRING, '"' . $keyNode->getValue() . '"');
×
241

NEW
242
        return null;
×
243
    }
244

245
    /**
246
     * @param NumberNode $numberNode
247
     * @return mixed
248
     * @throws UnexpectedValueException
249
     */
250
    public function visitNumberNode(NumberNode $numberNode): mixed
251
    {
NEW
252
        $this->addToken(TokenType::TOKEN_NUMBER, (string) $numberNode->getValue());
×
253

NEW
254
        return null;
×
255
    }
256

257
    /**
258
     * @param IntegerNode $integerNode
259
     * @return mixed
260
     * @throws UnexpectedValueException
261
     */
262
    public function visitIntegerNode(IntegerNode $integerNode): mixed
263
    {
NEW
264
        $this->addToken(TokenType::TOKEN_INT, (string) $integerNode->getValue());
×
265

NEW
266
        return null;
×
267
    }
268

269
    /**
270
     * @param BooleanNode $booleanNode
271
     * @return mixed
272
     * @throws UnexpectedValueException
273
     */
274
    public function visitBooleanNode(BooleanNode $booleanNode): mixed
275
    {
NEW
276
        $this->addToken(TokenType::TOKEN_KEYWORD, $booleanNode->getValue() ? 'true' : 'false');
×
277

NEW
278
        return null;
×
279
    }
280

281
    /**
282
     * @param NullNode $nullNode
283
     * @return mixed
284
     * @throws UnexpectedValueException
285
     */
286
    public function visitNullNode(NullNode $nullNode): mixed
287
    {
NEW
288
        $this->addToken(TokenType::TOKEN_KEYWORD, 'null');
×
289

NEW
290
        return null;
×
291
    }
292

293
    /**
294
     * @param MemberNode $memberNode
295
     * @return mixed
296
     * @throws UnexpectedValueException
297
     */
298
    public function visitMemberNode(MemberNode $memberNode): mixed
299
    {
NEW
300
        $memberNode->getKey()->accept($this);
×
NEW
301
        $spaceBefore = '';
×
NEW
302
        $spaceAfter = '';
×
UNCOV
303
        if ($this->options['pretty']) {
×
NEW
304
            $spaceBefore = str_repeat(' ', $this->options['spacesBeforeColon']);
×
NEW
305
            $spaceAfter = str_repeat(' ', $this->options['spacesAfterColon']);
×
306
        }
NEW
307
        if ($spaceBefore !== '') {
×
NEW
308
            $this->addToken(TokenType::TOKEN_WHITE_SPACE, $spaceBefore);
×
309
        }
NEW
310
        $this->addToken(TokenType::TOKEN_COLON, ':');
×
NEW
311
        if ($spaceAfter !== '') {
×
NEW
312
            $this->addToken(TokenType::TOKEN_WHITE_SPACE, $spaceAfter);
×
313
        }
NEW
314
        $memberNode->getValue()->accept($this);
×
NEW
315
        $this->addNewline();
×
316

NEW
317
        return null;
×
318
    }
319
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc