Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
61.11% |
22 / 36 |
|
27.27% |
3 / 11 |
CRAP | |
0.00% |
0 / 1 |
ForeignAttributes | |
61.11% |
22 / 36 |
|
27.27% |
3 / 11 |
34.00 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
offsetExists | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
offsetGet | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
offsetSet | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
offsetUnset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getValues | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
count | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIterator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getObjects | |
83.33% |
15 / 18 |
|
0.00% |
0 / 1 |
6.17 | |||
merge | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
clone | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Wikimedia\RemexHtml\TreeBuilder; |
4 | |
5 | use Wikimedia\RemexHtml\HTMLData; |
6 | use Wikimedia\RemexHtml\Tokenizer\Attribute; |
7 | use Wikimedia\RemexHtml\Tokenizer\Attributes; |
8 | |
9 | /** |
10 | * An Attributes class for storing attributes on foreign elements, which may |
11 | * have namespaces. Features lazy adjustment of attribute name case. |
12 | */ |
13 | class ForeignAttributes implements Attributes { |
14 | /** @var Attributes */ |
15 | protected $unadjusted; |
16 | |
17 | /** @var array The map of lowercase attribute name to correct attribute name */ |
18 | protected $table; |
19 | |
20 | /** @var Attribute[]|null */ |
21 | protected $attrObjects; |
22 | |
23 | /** |
24 | * Adjustment tables for the case of attributes on MathML and SVG elements |
25 | * @var array |
26 | */ |
27 | private static $adjustmentTables = [ |
28 | 'math' => [ |
29 | 'definitionurl' => 'definitionURL', |
30 | ], |
31 | 'svg' => [ |
32 | 'attributename' => 'attributeName', |
33 | 'attributetype' => 'attributeType', |
34 | 'basefrequency' => 'baseFrequency', |
35 | 'baseprofile' => 'baseProfile', |
36 | 'calcmode' => 'calcMode', |
37 | 'clippathunits' => 'clipPathUnits', |
38 | 'diffuseconstant' => 'diffuseConstant', |
39 | 'edgemode' => 'edgeMode', |
40 | 'filterunits' => 'filterUnits', |
41 | 'glyphref' => 'glyphRef', |
42 | 'gradienttransform' => 'gradientTransform', |
43 | 'gradientunits' => 'gradientUnits', |
44 | 'kernelmatrix' => 'kernelMatrix', |
45 | 'kernelunitlength' => 'kernelUnitLength', |
46 | 'keypoints' => 'keyPoints', |
47 | 'keysplines' => 'keySplines', |
48 | 'keytimes' => 'keyTimes', |
49 | 'lengthadjust' => 'lengthAdjust', |
50 | 'limitingconeangle' => 'limitingConeAngle', |
51 | 'markerheight' => 'markerHeight', |
52 | 'markerunits' => 'markerUnits', |
53 | 'markerwidth' => 'markerWidth', |
54 | 'maskcontentunits' => 'maskContentUnits', |
55 | 'maskunits' => 'maskUnits', |
56 | 'numoctaves' => 'numOctaves', |
57 | 'pathlength' => 'pathLength', |
58 | 'patterncontentunits' => 'patternContentUnits', |
59 | 'patterntransform' => 'patternTransform', |
60 | 'patternunits' => 'patternUnits', |
61 | 'pointsatx' => 'pointsAtX', |
62 | 'pointsaty' => 'pointsAtY', |
63 | 'pointsatz' => 'pointsAtZ', |
64 | 'preservealpha' => 'preserveAlpha', |
65 | 'preserveaspectratio' => 'preserveAspectRatio', |
66 | 'primitiveunits' => 'primitiveUnits', |
67 | 'refx' => 'refX', |
68 | 'refy' => 'refY', |
69 | 'repeatcount' => 'repeatCount', |
70 | 'repeatdur' => 'repeatDur', |
71 | 'requiredextensions' => 'requiredExtensions', |
72 | 'requiredfeatures' => 'requiredFeatures', |
73 | 'specularconstant' => 'specularConstant', |
74 | 'specularexponent' => 'specularExponent', |
75 | 'spreadmethod' => 'spreadMethod', |
76 | 'startoffset' => 'startOffset', |
77 | 'stddeviation' => 'stdDeviation', |
78 | 'stitchtiles' => 'stitchTiles', |
79 | 'surfacescale' => 'surfaceScale', |
80 | 'systemlanguage' => 'systemLanguage', |
81 | 'tablevalues' => 'tableValues', |
82 | 'targetx' => 'targetX', |
83 | 'targety' => 'targetY', |
84 | 'textlength' => 'textLength', |
85 | 'viewbox' => 'viewBox', |
86 | 'viewtarget' => 'viewTarget', |
87 | 'xchannelselector' => 'xChannelSelector', |
88 | 'ychannelselector' => 'yChannelSelector', |
89 | 'zoomandpan' => 'zoomAndPan', |
90 | ], |
91 | 'other' => [], |
92 | ]; |
93 | |
94 | /** |
95 | * The potentially namespaced attributes, and the namespaces they belong to. |
96 | * Excepting xmlns since it is very special. |
97 | * |
98 | * @var array |
99 | */ |
100 | private static $namespaceMap = [ |
101 | 'xlink:actuate' => HTMLData::NS_XLINK, |
102 | 'xlink:arcrole' => HTMLData::NS_XLINK, |
103 | 'xlink:href' => HTMLData::NS_XLINK, |
104 | 'xlink:role' => HTMLData::NS_XLINK, |
105 | 'xlink:show' => HTMLData::NS_XLINK, |
106 | 'xlink:title' => HTMLData::NS_XLINK, |
107 | 'xlink:type' => HTMLData::NS_XLINK, |
108 | 'xml:lang' => HTMLData::NS_XML, |
109 | 'xml:space' => HTMLData::NS_XML, |
110 | 'xmlns:xlink' => HTMLData::NS_XMLNS, |
111 | ]; |
112 | |
113 | /** |
114 | * @param Attributes $unadjusted The unadjusted attributes from the Tokenizer |
115 | * @param string $type The element type, which may be "math", "svg" or "other". |
116 | */ |
117 | public function __construct( Attributes $unadjusted, $type ) { |
118 | $this->unadjusted = $unadjusted; |
119 | $this->table = self::$adjustmentTables[$type]; |
120 | } |
121 | |
122 | public function offsetExists( $offset ): bool { |
123 | $offset = $this->table[$offset] ?? $offset; |
124 | return $this->unadjusted->offsetExists( $offset ); |
125 | } |
126 | |
127 | public function &offsetGet( $offset ): string { |
128 | $offset = $this->table[$offset] ?? $offset; |
129 | $value = &$this->unadjusted->offsetGet( $offset ); |
130 | return $value; |
131 | } |
132 | |
133 | public function offsetSet( $offset, $value ): void { |
134 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
135 | throw new TreeBuilderError( "Setting foreign attributes is not supported" ); |
136 | } |
137 | |
138 | public function offsetUnset( $offset ): void { |
139 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
140 | throw new TreeBuilderError( "Setting foreign attributes is not supported" ); |
141 | } |
142 | |
143 | public function getValues() { |
144 | $result = []; |
145 | foreach ( $this->unadjusted->getValues() as $name => $value ) { |
146 | $name = $this->table[$name] ?? $name; |
147 | $result[$name] = $value; |
148 | } |
149 | return $result; |
150 | } |
151 | |
152 | public function count(): int { |
153 | return $this->unadjusted->count(); |
154 | } |
155 | |
156 | public function getIterator(): \ArrayIterator { |
157 | return new \ArrayIterator( $this->getValues() ); |
158 | } |
159 | |
160 | public function getObjects() { |
161 | if ( $this->attrObjects === null ) { |
162 | $result = []; |
163 | foreach ( $this->unadjusted->getValues() as $name => $value ) { |
164 | if ( isset( $this->table[$name] ) ) { |
165 | $name = $this->table[$name]; |
166 | } |
167 | if ( $name === 'xmlns' ) { |
168 | $prefix = null; |
169 | $namespace = HTMLData::NS_XMLNS; |
170 | $localName = $name; |
171 | } elseif ( isset( self::$namespaceMap[$name] ) ) { |
172 | $namespace = self::$namespaceMap[$name]; |
173 | [ $prefix, $localName ] = explode( ':', $name, 2 ); |
174 | } else { |
175 | $prefix = null; |
176 | $namespace = null; |
177 | $localName = $name; |
178 | } |
179 | $result[$name] = new Attribute( $name, $namespace, $prefix, $localName, $value ); |
180 | } |
181 | $this->attrObjects = $result; |
182 | } |
183 | return $this->attrObjects; |
184 | } |
185 | |
186 | public function merge( Attributes $other ) { |
187 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
188 | throw new TreeBuilderError( __METHOD__ . ': unimplemented' ); |
189 | } |
190 | |
191 | public function clone() { |
192 | return $this; |
193 | } |
194 | } |