Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 17 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
NodeData | |
0.00% |
0 / 17 |
|
0.00% |
0 / 2 |
132 | |
0.00% |
0 / 1 |
__clone | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
cloneNodeData | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
72 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\NodeData; |
5 | |
6 | use Wikimedia\Parsoid\DOM\Node; |
7 | use Wikimedia\Parsoid\Utils\DOMCompat; |
8 | use Wikimedia\Parsoid\Utils\DOMUtils; |
9 | |
10 | // phpcs:disable MediaWiki.Commenting.PropertyDocumentation.ObjectTypeHintVar |
11 | |
12 | /** |
13 | * This object stores data associated with a single DOM node. |
14 | * |
15 | * Using undeclared properties reduces memory usage and CPU time if the |
16 | * property is null in more than about 75% of instances. There are typically |
17 | * a very large number of NodeData objects, so this optimisation is worthwhile. |
18 | * |
19 | * @property object|null $mw_variant |
20 | * @property int|null $storedId |
21 | */ |
22 | #[\AllowDynamicProperties] |
23 | class NodeData { |
24 | /** |
25 | * The unserialized data-parsoid attribute |
26 | */ |
27 | public ?DataParsoid $parsoid = null; |
28 | |
29 | /** |
30 | * The unserialized data-mw attribute |
31 | */ |
32 | public ?DataMw $mw = null; |
33 | |
34 | public function __clone() { |
35 | // PHP performs a shallow clone then calls this method. |
36 | // Make a deep clone of every object-valued property. |
37 | // (Note that decoded 'rich attributes' are object-valued properties; |
38 | // undecoded rich attributes and hints are not, but they are immutable |
39 | // and thus don't need to be deep-cloned.) |
40 | foreach ( get_object_vars( $this ) as $k => $v ) { |
41 | if ( is_object( $v ) ) { |
42 | $this->$k = clone $v; |
43 | } |
44 | } |
45 | } |
46 | |
47 | /** |
48 | * Deep clone this object |
49 | * If $stripSealedFragments is true, sealed DOMFragment included in expanded attributes are deleted in the |
50 | * clone. |
51 | * @param bool $stripSealedFragments |
52 | * @return self |
53 | */ |
54 | public function cloneNodeData( bool $stripSealedFragments = false ): self { |
55 | $nd = clone $this; |
56 | |
57 | if ( $this->mw === null || !$stripSealedFragments ) { |
58 | return $nd; |
59 | } |
60 | |
61 | // Avoid cloning sealed DOMFragments that may occur in expanded attributes |
62 | foreach ( $nd->mw->attribs ?? [] as $attr ) { |
63 | // Look for DOMFragments in both key and value of DataMwAttrib |
64 | foreach ( [ 'key', 'value' ] as $part ) { |
65 | if ( |
66 | isset( $attr->$part['html'] ) && |
67 | str_contains( $attr->$part['html'], 'mw:DOMFragment/sealed' ) |
68 | ) { |
69 | $doc = DOMUtils::parseHTML( $attr->$part['html'] ); |
70 | DOMUtils::visitDOM( $doc, static function ( Node $node ) { |
71 | if ( |
72 | DOMUtils::matchTypeOf( $node, '#^mw:DOMFragment/sealed/\w+$#D' ) |
73 | ) { |
74 | DOMCompat::getParentElement( $node )->removeChild( $node ); |
75 | } |
76 | } ); |
77 | $attr->$part['html'] = DOMCompat::getInnerHTML( DOMCompat::getBody( $doc ) ); |
78 | } |
79 | } |
80 | } |
81 | |
82 | return $nd; |
83 | } |
84 | } |