Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.49% covered (success)
91.49%
43 / 47
89.47% covered (warning)
89.47%
17 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageLinksTable
91.49% covered (success)
91.49%
43 / 47
89.47% covered (warning)
89.47%
17 / 19
25.39
0.00% covered (danger)
0.00%
0 / 1
 setParserOutput
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getTableName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFromField
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTargetIdField
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExistingFields
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNewLinkIDs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getExistingLinks
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getExistingLinkIDs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isExisting
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isInNewSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 needForcedLinkRefresh
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 insertLink
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 deleteLink
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 makePageReferenceValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deduplicateLinkIds
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 finishUpdate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 invalidateImageDescriptions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 virtualDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Deferred\LinksUpdate;
4
5use MediaWiki\JobQueue\Utils\PurgeJobUtils;
6use MediaWiki\Page\PageReferenceValue;
7use MediaWiki\Parser\ParserOutput;
8use MediaWiki\Parser\ParserOutputLinkTypes;
9use MediaWiki\Title\Title;
10
11/**
12 * imagelinks
13 *
14 * Link ID format: string image name
15 *
16 * @since 1.38
17 */
18class ImageLinksTable extends TitleLinksTable {
19    /**
20     * @var array New links with the name in the key, value arbitrary
21     */
22    private $newLinks;
23
24    /**
25     * @var array Existing links with the name in the key, value arbitrary
26     */
27    private $existingLinks;
28
29    public function setParserOutput( ParserOutput $parserOutput ) {
30        // Convert the format of the local links
31        $this->newLinks = [];
32        foreach (
33            $parserOutput->getLinkList( ParserOutputLinkTypes::MEDIA )
34            as [ 'link' => $link ]
35        ) {
36            $this->newLinks[$link->getDBkey()] = 1;
37        }
38    }
39
40    /** @inheritDoc */
41    protected function getTableName() {
42        return 'imagelinks';
43    }
44
45    /** @inheritDoc */
46    protected function getFromField() {
47        return 'il_from';
48    }
49
50    /** @inheritDoc */
51    protected function getTargetIdField() {
52        return 'il_target_id';
53    }
54
55    /** @inheritDoc */
56    protected function getExistingFields() {
57        return [ 'lt_title' ];
58    }
59
60    /** @inheritDoc */
61    protected function getNewLinkIDs() {
62        foreach ( $this->newLinks as $link => $unused ) {
63            yield (string)$link;
64        }
65    }
66
67    /**
68     * Get existing links with the name in the key, value arbitrary.
69     *
70     * @return array
71     */
72    private function getExistingLinks() {
73        if ( $this->existingLinks === null ) {
74            $this->existingLinks = [];
75            foreach ( $this->fetchExistingRows() as $row ) {
76                $this->existingLinks[$row->lt_title] = true;
77            }
78        }
79        return $this->existingLinks;
80    }
81
82    /** @inheritDoc */
83    protected function getExistingLinkIDs() {
84        foreach ( $this->getExistingLinks() as $link => $unused ) {
85            yield (string)$link;
86        }
87    }
88
89    /** @inheritDoc */
90    protected function isExisting( $linkId ) {
91        return \array_key_exists( $linkId, $this->getExistingLinks() );
92    }
93
94    /** @inheritDoc */
95    protected function isInNewSet( $linkId ) {
96        return \array_key_exists( $linkId, $this->newLinks );
97    }
98
99    /** @inheritDoc */
100    protected function needForcedLinkRefresh() {
101        return $this->isCrossNamespaceMove();
102    }
103
104    /** @inheritDoc */
105    protected function insertLink( $linkId ) {
106        $this->insertRow( [
107            'il_from_namespace' => $this->getSourcePage()->getNamespace(),
108            'il_target_id' => $this->linkTargetLookup->acquireLinkTargetId(
109                $this->makeTitle( $linkId ),
110                $this->getDB()
111            ),
112        ] );
113    }
114
115    /** @inheritDoc */
116    protected function deleteLink( $linkId ) {
117        $this->deleteRow( [
118            'il_target_id' => $this->linkTargetLookup->acquireLinkTargetId(
119                $this->makeTitle( $linkId ),
120                $this->getDB()
121            )
122        ] );
123    }
124
125    /** @inheritDoc */
126    protected function makePageReferenceValue( $linkId ): PageReferenceValue {
127        return PageReferenceValue::localReference( NS_FILE, $linkId );
128    }
129
130    /** @inheritDoc */
131    protected function makeTitle( $linkId ): Title {
132        return Title::makeTitle( NS_FILE, $linkId );
133    }
134
135    /** @inheritDoc */
136    protected function deduplicateLinkIds( $linkIds ) {
137        if ( !is_array( $linkIds ) ) {
138            $linkIds = iterator_to_array( $linkIds );
139        }
140        return array_unique( $linkIds );
141    }
142
143    protected function finishUpdate() {
144        // A update of namespace on cross namespace move is detected as insert + delete,
145        // but the updates are not needed there.
146        $allInsertedLinks = array_column( $this->insertedLinks, 0 );
147        $allDeletedLinks = array_column( $this->deletedLinks, 0 );
148        $insertedLinks = array_diff( $allInsertedLinks, $allDeletedLinks );
149        $deletedLinks = array_diff( $allDeletedLinks, $allInsertedLinks );
150
151        $this->invalidateImageDescriptions( $insertedLinks, $deletedLinks );
152    }
153
154    /**
155     * Invalidate all image description pages which had links added or removed
156     * @param array $insertedLinks
157     * @param array $deletedLinks
158     */
159    private function invalidateImageDescriptions( array $insertedLinks, array $deletedLinks ) {
160        PurgeJobUtils::invalidatePages(
161            $this->getDB(), NS_FILE,
162            array_merge( $insertedLinks, $deletedLinks ) );
163    }
164
165    /** @inheritDoc */
166    protected function virtualDomain() {
167        return self::VIRTUAL_DOMAIN;
168    }
169}