Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
MediaStructure
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 6
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 isRedLink
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getResource
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAlt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMediaUrl
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 parse
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Core;
5
6use Wikimedia\Parsoid\DOM\Element;
7use Wikimedia\Parsoid\DOM\Node;
8use Wikimedia\Parsoid\Utils\DiffDOMUtils;
9use Wikimedia\Parsoid\Utils\DOMCompat;
10use Wikimedia\Parsoid\Utils\WTUtils;
11use Wikimedia\Parsoid\Wikitext\Consts;
12
13/**
14 * All media should have a fixed structure:
15 *
16 * ```
17 * <conatinerElt>
18 *  <linkElt><mediaElt /></linkElt>
19 *  <captionElt>...</captionElt>
20 * </containerElt>
21 * ```
22 *
23 * Pull out this fixed structure, being as generous as possible with
24 * possibly-broken HTML.
25 */
26class MediaStructure {
27
28    /**
29     * Node names: figure, span
30     *
31     * @var ?Element
32     */
33     public $containerElt;
34
35    /**
36     * Node names: a, span
37     *
38     * @var ?Element
39     */
40    public $linkElt;
41
42    /**
43     * Node names: img, audio, video, span
44     *
45     * @var ?Element
46     */
47    public $mediaElt;
48
49    /**
50     * Node names: figcaption
51     *
52     * @var ?Element
53     */
54    public $captionElt;
55
56    /**
57     * @param Element $mediaElt
58     * @param ?Element $linkElt
59     * @param ?Element $containerElt
60     */
61    public function __construct(
62        Element $mediaElt, ?Element $linkElt = null,
63        ?Element $containerElt = null
64    ) {
65        $this->mediaElt = $mediaElt;
66        $this->linkElt = $linkElt;
67        $this->containerElt = $containerElt;
68        if ( $containerElt && DOMCompat::nodeName( $containerElt ) === 'figure' ) {
69            // FIXME: Support last child, which is not the linkElt, as the caption?
70            $this->captionElt = DOMCompat::querySelector( $containerElt, 'figcaption' );
71        }
72    }
73
74    /**
75     * We were not able to fetch info for the title, so the media was
76     * considered missing and rendered as a span.
77     *
78     * @return bool
79     */
80    public function isRedLink(): bool {
81        return ( DOMCompat::nodeName( $this->mediaElt ) === 'span' );
82    }
83
84    /**
85     * @return ?string the resource name if it exists, otherwise null
86     */
87    public function getResource(): ?string {
88        return DOMCompat::getAttribute( $this->mediaElt, 'resource' );
89    }
90
91    /**
92     * @return ?string The alt text if it exists, otherwise null
93     */
94    public function getAlt(): ?string {
95        return DOMCompat::getAttribute( $this->mediaElt, 'alt' );
96    }
97
98    /**
99     * @return ?string The media href if it exists, otherwise null.
100     */
101    public function getMediaUrl(): ?string {
102        return $this->linkElt ?
103            DOMCompat::getAttribute( $this->linkElt, 'href' ) :
104            null;
105    }
106
107    /**
108     * @param Node $node
109     * @return ?MediaStructure
110     */
111    public static function parse( Node $node ): ?MediaStructure {
112        if ( !WTUtils::isGeneratedFigure( $node ) ) {
113            return null;
114        }
115        '@phan-var Element $node';  // @var Element $node
116        $linkElt = $node;
117        do {
118            // Try being lenient, maybe there was a content model violation when
119            // parsing and an active formatting element was reopened in the wrapper
120            $linkElt = DiffDOMUtils::firstNonSepChild( $linkElt );
121        } while (
122            $linkElt instanceof Element && DOMCompat::nodeName( $linkElt ) !== 'a' &&
123            isset( Consts::$HTML['FormattingTags'][DOMCompat::nodeName( $linkElt )] )
124        );
125        if (
126            !( $linkElt instanceof Element &&
127                in_array( DOMCompat::nodeName( $linkElt ), [ 'a', 'span' ], true ) )
128        ) {
129            if ( $linkElt instanceof Element ) {
130                // Try being lenient, maybe this is the media element and we don't
131                // have a link elt.  See the test, "Image: from basic HTML (1)"
132                $mediaElt = $linkElt;
133                $linkElt = null;
134            } else {
135                return null;
136            }
137        } else {
138            $mediaElt = DiffDOMUtils::firstNonSepChild( $linkElt );
139        }
140        if (
141            !( $mediaElt instanceof Element &&
142                in_array( DOMCompat::nodeName( $mediaElt ), [ 'audio', 'img', 'span', 'video' ], true ) )
143        ) {
144            return null;
145        }
146        return new MediaStructure( $mediaElt, $linkElt, $node );
147    }
148
149}