Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
TemplateInfo
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 4
306
0.00% covered (danger)
0.00%
0 / 1
 __clone
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 newFromJsonArray
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
20
 jsonClassHintFor
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 toJsonArray
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
90
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;
10
11class TemplateInfo implements JsonCodecable {
12    use JsonCodecableTrait;
13
14    /**
15     * The target wikitext
16     */
17    public ?string $targetWt = null;
18
19    /**
20     * The parser function name
21     */
22    public ?string $func = null;
23
24    /**
25     * The URL of the target
26     */
27    public ?string $href = null;
28
29    /**
30     * Param infos indexed by key (ParamInfo->k)
31     * @var list<ParamInfo>
32     */
33    public array $paramInfos = [];
34
35    /**
36     * The type of template (template, templatearg, parserfunction).
37     * @note For backward-compatibility reasons, this property is
38     * not serialized/deserialized.
39     * @var 'template'|'templatearg'|'parserfunction'|null
40     */
41    public ?string $type = null;
42
43    /**
44     * The index into data-parsoid.pi
45     */
46    public ?int $i = null;
47
48    public function __clone() {
49        foreach ( $this->paramInfos as &$pi ) {
50            $pi = clone $pi;
51        }
52    }
53
54    /** @inheritDoc */
55    public static function newFromJsonArray( array $json ): TemplateInfo {
56        $ti = new TemplateInfo;
57        $ti->targetWt = $json['target']['wt'] ?? null;
58        $ti->href = $json['target']['href'] ?? null;
59        $ti->paramInfos = [];
60        $params = (array)( $json['params'] ?? null );
61
62        if ( isset( $json['target']['key'] ) ) {
63            $ti->func = $json['target']['key'];
64            $ti->targetWt .= ':' . $params['1']->wt;
65            // Downshift all params by 1
66            $numKeys = count( $params );
67            for ( $i = 1; $i < $numKeys; $i++ ) {
68                $params[(string)$i] = $params[(string)( $i + 1 )];
69            }
70            unset( $params[(string)$numKeys] );
71        } else {
72            $ti->func = $json['target']['function'] ?? null;
73        }
74
75        foreach ( $params as $k => $v ) {
76            // Converting $params to an array can turn the keys into ints,
77            // so we need to explicitly cast them back to string.
78            $info = new ParamInfo( (string)$k );
79            $info->valueWt = $v->wt ?? null;
80            $info->html = $v->html ?? null;
81            $info->keyWt = $v->key->wt ?? null;
82            $ti->paramInfos[] = $info;
83        }
84        $ti->i = $json['i'] ?? null;
85        return $ti;
86    }
87
88    /** @inheritDoc */
89    public static function jsonClassHintFor( string $keyname ) {
90        static $hints = null;
91        if ( $hints === null ) {
92            $hints = [
93                // The most deeply nested stdClass structure is "wt" inside
94                // "key" inside a parameter:
95                //     "params":{"1":{"key":{"wt":"..."}}}
96                'params' => Hint::build(
97                    stdClass::class, Hint::ALLOW_OBJECT,
98                    Hint::STDCLASS, Hint::ALLOW_OBJECT,
99                    Hint::STDCLASS, Hint::ALLOW_OBJECT
100                ),
101            ];
102        }
103        return $hints[$keyname] ?? null;
104    }
105
106    /** @inheritDoc */
107    public function toJsonArray(): array {
108        // This is a complicated serialization, but necessary for
109        // backward compatibility with existing data-mw
110
111        $v3PF = $this->type === 'v3parserfunction';
112        $target = [ 'wt' => $this->targetWt ];
113        if ( $this->func !== null ) {
114            $key = $v3PF ? 'key' : 'function';
115            $target[$key] = $this->func;
116        }
117        if ( $this->href !== null ) {
118            $target['href'] = $this->href;
119        }
120        $params = [];
121        foreach ( $this->paramInfos as $info ) {
122            // Non-standard serialization of ParamInfo, alas.
123            $param = [
124                'wt' => $info->valueWt,
125            ];
126            if ( $info->html !== null ) {
127                $param['html'] = $info->html;
128            }
129            if ( $info->keyWt !== null ) {
130                $param['key'] = (object)[
131                    'wt' => $info->keyWt,
132                ];
133            }
134            $params[$info->k] = (object)$param;
135        }
136
137        if ( $v3PF ) {
138            // Upshift all params by 1
139            $numKeys = count( $params );
140            for ( $i = $numKeys; $i > 0; $i-- ) {
141                $params[(string)( $i + 1 )] = $params[(string)$i];
142            }
143
144            $matches = null;
145            preg_match( '/^([^:]*):(.*)$/', $this->targetWt, $matches );
146            $params['1'] = (object)[ 'wt' => $matches[2] ];
147            $target['wt'] = $matches[1];
148        }
149
150        return [
151            'target' => $target,
152            'params' => (object)$params,
153            'i' => $this->i,
154        ];
155    }
156}