Completed
Push — master ( 190d9b...e1c46c )
by MusikAnimal
13s
created

Repository::getTableName()   D

Complexity

Conditions 9
Paths 12

Size

Total Lines 33
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 4.909
c 0
b 0
f 0
cc 9
eloc 14
nc 12
nop 3
1
<?php
2
/**
3
 * This file contains only the Repository class.
4
 */
5
6
namespace Xtools;
7
8
use Doctrine\DBAL\Connection;
9
use Mediawiki\Api\MediawikiApi;
10
use Psr\Cache\CacheItemPoolInterface;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
use Symfony\Component\DependencyInjection\Container;
14
use Symfony\Component\Stopwatch\Stopwatch;
15
use GuzzleHttp\Promise\Promise;
16
17
/**
18
 * A repository is responsible for retrieving data from wherever it lives (databases, APIs,
19
 * filesystems, etc.)
20
 */
21
abstract class Repository
22
{
23
24
    /** @var Container The application's DI container. */
25
    protected $container;
26
27
    /** @var Connection The database connection to the meta database. */
28
    private $metaConnection;
29
30
    /** @var Connection The database connection to the projects' databases. */
31
    private $projectsConnection;
32
33
    /** @var Connection The database connection to other tools' databases.  */
34
    private $toolsConnection;
35
36
    /** @var GuzzleHttp\Client $apiConnection Connection to XTools API. */
37
    private $apiConnection;
38
39
    /** @var CacheItemPoolInterface The cache. */
40
    protected $cache;
41
42
    /** @var LoggerInterface The log. */
43
    protected $log;
44
45
    /** @var Stopwatch The stopwatch for time profiling. */
46
    protected $stopwatch;
47
48
    /**
49
     * Create a new Repository with nothing but a null-logger.
50
     */
51
    public function __construct()
52
    {
53
        $this->log = new NullLogger();
54
    }
55
56
    /**
57
     * Set the DI container.
58
     * @param Container $container
59
     */
60
    public function setContainer(Container $container)
61
    {
62
        $this->container = $container;
63
        $this->cache = $container->get('cache.app');
64
        $this->log = $container->get('logger');
65
        $this->stopwatch = $container->get('debug.stopwatch');
66
    }
67
68
    /**
69
     * Get the NullLogger instance.
70
     * @return NullLogger
71
     */
72
    public function getLog()
73
    {
74
        return $this->log;
75
    }
76
77
    /**
78
     * Get the database connection for the 'meta' database.
79
     * @return Connection
80
     */
81
    protected function getMetaConnection()
82
    {
83
        if (!$this->metaConnection instanceof Connection) {
84
            $this->metaConnection = $this->container
85
                ->get('doctrine')
86
                ->getManager("meta")
87
                ->getConnection();
88
        }
89
        return $this->metaConnection;
90
    }
91
92
    /**
93
     * Get the database connection for the 'projects' database.
94
     * @return Connection
95
     */
96
    protected function getProjectsConnection()
97
    {
98
        if (!$this->projectsConnection instanceof Connection) {
99
            $this->projectsConnection = $this->container
100
                ->get('doctrine')
101
                ->getManager('replicas')
102
                ->getConnection();
103
        }
104
        return $this->projectsConnection;
105
    }
106
107
    /**
108
     * Get the database connection for the 'tools' database
109
     * (the one that other tools store data in).
110
     * @return Connection
111
     */
112
    protected function getToolsConnection()
113
    {
114
        if (!$this->toolsConnection instanceof Connection) {
115
            $this->toolsConnection = $this->container
116
                ->get('doctrine')
117
                ->getManager("toolsdb")
118
                ->getConnection();
119
        }
120
        return $this->toolsConnection;
121
    }
122
123
    /**
124
     * Get the API object for the given project.
125
     *
126
     * @param Project $project
127
     * @return MediawikiApi
128
     */
129
    public function getMediawikiApi(Project $project)
130
    {
131
        $apiPath = $this->container->getParameter('api_path');
132
        if ($apiPath) {
133
            $api = MediawikiApi::newFromApiEndpoint($project->getUrl().$apiPath);
134
        } else {
135
            $api = MediawikiApi::newFromPage($project->getUrl());
136
        }
137
        return $api;
138
    }
139
140
    /**
141
     * Is XTools connecting to MMF Labs?
142
     * @return boolean
143
     */
144
    public function isLabs()
145
    {
146
        return (bool)$this->container->getParameter('app.is_labs');
147
    }
148
149
    /**
150
     * Make a request to the XTools API. This requires that app.multithread.api_url be set.
151
     * @param string $endpoint Relative path to endpoint with relevant query parameters.
152
     * @param bool $async Set to true to asynchronously query and return a promise.
153
     * @return mixed|GuzzleHttp\Promise\Promise
154
     */
155
    public function queryXToolsApi($endpoint, $async = false)
156
    {
157
        if (!$this->apiConnection) {
158
            $this->apiConnection = $this->container->get('guzzle.client.xtools');
159
        }
160
161
        // Ensure there's a trailing slash.
162
        $apiRoot = trim($this->container->getParameter('app.multithread.api_url'), '/');
163
        $endpoint = $apiRoot . '/api/' . $endpoint;
164
165
        if ($async) {
166
            return $this->apiConnection->getAsync($endpoint);
167
        } else {
168
            return $this->apiConnection->get($endpoint);
169
        }
170
    }
171
172
    /**
173
     * Normalize and quote a table name for use in SQL.
174
     *
175
     * @param string $databaseName
176
     * @param string $tableName
177
     * @param string|null [$tableExtension] Optional table extension, which will only get used if we're on labs.
178
     * @return string Fully-qualified and quoted table name.
179
     */
180
    public function getTableName($databaseName, $tableName, $tableExtension = null)
181
    {
182
        $mapped = false;
183
184
        // This is a workaround for a one-to-many mapping
185
        // as required by Labs. We combine $tableName with
186
        // $tableExtension in order to generate the new table name
187
        if ($this->isLabs() && $tableExtension !== null) {
188
            $mapped = true;
189
            $tableName = $tableName . '_' . $tableExtension;
190
        } elseif ($this->container->hasParameter("app.table.$tableName")) {
191
            // Use the table specified in the table mapping configuration, if present.
192
            $mapped = true;
193
            $tableName = $this->container->getParameter("app.table.$tableName");
194
        }
195
196
        // For 'revision' and 'logging' tables (actually views) on Labs, use the indexed versions
197
        // (that have some rows hidden, e.g. for revdeleted users).
198
        // This is a safeguard in case table mapping isn't properly set up.
199
        $isLoggingOrRevision = in_array($tableName, ['revision', 'logging', 'archive']);
200
        if (!$mapped && $isLoggingOrRevision && $this->isLabs()) {
201
            $tableName = $tableName."_userindex";
202
        }
203
204
        // Figure out database name.
205
        // Use class variable for the database name if not set via function parameter.
206
        if ($this->isLabs() && substr($databaseName, -2) != '_p') {
207
            // Append '_p' if this is labs.
208
            $databaseName .= '_p';
209
        }
210
211
        return "`$databaseName`.`$tableName`";
212
    }
213
}
214