Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit d9a4ba9

Browse files
committed
pref: add BlockGenerator to lazyload parsing
1 parent 99b8f13 commit d9a4ba9

File tree

7 files changed

+89
-34
lines changed

7 files changed

+89
-34
lines changed

src/Definition/AbstractBlock.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ protected function __construct(
6969
#[\Override]
7070
public static function create(array $data, array $allowedTags = []): static
7171
{
72+
Assert::notEmpty($data['id']);
73+
Assert::notEmpty($data['type']);
74+
Assert::isArray($data['data']);
75+
Assert::notEmpty($data['data']);
76+
7277
return new static(
7378
id: $data['id'],
7479
type: $data['type'],

src/Definition/BlockGenerator.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Devscast\EditorJs\Definition;
6+
7+
/**
8+
* Class BlockGenerator.
9+
*
10+
* The `BlockGenerator` class is responsible for generating blocks from the provided data
11+
* using a `BlockRegistry`. It implements an iterator to yield block instances parsed
12+
* from the data.
13+
*
14+
* @author bernard-ng <bernard@devscast.tech>
15+
*/
16+
final readonly class BlockGenerator
17+
{
18+
/**
19+
* Constructor for the `BlockGenerator` class.
20+
*
21+
* @param string $data The raw data to be parsed into blocks.
22+
* @param BlockRegistry $registry The registry containing block definitions.
23+
*/
24+
public function __construct(
25+
private string $data,
26+
private BlockRegistry $registry
27+
) {
28+
}
29+
30+
/**
31+
* Get an iterator for the blocks.
32+
*
33+
* This method parses the provided data using the `BlockFactory` and yields
34+
* instances of `BlockInterface`.
35+
*
36+
* @return iterable<BlockInterface> An iterable collection of block instances.
37+
*/
38+
public function getIterator(): iterable
39+
{
40+
yield from BlockFactory::parse($this->data, $this->registry);
41+
}
42+
}

src/Editor.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414
namespace Devscast\EditorJs;
1515

16-
use Devscast\EditorJs\Definition\BlockFactory;
17-
use Devscast\EditorJs\Definition\BlockInterface;
16+
use Devscast\EditorJs\Definition\BlockGenerator;
1817
use Devscast\EditorJs\Definition\BlockRegistry;
1918
use Devscast\EditorJs\Exception\EditorException;
2019
use Devscast\EditorJs\Sanitizer\Sanitizer;
@@ -35,10 +34,9 @@
3534
final readonly class Editor
3635
{
3736
/**
38-
* @var iterable<BlockInterface>
3937
* The collection of parsed blocks from the provided JSON data.
4038
*/
41-
public iterable $blocks;
39+
public BlockGenerator $blocks;
4240

4341
/**
4442
* Initializes the `Editor` instance by parsing the provided JSON data into blocks
@@ -59,15 +57,15 @@ public function __construct(
5957
throw new EditorException('Invalid JSON data provided');
6058
}
6159

62-
$this->blocks = BlockFactory::parse($data, $registry);
60+
$this->blocks = new BlockGenerator($this->data, $this->registry);
6361
}
6462

6563
/**
6664
* Get the parsed blocks.
6765
*
68-
* @return iterable<BlockInterface> The collection of parsed blocks.
66+
* @return BlockGenerator The collection of parsed blocks.
6967
*/
70-
public function getBlocks(): iterable
68+
public function getBlocks(): BlockGenerator
7169
{
7270
return $this->blocks;
7371
}

src/Sanitizer/Sanitizer.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace Devscast\EditorJs\Sanitizer;
1515

16+
use Devscast\EditorJs\Definition\BlockGenerator;
1617
use Devscast\EditorJs\Definition\BlockInterface;
1718
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
1819
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
@@ -39,30 +40,31 @@ final class Sanitizer implements SanitizerInterface
3940
* This method iterates over the given blocks, converts each block to its HTML
4041
* representation, and sanitizes the resulting HTML using the configured sanitizer.
4142
*
42-
* @param iterable<BlockInterface> $blocks The collection of blocks to sanitize.
43+
* @param BlockGenerator $blocks The collection of blocks to sanitize.
4344
* @return string The sanitized HTML content.
4445
*/
4546
#[\Override]
46-
public function sanitize(iterable $blocks): string
47+
public function sanitize(BlockGenerator $blocks): string
4748
{
48-
$blocks = \iterator_to_array($blocks);
49-
$sanitizer = new HtmlSanitizer($this->getConfig($blocks));
49+
$config = $this->getConfig($blocks->getIterator());
50+
$sanitizer = new HtmlSanitizer($config);
5051

51-
return array_reduce(
52-
$blocks,
53-
fn (string $html, BlockInterface $block) => $html . $sanitizer->sanitize($block->toHtml()),
54-
initial: ''
55-
);
52+
$html = '';
53+
foreach ($blocks->getIterator() as $block) {
54+
$html .= $sanitizer->sanitize($block->toHtml());
55+
}
56+
57+
return $html;
5658
}
5759

5860
/**
5961
* Generate the HTML sanitizer configuration based on the allowed tags and attributes
6062
* of the provided blocks.
6163
*
62-
* @param array<BlockInterface> $blocks The array of blocks to configure the sanitizer for.
64+
* @param iterable<BlockInterface> $blocks The collection of blocks to configure the sanitizer for.
6365
* @return HtmlSanitizerConfig The configured HTML sanitizer.
6466
*/
65-
private function getConfig(array $blocks): HtmlSanitizerConfig
67+
private function getConfig(iterable $blocks): HtmlSanitizerConfig
6668
{
6769
$config = (new HtmlSanitizerConfig())
6870
->allowRelativeLinks()

src/Sanitizer/SanitizerInterface.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Devscast\EditorJs\Sanitizer;
1515

16-
use Devscast\EditorJs\Definition\BlockInterface;
16+
use Devscast\EditorJs\Definition\BlockGenerator;
1717

1818
/**
1919
* Interface SanitizerInterface.
@@ -34,8 +34,8 @@ interface SanitizerInterface
3434
* This method processes the given blocks, converts them to their HTML representation,
3535
* and ensures the resulting HTML is safe and adheres to the allowed tags and attributes.
3636
*
37-
* @param iterable<BlockInterface> $blocks The collection of blocks to sanitize.
37+
* @param BlockGenerator $blocks The collection of blocks to sanitize.
3838
* @return string The sanitized HTML content.
3939
*/
40-
public function sanitize(iterable $blocks): string;
40+
public function sanitize(BlockGenerator $blocks): string;
4141
}

tests/Blocks/AbstractBlockTest.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Devscast\EditorJs\Tests\Blocks;
66

7-
use Devscast\EditorJs\Definition\AbstractBlock;
87
use Devscast\EditorJs\Editor;
98
use Devscast\EditorJs\Exception\EditorException;
109
use Generator;
@@ -27,6 +26,7 @@ public function testBlockParsingWithInvalidSchema(string $data, string $message)
2726
{
2827
$this->expectException(EditorException::class);
2928
$editor = new Editor($data);
29+
$editor->getHtml();
3030
}
3131

3232
#[DataProvider('getValidSchemasProvider')]
@@ -35,8 +35,6 @@ public function testBlockParsingWithValidSchema(string $data, string $expected,
3535
$this->assertNotEmpty($data);
3636

3737
$editor = new Editor($data);
38-
$this->assertNotEmpty($editor->getBlocks());
39-
$this->assertContainsOnlyInstancesOf(AbstractBlock::class, $editor->getBlocks());
4038
$this->assertSame($expected, $editor->getHtml(), $message);
4139
}
4240
}

tests/Blocks/ParagraphTest.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@ class ParagraphTest extends AbstractBlockTest
1515
#[\Override]
1616
public static function getValidSchemasProvider(): \Generator
1717
{
18+
yield [
19+
<<< JSON
20+
{
21+
"time": 1739980616433,
22+
"version": "2",
23+
"blocks": [{"id": "mhTl6ghSkV", "type": "invalid-paragraph","data": {"text": "This is a test paragraph"}}]
24+
}
25+
JSON,
26+
'',
27+
'invalid block is filtered out',
28+
];
1829
yield [
1930
<<< JSON
2031
{
2132
"time": 1739980616433,
33+
"version": "2",
2234
"blocks": [
2335
{
2436
"id": "mhTl6ghSkV",
@@ -37,6 +49,7 @@ public static function getValidSchemasProvider(): \Generator
3749
<<< JSON
3850
{
3951
"time": 1739980616433,
52+
"version": "2",
4053
"blocks": [
4154
{
4255
"id": "mhTl6ghSkV",
@@ -62,6 +75,7 @@ public static function getValidSchemasProvider(): \Generator
6275
<<< JSON
6376
{
6477
"time": 1739980616433,
78+
"version": "2",
6579
"blocks": [
6680
{
6781
"id": "mhTl6ghSkV",
@@ -80,6 +94,7 @@ public static function getValidSchemasProvider(): \Generator
8094
<<< JSON
8195
{
8296
"time": 1739980616433,
97+
"version": "2",
8398
"blocks": [
8499
{
85100
"id": "mhTl6ghSkV",
@@ -98,6 +113,7 @@ public static function getValidSchemasProvider(): \Generator
98113
<<< JSON
99114
{
100115
"time": 1739980616433,
116+
"version": "2",
101117
"blocks": [
102118
{
103119
"id": "mhTl6ghSkV",
@@ -127,16 +143,8 @@ public static function getInvalidSchemasProvider(): \Generator
127143
<<< JSON
128144
{
129145
"time": 1739980616433,
130-
"blocks": [{"id": "mhTl6ghSkV", "type": "invalid-type","data": {"text": "This is a test paragraph"}}]
131-
}
132-
JSON,
133-
'invalid block type',
134-
];
135-
yield [
136-
<<< JSON
137-
{
138-
"time": 1739980616433,
139-
"blocks": [{"id": "", "type": "invalid-type","data": {"text": "This is a test paragraph"}}]
146+
"version": "2",
147+
"blocks": [{"id": "", "type": "paragraph","data": {"text": "This is a test paragraph"}}]
140148
}
141149
JSON,
142150
'invalid block id',
@@ -145,6 +153,7 @@ public static function getInvalidSchemasProvider(): \Generator
145153
<<< JSON
146154
{
147155
"time": 1739980616433,
156+
"version": "2",
148157
"blocks": [{"id": "mhTl6ghSkV", "type": "paragraph","data": ""}]
149158
}
150159
JSON,
@@ -154,6 +163,7 @@ public static function getInvalidSchemasProvider(): \Generator
154163
<<< JSON
155164
{
156165
"time": 1739980616433,
166+
"version": "2",
157167
"blocks": [{"id": "mhTl6ghSkV", "type": "paragraph","data": {}}]
158168
}
159169
JSON,

0 commit comments

Comments
 (0)