fix: harden the Divi BlockTree against opaque/invalid writes
Final fix wave for stage 2a, load-bearing for 2b's writers:
- Block::setAttrs()/Container::insertChild()/removeChild() refuse on an opaque
block (undecodable attrs or a global module) instead of silently corrupting it.
- Container::append() is now @internal (Parser only) and refuses on a void
block, so a child can no longer be silently dropped from the serialized output.
- Block::create() validates the block name against namespace/block-name.
- Json::decodeObject() returns null (-> opaque) for a top-level object whose
keys are a plain list ("0","1",...) rather than returning [] and letting the
Serializer re-encode it as a corrupting JSON array.
- AttrMap::set() rejects empty path segments and keeps an existing stdClass
value an object when rewrapping it (no silent object->list flip); AttrMap::get()
is updated in step so it can still read back through such an object.
- Block::copy() deep-clones stdClass attribute values instead of sharing them.
- Json::encode()'s existing JsonException-on-invalid-UTF-8 behaviour is documented.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,16 @@ final class ParserTest extends TestCase
|
||||
self::assertSame([], $broken->children()[0]->attrs());
|
||||
}
|
||||
|
||||
public function testMarksListLikeTopLevelAttributesOpaque(): void
|
||||
{
|
||||
$content = '<!-- wp:divi/text {"0":"a","1":"b"} /-->';
|
||||
$document = (new Parser())->parse($content);
|
||||
|
||||
self::assertTrue($document->children()[0]->isOpaque());
|
||||
self::assertSame([], $document->children()[0]->attrs());
|
||||
self::assertSame($content, Serializer::document($document));
|
||||
}
|
||||
|
||||
public function testKeepsEmptyAndListLikeObjectsAsObjects(): void
|
||||
{
|
||||
$document = (new Parser())->parse($this->fixture('synthetic-mixed.html'));
|
||||
|
||||
Reference in New Issue
Block a user