Completed
Pull Request — master (#24)
by
unknown
02:45
created

LabsHelper::normalizeProject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
3
namespace AppBundle\Helper;
4
5
use Symfony\Component\Config\Definition\Exception\Exception;
6
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
7
use Symfony\Component\DependencyInjection\ContainerInterface;
8
use Symfony\Component\VarDumper\VarDumper;
9
10
class LabsHelper
11
{
12
    /** @var string */
13
    protected $dbName;
14
15
    /** @var \Doctrine\DBAL\Connection */
16
    protected $client;
17
18
    /** @var ContainerInterface */
19
    protected $container;
20
21
    /** @var string */
22
    protected $url;
23
24
    public function __construct(ContainerInterface $container)
25
    {
26
        $this->container = $container;
27
    }
28
29
    public function checkEnabled($tool)
30
    {
31
        if (!$this->container->getParameter("enable.$tool")) {
32
            throw new NotFoundHttpException('This tool is disabled');
33
        }
34
    }
35
36
    /**
37
     * Is xTools connecting to MMF Labs?
38
     * @return boolean
39
     */
40
    public function isLabs()
41
    {
42
        return (bool)$this->container->getParameter('app.is_labs');
43
    }
44
45
    /**
46
     * Set up LabsHelper::$client and return the database name, wiki name,
47
     * and URL of a given project.
48
     * @todo: Handle failure better
49
     * @return string[] With keys 'dbName', 'wikiName', 'url', and 'lang'.
50
     */
51
    public function databasePrepare($project = 'wiki')
52
    {
53
        if ($this->container->getParameter('app.single_wiki')) {
54
            $dbName = $this->container->getParameter('database_replica_name');
55
            $wikiName = 'wiki';
56
            $url = $this->container->getParameter('wiki_url');
57
            $lang = $this->container->getParameter('lang');
58
        } else {
59
            $metaData = $this->getProjectMetadata($project);
60
61
            if (!$metaData) {
62
                throw new Exception("Unable to find project '$project'");
63
            }
64
65
            $dbName = $metaData['dbname'];
66
            $wikiName = $metaData['name'];
67
            $url = $metaData['url'];
68
            $lang = $metaData['lang'];
69
        }
70
71
        $this->dbName = $dbName;
72
        $this->url = $url;
73
74
        return [ 'dbName' => $dbName, 'wikiName' => $wikiName, 'url' => $url, 'lang' => $lang ];
75
    }
76
77
    /**
78
     * Get the record for the given project in the meta.wiki table
79
     * @param  string $project Valid project in the formats:
80
     *                         https://en.wikipedia.org, en.wikipedia, enwiki
81
     * @return array|false     Database record or false if no record was found.
82
     *                         Relevant values returned include the 'dbname' (enwiki),
83
     *                         'lang', 'name' (Wikipedia) and 'url' (https://en.wikipeda.org)
84
     */
85
    private function getProjectMetadata($project)
86
    {
87
        // First, run through our project map.  This is expected to return back
88
        // to the project name if there is no defined mapping.
89
        if ($this->container->hasParameter("app.project.$project")) {
90
            $project = $this->container->getParameter("app.project.$project");
91
        }
92
93
        // Grab the connection to the meta database
94
        $this->client = $this->container
95
            ->get('doctrine')
96
            ->getManager('meta')
97
            ->getConnection();
98
99
        // Create the query we're going to run against the meta database
100
        $wikiQuery = $this->client->createQueryBuilder();
101
        $wikiQuery
102
            ->select([ 'dbname', 'name', 'url', 'lang' ])
103
            ->from('wiki')
104
            ->where($wikiQuery->expr()->eq('dbname', ':project'))
105
            // The meta database will have the project's URL stored as https://en.wikipedia.org
106
            // so we need to query for it accordingly, trying different variations the user
107
            // might have inputted.
108
            ->orwhere($wikiQuery->expr()->like('url', ':projectUrl'))
109
            ->orwhere($wikiQuery->expr()->like('url', ':projectUrl2'))
110
            ->setParameter('project', $project)
111
            ->setParameter('projectUrl', "https://$project")
112
            ->setParameter('projectUrl2', "https://$project.org");
113
        $wikiStatement = $wikiQuery->execute();
114
115
        // Fetch the wiki data
116
        $wikis = $wikiStatement->fetchAll();
117
118
        // Return false if we can't find the wiki
119
        if (count($wikis) < 1) {
120
            return false;
121
        }
122
123
        // Otherwise, return the first result (in the rare event there are more than one).
124
        return $wikis[0];
125
    }
126
127
    /**
128
     * Returns a project's domain (en.wikipedia) given various formats
129
     * @param  string $project Valid project in the formats:
130
     *                         https://en.wikipedia.org, en.wikipedia, enwiki
131
     * @return string|false    lang.project.org ('url' value for that wiki)
132
     *                         or false if project was not found
133
     */
134
    public function normalizeProject($project)
135
    {
136
        $metaData = $this->getProjectMetadata($project);
137
138
        if ($metaData) {
139
            // Get domain from the first result (in the rare event there are more than one).
140
            return preg_replace("/https?:\/\//", '', $metaData['url']);
141
        } else {
142
            return false;
143
        }
144
    }
145
146
    /**
147
     * Get a list of all projects.
148
     */
149
    public function allProjects()
150
    {
151
        $wikiQuery = $this->client->createQueryBuilder();
152
        $wikiQuery->select([ 'dbName', 'name', 'url' ])->from('wiki');
153
        $stmt = $wikiQuery->execute();
154
        $out = $stmt->fetchAll();
155
        return $out;
156
    }
157
158
    /**
159
     * All mapping tables to environment-specific names, as specified in config/table_map.yml
160
     * Used for example to convert revision -> revision_replica
161
     * https://wikitech.wikimedia.org/wiki/Help:Tool_Labs/Database#Tables_for_revision_or_logging_queries_involving_user_names_and_IDs
162
     *
163
     * @param string $table  Table name
164
     * @param string $dbName Database name
165
     *
166
     * @return string Converted table name
167
     */
168
    public function getTable($table, $dbName = null)
169
    {
170
        // Use the table specified in the table mapping configuration, if present.
171
        $mapped = false;
172
        if ($this->container->hasParameter("app.table.$table")) {
173
            $mapped = true;
174
            $table = $this->container->getParameter("app.table.$table");
175
        }
176
177
        // For 'revision' and 'logging' tables (actually views) on Labs, use the indexed versions
178
        // (that have some rows hidden, e.g. for revdeleted users).
179
        $isLoggingOrRevision = in_array($table, ['revision', 'logging', 'archive']);
180
        if (!$mapped && $isLoggingOrRevision && $this->isLabs()) {
181
            $table = $table."_userindex";
182
        }
183
184
        // Figure out database name.
185
        // Use class variable for the database name if not set via function parameter.
186
        $dbNameActual = $dbName ? $dbName : $this->dbName;
187
        if ($this->isLabs() && substr($dbNameActual, -2) != '_p') {
188
            // Append '_p' if this is labs.
189
            $dbNameActual .= '_p';
190
        }
191
192
        return $dbNameActual ? "$dbNameActual.$table" : $table;
193
    }
194
195
    // TODO: figure out how to use Doctrine to query host 'tools-db'
196
}
197