Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
30.00% covered (danger)
30.00%
3 / 10
CRAP
64.86% covered (warning)
64.86%
24 / 37
ForeignAttributes
0.00% covered (danger)
0.00%
0 / 1
30.00% covered (danger)
30.00%
3 / 10
28.05
63.89% covered (warning)
63.89%
23 / 36
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 offsetExists
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 offsetGet
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 offsetSet
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 offsetUnset
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getValues
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 count
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getIterator
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getObjects
0.00% covered (danger)
0.00%
0 / 1
6.17
83.33% covered (warning)
83.33%
15 / 18
 merge
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
<?php
namespace Wikimedia\RemexHtml\TreeBuilder;
use Wikimedia\RemexHtml\HTMLData;
use Wikimedia\RemexHtml\Tokenizer\Attribute;
use Wikimedia\RemexHtml\Tokenizer\Attributes;
/**
 * An Attributes class for storing attributes on foreign elements, which may
 * have namespaces. Features lazy adjustment of attribute name case.
 */
class ForeignAttributes implements Attributes {
    /** @var Attributes */
    protected $unadjusted;
    /** @var array The map of lowercase attribute name to correct attribute name */
    protected $table;
    /** @var Attribute[]|null */
    protected $attrObjects;
    /**
     * Adjustment tables for the case of attributes on MathML and SVG elements
     */
    private static $adjustmentTables = [
        'math' => [
            'definitionurl' => 'definitionURL',
        ],
        'svg' => [
            'attributename' => 'attributeName',
            'attributetype' => 'attributeType',
            'basefrequency' => 'baseFrequency',
            'baseprofile' => 'baseProfile',
            'calcmode' => 'calcMode',
            'clippathunits' => 'clipPathUnits',
            'diffuseconstant' => 'diffuseConstant',
            'edgemode' => 'edgeMode',
            'filterunits' => 'filterUnits',
            'glyphref' => 'glyphRef',
            'gradienttransform' => 'gradientTransform',
            'gradientunits' => 'gradientUnits',
            'kernelmatrix' => 'kernelMatrix',
            'kernelunitlength' => 'kernelUnitLength',
            'keypoints' => 'keyPoints',
            'keysplines' => 'keySplines',
            'keytimes' => 'keyTimes',
            'lengthadjust' => 'lengthAdjust',
            'limitingconeangle' => 'limitingConeAngle',
            'markerheight' => 'markerHeight',
            'markerunits' => 'markerUnits',
            'markerwidth' => 'markerWidth',
            'maskcontentunits' => 'maskContentUnits',
            'maskunits' => 'maskUnits',
            'numoctaves' => 'numOctaves',
            'pathlength' => 'pathLength',
            'patterncontentunits' => 'patternContentUnits',
            'patterntransform' => 'patternTransform',
            'patternunits' => 'patternUnits',
            'pointsatx' => 'pointsAtX',
            'pointsaty' => 'pointsAtY',
            'pointsatz' => 'pointsAtZ',
            'preservealpha' => 'preserveAlpha',
            'preserveaspectratio' => 'preserveAspectRatio',
            'primitiveunits' => 'primitiveUnits',
            'refx' => 'refX',
            'refy' => 'refY',
            'repeatcount' => 'repeatCount',
            'repeatdur' => 'repeatDur',
            'requiredextensions' => 'requiredExtensions',
            'requiredfeatures' => 'requiredFeatures',
            'specularconstant' => 'specularConstant',
            'specularexponent' => 'specularExponent',
            'spreadmethod' => 'spreadMethod',
            'startoffset' => 'startOffset',
            'stddeviation' => 'stdDeviation',
            'stitchtiles' => 'stitchTiles',
            'surfacescale' => 'surfaceScale',
            'systemlanguage' => 'systemLanguage',
            'tablevalues' => 'tableValues',
            'targetx' => 'targetX',
            'targety' => 'targetY',
            'textlength' => 'textLength',
            'viewbox' => 'viewBox',
            'viewtarget' => 'viewTarget',
            'xchannelselector' => 'xChannelSelector',
            'ychannelselector' => 'yChannelSelector',
            'zoomandpan' => 'zoomAndPan',
        ],
        'other' => [],
    ];
    /**
     * The potentially namespaced attributes, and the namespaces they belong to.
     * Excepting xmlns since it is very special.
     */
    private static $namespaceMap = [
        'xlink:actuate' => HTMLData::NS_XLINK,
        'xlink:arcrole' => HTMLData::NS_XLINK,
        'xlink:href' => HTMLData::NS_XLINK,
        'xlink:role' => HTMLData::NS_XLINK,
        'xlink:show' => HTMLData::NS_XLINK,
        'xlink:title' => HTMLData::NS_XLINK,
        'xlink:type' => HTMLData::NS_XLINK,
        'xml:lang' => HTMLData::NS_XML,
        'xml:space' => HTMLData::NS_XML,
        'xmlns:xlink' => HTMLData::NS_XMLNS,
    ];
    /**
     * @param Attributes $unadjusted The unadjusted attributes from the Tokenizer
     * @param string $type The element type, which may be "math", "svg" or "other".
     */
    public function __construct( Attributes $unadjusted, $type ) {
        $this->unadjusted = $unadjusted;
        $this->table = self::$adjustmentTables[$type];
    }
    public function offsetExists( $offset ) {
        $offset = $this->table[$offset] ?? $offset;
        return $this->unadjusted->offsetExists( $offset );
    }
    public function &offsetGet( $offset ) {
        $offset = $this->table[$offset] ?? $offset;
        $value = &$this->unadjusted->offsetGet( $offset );
        return $value;
    }
    public function offsetSet( $offset, $value ) {
        throw new TreeBuilderError( "Setting foreign attributes is not supported" );
    }
    public function offsetUnset( $offset ) {
        throw new TreeBuilderError( "Setting foreign attributes is not supported" );
    }
    public function getValues() {
        $result = [];
        foreach ( $this->unadjusted->getValues() as $name => $value ) {
            $name = $this->table[$name] ?? $name;
            $result[$name] = $value;
        }
        return $result;
    }
    public function count() {
        return $this->unadjusted->count();
    }
    public function getIterator() {
        return new \ArrayIterator( $this->getValues() );
    }
    public function getObjects() {
        if ( $this->attrObjects === null ) {
            $result = [];
            foreach ( $this->unadjusted->getValues() as $name => $value ) {
                if ( isset( $this->table[$name] ) ) {
                    $name = $this->table[$name];
                }
                if ( $name === 'xmlns' ) {
                    $prefix = null;
                    $namespace = HTMLData::NS_XMLNS;
                    $localName = $name;
                } elseif ( isset( self::$namespaceMap[$name] ) ) {
                    $namespace = self::$namespaceMap[$name];
                    list( $prefix, $localName ) = explode( ':', $name, 2 );
                } else {
                    $prefix = null;
                    $namespace = null;
                    $localName = $name;
                }
                $result[$name] = new Attribute( $name, $namespace, $prefix, $localName, $value );
            }