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
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* A repository is responsible for retrieving data from wherever it lives (databases, APIs, |
18
|
|
|
* filesystems, etc.) |
19
|
|
|
*/ |
20
|
|
|
abstract class Repository |
21
|
|
|
{ |
22
|
|
|
|
23
|
|
|
/** @var Container The application's DI container. */ |
24
|
|
|
protected $container; |
25
|
|
|
|
26
|
|
|
/** @var Connection The database connection to the meta database. */ |
27
|
|
|
private $metaConnection; |
28
|
|
|
|
29
|
|
|
/** @var Connection The database connection to the projects' databases. */ |
30
|
|
|
private $projectsConnection; |
31
|
|
|
|
32
|
|
|
/** @var Connection The database connection to other tools' databases. */ |
33
|
|
|
private $toolsConnection; |
34
|
|
|
|
35
|
|
|
/** @var CacheItemPoolInterface The cache. */ |
36
|
|
|
protected $cache; |
37
|
|
|
|
38
|
|
|
/** @var LoggerInterface The log. */ |
39
|
|
|
protected $log; |
40
|
|
|
|
41
|
|
|
/** @var Stopwatch The stopwatch for time profiling. */ |
42
|
|
|
protected $stopwatch; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Create a new Repository with nothing but a null-logger. |
46
|
|
|
*/ |
47
|
|
|
public function __construct() |
48
|
|
|
{ |
49
|
|
|
$this->log = new NullLogger(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Set the DI container. |
54
|
|
|
* @param Container $container |
55
|
|
|
*/ |
56
|
|
|
public function setContainer(Container $container) |
57
|
|
|
{ |
58
|
|
|
$this->container = $container; |
59
|
|
|
$this->cache = $container->get('cache.app'); |
60
|
|
|
$this->log = $container->get('logger'); |
61
|
|
|
$this->stopwatch = $container->get('debug.stopwatch'); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Get the database connection for the 'meta' database. |
66
|
|
|
* @return Connection |
67
|
|
|
*/ |
68
|
|
|
protected function getMetaConnection() |
69
|
|
|
{ |
70
|
|
|
if (!$this->metaConnection instanceof Connection) { |
71
|
|
|
$this->metaConnection = $this->container |
72
|
|
|
->get('doctrine') |
73
|
|
|
->getManager("meta") |
74
|
|
|
->getConnection(); |
75
|
|
|
} |
76
|
|
|
return $this->metaConnection; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Get the database connection for the 'projects' database. |
81
|
|
|
* @return Connection |
82
|
|
|
*/ |
83
|
|
|
protected function getProjectsConnection() |
84
|
|
|
{ |
85
|
|
|
if (!$this->projectsConnection instanceof Connection) { |
86
|
|
|
$this->projectsConnection = $this->container |
87
|
|
|
->get('doctrine') |
88
|
|
|
->getManager('replicas') |
89
|
|
|
->getConnection(); |
90
|
|
|
} |
91
|
|
|
return $this->projectsConnection; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Get the database connection for the 'tools' database |
96
|
|
|
* (the one that other tools store data in). |
97
|
|
|
* @return Connection |
98
|
|
|
*/ |
99
|
|
|
protected function getToolsConnection() |
100
|
|
|
{ |
101
|
|
|
if (!$this->toolsConnection instanceof Connection) { |
102
|
|
|
$this->toolsConnection = $this->container |
103
|
|
|
->get('doctrine') |
104
|
|
|
->getManager("toolsdb") |
105
|
|
|
->getConnection(); |
106
|
|
|
} |
107
|
|
|
return $this->toolsConnection; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get the API object for the given project. |
112
|
|
|
* |
113
|
|
|
* @param Project $project |
114
|
|
|
* @return MediawikiApi |
115
|
|
|
*/ |
116
|
|
|
public function getMediawikiApi(Project $project) |
117
|
|
|
{ |
118
|
|
|
$apiPath = $this->container->getParameter('api_path'); |
119
|
|
|
if ($apiPath) { |
120
|
|
|
$api = MediawikiApi::newFromApiEndpoint($project->getUrl().$apiPath); |
121
|
|
|
} else { |
122
|
|
|
$api = MediawikiApi::newFromPage($project->getUrl()); |
123
|
|
|
} |
124
|
|
|
return $api; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Is XTools connecting to MMF Labs? |
129
|
|
|
* @return boolean |
130
|
|
|
*/ |
131
|
|
|
public function isLabs() |
132
|
|
|
{ |
133
|
|
|
return (bool)$this->container->getParameter('app.is_labs'); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Normalize and quote a table name for use in SQL. |
138
|
|
|
* |
139
|
|
|
* @param string $databaseName |
140
|
|
|
* @param string $tableName |
141
|
|
|
* @return string Fully-qualified and quoted table name. |
142
|
|
|
*/ |
143
|
|
|
public function getTableName($databaseName, $tableName) |
144
|
|
|
{ |
145
|
|
|
// Use the table specified in the table mapping configuration, if present. |
146
|
|
|
$mapped = false; |
147
|
|
View Code Duplication |
if ($this->container->hasParameter("app.table.$tableName")) { |
|
|
|
|
148
|
|
|
$mapped = true; |
149
|
|
|
$tableName = $this->container->getParameter("app.table.$tableName"); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// For 'revision' and 'logging' tables (actually views) on Labs, use the indexed versions |
153
|
|
|
// (that have some rows hidden, e.g. for revdeleted users). |
154
|
|
|
$isLoggingOrRevision = in_array($tableName, ['revision', 'logging', 'archive']); |
155
|
|
|
if (!$mapped && $isLoggingOrRevision && $this->isLabs()) { |
156
|
|
|
$tableName = $tableName."_userindex"; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
// Figure out database name. |
160
|
|
|
// Use class variable for the database name if not set via function parameter. |
161
|
|
|
if ($this->isLabs() && substr($databaseName, -2) != '_p') { |
162
|
|
|
// Append '_p' if this is labs. |
163
|
|
|
$databaseName .= '_p'; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
return "`$databaseName`.`$tableName`"; |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.