Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.20% covered (success)
94.20%
65 / 69
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
MathNativeMML
94.20% covered (success)
94.20%
65 / 69
76.92% covered (warning)
76.92%
10 / 13
21.09
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 renderReferenceEntry
85.71% covered (warning)
85.71%
12 / 14
0.00% covered (danger)
0.00%
0 / 1
2.01
 addLinksToMathML
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
3
 getMainConfig
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getHookContainer
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doRender
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
6
 getChecker
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getHtmlOutput
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 readFromCache
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 writeCache
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHookContainer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMainConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setChecker
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * MediaWiki math extension
4 *
5 * @copyright 2002-2023 various MediaWiki contributors
6 * @license GPL-2.0-or-later
7 */
8
9namespace MediaWiki\Extension\Math;
10
11use DOMDocument;
12use DOMXPath;
13use MediaWiki\Config\Config;
14use MediaWiki\Extension\Math\InputCheck\LocalChecker;
15use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmath;
16use MediaWiki\HookContainer\HookContainer;
17use MediaWiki\MediaWikiServices;
18use StatusValue;
19use Wikimedia\ObjectCache\WANObjectCache;
20
21/**
22 * Converts LaTeX to MathML using PHP
23 */
24class MathNativeMML extends MathMathML {
25    private LocalChecker $checker;
26    private Config $mainConfig;
27    private HookContainer $hookContainer;
28
29    /** @inheritDoc */
30    public function __construct( $tex = '', $params = [], $cache = null, $mathConfig = null ) {
31        parent::__construct( $tex, $params, $cache, $mathConfig );
32        $this->setMode( MathConfig::MODE_NATIVE_MML );
33    }
34
35    public static function renderReferenceEntry(
36        array &$entry,
37        ?MathConfig $mathConfig = null,
38        ?HookContainer $hookContainer = null,
39        ?Config $config = null ): bool {
40        $mathConfig ??= Math::getMathConfig();
41        $hookContainer ??= MediaWikiServices::getInstance()->getHookContainer();
42        $config ??= MediaWikiServices::getInstance()->getMainConfig();
43        $renderer = new MathNativeMML( $entry['input'], $entry['params'], WANObjectCache::newEmpty(), $mathConfig );
44        $renderer->setRawError( true );
45        $renderer->setHookContainer( $hookContainer );
46        $renderer->setMainConfig( $config );
47        $renderer->setChecker( new LocalChecker( WANObjectCache::newEmpty(), $entry['input'], 'tex' ) );
48        $result = $renderer->render();
49        $entry['output'] = $renderer->getMathml();
50        if ( !$result ) {
51            $entry['skipped'] = true;
52            $entry['error'] = $renderer->getLastError();
53        }
54        return $result;
55    }
56
57    /**
58     * Adds hyperlinks to MathML elements
59     * @param string $qid Identifier for symbol mapping
60     * @param string $mathml Input MathML HTML content
61     * @return string Modified MathML with either anchor tags or hrefs
62     */
63    private function addLinksToMathML( string $qid, string $mathml ): string {
64        $services = MediaWikiServices::getInstance();
65        $connector = $services->getService( 'Math.WikibaseConnector' );
66        $language = $services->getContentLanguageCode()->toString();
67        $qmap = $connector->getUrlFromSymbol( $qid, $language );
68        $dom = new DOMDocument();
69        $dom->loadXML( $mathml );
70        $xpath = new DOMXPath( $dom );
71        $xpath->registerNamespace( 'mathml', 'http://www.w3.org/1998/Math/MathML' );
72        $linkableElements = $xpath->query( '//mathml:mi | //mathml:mo | //mathml:mtext' );
73        foreach ( $linkableElements as $linkableElement ) {
74            $textValue = $linkableElement->nodeValue;
75            if ( empty( $qmap[$textValue]['url'] ) ) {
76                continue;
77            }
78            $a = $dom->createElement( 'a' );
79            $a->setAttribute( 'href', $qmap[$textValue]['url'] );
80            $a->setAttribute( 'title', $qmap[$textValue]['title'] );
81            $a->nodeValue = $linkableElement->nodeValue;
82            $linkableElement->nodeValue = "";
83            $linkableElement->appendChild( $a );
84        }
85        return $dom->saveXML();
86    }
87
88    public function getMainConfig(): Config {
89        $this->mainConfig ??= MediaWikiServices::getInstance()->getMainConfig();
90        return $this->mainConfig;
91    }
92
93    public function getHookContainer(): HookContainer {
94        $this->hookContainer ??= MediaWikiServices::getInstance()->getHookContainer();
95        return $this->hookContainer;
96    }
97
98    protected function doRender(): StatusValue {
99        $checker = $this->getChecker();
100        $checker->setContext( $this );
101        $checker->setHookContainer( $this->getHookContainer() );
102        $presentation = $checker->getPresentationMathMLFragment();
103        $config = $this->getMainConfig();
104        $attributes = [ 'class' => 'mwe-math-element' ];
105        if ( $this->getID() !== '' ) {
106            $attributes['id'] = $this->getID();
107        }
108        if ( $this->getMathStyle() == 'display' ) {
109            $attributes['display'] = 'block';
110        }
111        $root = new MMLmath( "", $attributes );
112        $mathElement = $root->encapsulateRaw( $presentation ?? '' );
113        if ( isset( $this->params['qid'] ) &&
114            preg_match( '/Q\d+/', $this->params['qid'] ) &&
115            $config->get( "MathEnableFormulaLinks" ) ) {
116            $this->setMathml( $this->addLinksToMathML(
117                $this->params['qid'],
118                $mathElement ) );
119        } else {
120            $this->setMathml( $mathElement );
121        }
122        return StatusValue::newGood();
123    }
124
125    protected function getChecker(): LocalChecker {
126        $this->checker ??= Math::getCheckerFactory()
127            ->newLocalChecker( $this->tex, $this->getInputType(), $this->isPurge() );
128        return $this->checker;
129    }
130
131    /**
132     * @inheritDoc
133     */
134    public function getHtmlOutput( bool $svg = true ): string {
135        return $this->getMathml();
136    }
137
138    public function readFromCache(): bool {
139        return false;
140    }
141
142    /** @inheritDoc */
143    public function writeCache() {
144        return true;
145    }
146
147    private function setHookContainer( HookContainer $hookContainer ) {
148        $this->hookContainer = $hookContainer;
149    }
150
151    private function setMainConfig( Config $config ) {
152        $this->mainConfig = $config;
153    }
154
155    private function setChecker( LocalChecker $checker ) {
156        $this->checker = $checker;
157    }
158}