Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.74% covered (warning)
76.74%
33 / 43
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
DatabaseIndexForPageLookup
76.74% covered (warning)
76.74%
33 / 43
33.33% covered (danger)
33.33%
2 / 6
20.63
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
 isPageTitleInCache
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIndexForPageTitle
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 findIndexTitle
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
7.19
 findPossibleIndexTitleBasedOnName
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
5.12
 findIndexesWhichLinkTo
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
3.07
1<?php
2
3namespace ProofreadPage\Page;
4
5use MediaWiki\MediaWikiServices;
6use MediaWiki\Title\Title;
7use RepoGroup;
8
9/**
10 * @license GPL-2.0-or-later
11 *
12 * Allows to retrieve the Index: page for a Page: page
13 */
14class DatabaseIndexForPageLookup implements IndexForPageLookup {
15
16    /**
17     * @var int
18     */
19    private $indexNamespaceId;
20
21    /**
22     * @var RepoGroup
23     */
24    private $repoGroup;
25
26    /** @var (?Title)[] */
27    private $cache = [];
28
29    /**
30     * @param int $indexNamespaceId
31     * @param RepoGroup $repoGroup
32     */
33    public function __construct( $indexNamespaceId, RepoGroup $repoGroup ) {
34        $this->indexNamespaceId = $indexNamespaceId;
35        $this->repoGroup = $repoGroup;
36    }
37
38    /**
39     * @inheritDoc
40     */
41    public function isPageTitleInCache( Title $pageTitle ): bool {
42        return array_key_exists( $pageTitle->getDBkey(), $this->cache );
43    }
44
45    /**
46     * @inheritDoc
47     */
48    public function getIndexForPageTitle( Title $pageTitle ) {
49        $cacheKey = $pageTitle->getDBkey();
50        if ( !array_key_exists( $cacheKey, $this->cache ) ) {
51            $this->cache[$cacheKey] = $this->findIndexTitle( $pageTitle );
52        }
53        return $this->cache[$cacheKey];
54    }
55
56    /**
57     * @param Title $pageTitle
58     * @return ?Title
59     */
60    private function findIndexTitle( Title $pageTitle ) {
61        $possibleIndexTitle = $this->findPossibleIndexTitleBasedOnName( $pageTitle );
62
63        // Try to find links from Index: pages
64        $indexesThatLinksHere = [];
65        foreach ( $this->findIndexesWhichLinkTo( $pageTitle ) as $indexTitle ) {
66            // It is the same as the linked file, we know it's this Index:
67            if ( $possibleIndexTitle !== null && $indexTitle->equals( $possibleIndexTitle ) ) {
68                return $indexTitle;
69            }
70            $indexesThatLinksHere[] = $indexTitle;
71        }
72        if ( $indexesThatLinksHere ) {
73            // TODO: what should we do if there are more than 1 possible index?
74            return reset( $indexesThatLinksHere );
75        }
76
77        return $possibleIndexTitle;
78    }
79
80    /**
81     * @param Title $pageTitle
82     * @return Title|null the index page based on the name of the Page: page and the existence
83     *   of a file with the same name
84     */
85    private function findPossibleIndexTitleBasedOnName( Title $pageTitle ) {
86        $m = explode( '/', $pageTitle->getText(), 2 );
87
88        $fileTitle = Title::makeTitleSafe( NS_FILE, $m[0] );
89        if ( $fileTitle === null ) {
90            return null;
91        }
92
93        $file = $this->repoGroup->findFile( $fileTitle );
94        if ( !$file || !$file->exists() ) {
95            return null;
96        }
97
98        if ( !( $file->isMultipage() xor isset( $m[1] ) ) ) {
99            return Title::makeTitle(
100                $this->indexNamespaceId, $file->getTitle()->getText()
101            );
102        }
103
104        return null;
105    }
106
107    /**
108     * @param Title $title
109     * @return \Generator<Title>
110     */
111    private function findIndexesWhichLinkTo( Title $title ) {
112        $services = MediaWikiServices::getInstance();
113        $dbr = $services->getDBLoadBalancerFactory()->getReplicaDatabase();
114        $linksMigration = $services->getLinksMigration();
115        $titleConditions = $linksMigration->getLinksConditions( 'pagelinks', $title );
116        $results = $dbr->newSelectQueryBuilder()
117            ->select( [ 'page_namespace', 'page_title' ] )
118            ->from( 'page' )
119            ->join( 'pagelinks', null, 'pl_from=page_id' )
120            ->where( [ 'pl_from_namespace' => $this->indexNamespaceId ] )
121            ->andWhere( $titleConditions )
122            ->caller( __METHOD__ )->fetchResultSet();
123
124        foreach ( $results as $row ) {
125            $indexTitle = Title::makeTitle( $row->page_namespace, $row->page_title );
126            if ( $indexTitle !== null ) {
127                yield $indexTitle;
128            }
129        }
130    }
131}