Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
DataMw
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 6
870
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 isEmpty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __clone
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
132
 jsonClassHintFor
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 toJsonArray
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
 newFromJsonArray
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\NodeData;
5
6use stdClass;
7use Wikimedia\JsonCodec\Hint;
8use Wikimedia\JsonCodec\JsonCodecable;
9use Wikimedia\JsonCodec\JsonCodecableTrait;
10use Wikimedia\Parsoid\Tokens\SourceRange;
11
12/**
13 * Editing data for a DOM node.  Managed by DOMDataUtils::get/setDataMw().
14 *
15 * To reduce memory usage, most of the properties need to be dynamic, but
16 * we use the property declarations below to allow type checking.
17 *
18 * @property list<string|TemplateInfo> $parts
19 * @property string $name
20 * @property string $extPrefix
21 * @property string $extSuffix
22 * @property list<DataMwAttrib> $attribs Extended attributes of an HTML tag
23 * @property string $src
24 * @property string $caption
25 * @property string $thumb
26 * @property bool $autoGenerated
27 * @property list<DataMwError> $errors
28 * @property stdClass $body
29 * @property mixed $html
30 * @property float $scale
31 * @property string $starttime
32 * @property string $endtime
33 * @property string $thumbtime
34 * @property string $page
35 * == Annotations ==
36 * @property string $rangeId
37 * @property SourceRange $wtOffsets
38 * @property bool $extendedRange
39 * @property stdClass $attrs Attributes for an extension tag or annotation (T367616 should be renamed)
40 */
41#[\AllowDynamicProperties]
42class DataMw implements JsonCodecable {
43    use JsonCodecableTrait;
44
45    public function __construct( array $initialVals = [] ) {
46        foreach ( $initialVals as $k => $v ) {
47            // @phan-suppress-next-line PhanNoopSwitchCases
48            switch ( $k ) {
49                // Add cases here for components which should be instantiated
50                // as proper classes.
51                default:
52                    $this->$k = $v;
53                    break;
54            }
55        }
56    }
57
58    /** Returns true iff there are no dynamic properties of this object. */
59    public function isEmpty(): bool {
60        return ( (array)$this ) === [];
61    }
62
63    public function __clone() {
64        // Deep clone non-primitive properties
65        if ( isset( $this->parts ) ) {
66            foreach ( $this->parts as &$part ) {
67                if ( !is_string( $part ) ) {
68                    $part = clone $part;
69                }
70            }
71        }
72        // Properties which are lists of cloneable objects
73        foreach ( [ 'attribs', 'errors' ] as $prop ) {
74            if ( isset( $this->$prop ) ) {
75                foreach ( $this->$prop as &$item ) {
76                    $item = clone $item;
77                }
78            }
79        }
80        // Properties which are cloneable objects
81        foreach ( [ 'wtOffsets' ] as $prop ) {
82            if ( isset( $this->$prop ) ) {
83                $this->$prop = clone $this->$prop;
84            }
85        }
86        // Generic stdClass, use PHP serialization as a kludge
87        foreach ( [ 'body', 'attrs' ] as $prop ) {
88            if ( isset( $this->$prop ) ) {
89                $this->$prop = unserialize( serialize( $this->$prop ) );
90            }
91        }
92    }
93
94    /** @inheritDoc */
95    public static function jsonClassHintFor( string $keyname ) {
96        static $hints = null;
97        if ( $hints === null ) {
98            $hints = [
99                'attribs' => Hint::build( DataMwAttrib::class, Hint::USE_SQUARE, Hint::LIST ),
100                // T367616: 'attrs' should be renamed to 'extAttrs'
101                'attrs' => Hint::build( stdClass::class, Hint::ALLOW_OBJECT ),
102                'body' => Hint::build( stdClass::class, Hint::ALLOW_OBJECT ),
103                'wtOffsets' => Hint::build( SourceRange::class, Hint::USE_SQUARE ),
104                'parts' => Hint::build( TemplateInfo::class, Hint::STDCLASS, Hint::LIST ),
105                'errors' => Hint::build( DataMwError::class, Hint::LIST ),
106            ];
107        }
108        return $hints[$keyname] ?? null;
109    }
110
111    /** @inheritDoc */
112    public function toJsonArray(): array {
113        $result = (array)$this;
114        // T367141: Third party clients (eg Cite) create arrays instead of
115        // error objects.  We should convert them to proper DataMwError
116        // objects once those exist.
117        if ( isset( $result['errors'] ) ) {
118            $result['errors'] = array_map(
119                fn ( $e ) => is_array( $e ) ? DataMwError::newFromJsonArray( $e ) :
120                    ( $e instanceof DataMwError ? $e : DataMwError::newFromJsonArray( (array)$e ) ),
121                $result['errors']
122            );
123        }
124        // Legacy encoding of parts.
125        if ( isset( $result['parts'] ) ) {
126            $result['parts'] = array_map( static function ( $p ) {
127                if ( $p instanceof TemplateInfo ) {
128                    $type = $p->type ?? 'template';
129                    if ( $type === 'parserfunction' ) {
130                        $type = 'template';
131                    }
132                    $pp = (object)[];
133                    $pp->$type = $p;
134                    return $pp;
135                }
136                return $p;
137            }, $result['parts'] );
138        }
139        return $result;
140    }
141
142    /** @inheritDoc */
143    public static function newFromJsonArray( array $json ): DataMw {
144        // Decode legacy encoding of parts.
145        if ( isset( $json['parts'] ) ) {
146            $json['parts'] = array_map( static function ( $p ) {
147                if ( is_object( $p ) ) {
148                    $type = 'template';
149                    if ( isset( $p->templatearg ) ) {
150                        $type = 'templatearg';
151                    }
152                    $p = $p->$type;
153                    if ( isset( $p->func ) ) {
154                        $type = 'parserfunction';
155                    }
156                    $p->type = $type;
157                }
158                return $p;
159            }, $json['parts'] );
160        }
161        return new DataMw( $json );
162    }
163}