DBSiteStore.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. use Wikimedia\Rdbms\ILoadBalancer;
  3. /**
  4. * Represents the site configuration of a wiki.
  5. * Holds a list of sites (ie SiteList), stored in the database.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. * http://www.gnu.org/copyleft/gpl.html
  21. *
  22. * @since 1.25
  23. *
  24. * @file
  25. * @ingroup Site
  26. *
  27. * @license GPL-2.0-or-later
  28. * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  29. * @author Daniel Kinzler
  30. */
  31. class DBSiteStore implements SiteStore {
  32. /**
  33. * @var SiteList|null
  34. */
  35. protected $sites = null;
  36. /**
  37. * @var ILoadBalancer
  38. */
  39. private $dbLoadBalancer;
  40. /**
  41. * @since 1.27
  42. *
  43. * @todo inject some kind of connection manager that is aware of the target wiki,
  44. * instead of injecting a LoadBalancer.
  45. *
  46. * @param ILoadBalancer $dbLoadBalancer
  47. */
  48. public function __construct( ILoadBalancer $dbLoadBalancer ) {
  49. $this->dbLoadBalancer = $dbLoadBalancer;
  50. }
  51. /**
  52. * @see SiteStore::getSites
  53. *
  54. * @since 1.25
  55. *
  56. * @return SiteList
  57. */
  58. public function getSites() {
  59. $this->loadSites();
  60. return $this->sites;
  61. }
  62. /**
  63. * Fetches the site from the database and loads them into the sites field.
  64. *
  65. * @since 1.25
  66. */
  67. protected function loadSites() {
  68. $this->sites = new SiteList();
  69. $dbr = $this->dbLoadBalancer->getConnectionRef( DB_REPLICA );
  70. $res = $dbr->select(
  71. 'sites',
  72. [
  73. 'site_id',
  74. 'site_global_key',
  75. 'site_type',
  76. 'site_group',
  77. 'site_source',
  78. 'site_language',
  79. 'site_protocol',
  80. 'site_domain',
  81. 'site_data',
  82. 'site_forward',
  83. 'site_config',
  84. ],
  85. '',
  86. __METHOD__,
  87. [ 'ORDER BY' => 'site_global_key' ]
  88. );
  89. foreach ( $res as $row ) {
  90. $site = Site::newForType( $row->site_type );
  91. $site->setGlobalId( $row->site_global_key );
  92. $site->setInternalId( (int)$row->site_id );
  93. $site->setForward( (bool)$row->site_forward );
  94. $site->setGroup( $row->site_group );
  95. $site->setLanguageCode( $row->site_language === ''
  96. ? null
  97. : $row->site_language
  98. );
  99. $site->setSource( $row->site_source );
  100. $site->setExtraData( unserialize( $row->site_data ) );
  101. $site->setExtraConfig( unserialize( $row->site_config ) );
  102. $this->sites[] = $site;
  103. }
  104. // Batch load the local site identifiers.
  105. $ids = $dbr->select(
  106. 'site_identifiers',
  107. [
  108. 'si_site',
  109. 'si_type',
  110. 'si_key',
  111. ],
  112. [],
  113. __METHOD__
  114. );
  115. foreach ( $ids as $id ) {
  116. if ( $this->sites->hasInternalId( $id->si_site ) ) {
  117. $site = $this->sites->getSiteByInternalId( $id->si_site );
  118. $site->addLocalId( $id->si_type, $id->si_key );
  119. $this->sites->setSite( $site );
  120. }
  121. }
  122. }
  123. /**
  124. * @see SiteStore::getSite
  125. *
  126. * @since 1.25
  127. *
  128. * @param string $globalId
  129. *
  130. * @return Site|null
  131. */
  132. public function getSite( $globalId ) {
  133. if ( $this->sites === null ) {
  134. $this->sites = $this->getSites();
  135. }
  136. return $this->sites->hasSite( $globalId ) ? $this->sites->getSite( $globalId ) : null;
  137. }
  138. /**
  139. * @see SiteStore::saveSite
  140. *
  141. * @since 1.25
  142. *
  143. * @param Site $site
  144. *
  145. * @return bool Success indicator
  146. */
  147. public function saveSite( Site $site ) {
  148. return $this->saveSites( [ $site ] );
  149. }
  150. /**
  151. * @see SiteStore::saveSites
  152. *
  153. * @since 1.25
  154. *
  155. * @param Site[] $sites
  156. *
  157. * @return bool Success indicator
  158. */
  159. public function saveSites( array $sites ) {
  160. if ( empty( $sites ) ) {
  161. return true;
  162. }
  163. $dbw = $this->dbLoadBalancer->getConnectionRef( DB_MASTER );
  164. $dbw->startAtomic( __METHOD__ );
  165. $success = true;
  166. $internalIds = [];
  167. $localIds = [];
  168. foreach ( $sites as $site ) {
  169. if ( $site->getInternalId() !== null ) {
  170. $internalIds[] = $site->getInternalId();
  171. }
  172. $fields = [
  173. // Site data
  174. 'site_global_key' => $site->getGlobalId(), // TODO: check not null
  175. 'site_type' => $site->getType(),
  176. 'site_group' => $site->getGroup(),
  177. 'site_source' => $site->getSource(),
  178. 'site_language' => $site->getLanguageCode() ?? '',
  179. 'site_protocol' => $site->getProtocol(),
  180. 'site_domain' => strrev( $site->getDomain() ) . '.',
  181. 'site_data' => serialize( $site->getExtraData() ),
  182. // Site config
  183. 'site_forward' => $site->shouldForward() ? 1 : 0,
  184. 'site_config' => serialize( $site->getExtraConfig() ),
  185. ];
  186. $rowId = $site->getInternalId();
  187. if ( $rowId !== null ) {
  188. $success = $dbw->update(
  189. 'sites', $fields, [ 'site_id' => $rowId ], __METHOD__
  190. ) && $success;
  191. } else {
  192. $success = $dbw->insert( 'sites', $fields, __METHOD__ ) && $success;
  193. $rowId = $dbw->insertId();
  194. }
  195. foreach ( $site->getLocalIds() as $idType => $ids ) {
  196. foreach ( $ids as $id ) {
  197. $localIds[] = [ $rowId, $idType, $id ];
  198. }
  199. }
  200. }
  201. if ( $internalIds !== [] ) {
  202. $dbw->delete(
  203. 'site_identifiers',
  204. [ 'si_site' => $internalIds ],
  205. __METHOD__
  206. );
  207. }
  208. foreach ( $localIds as $localId ) {
  209. $dbw->insert(
  210. 'site_identifiers',
  211. [
  212. 'si_site' => $localId[0],
  213. 'si_type' => $localId[1],
  214. 'si_key' => $localId[2],
  215. ],
  216. __METHOD__
  217. );
  218. }
  219. $dbw->endAtomic( __METHOD__ );
  220. $this->reset();
  221. return $success;
  222. }
  223. /**
  224. * Resets the SiteList
  225. *
  226. * @since 1.25
  227. */
  228. public function reset() {
  229. $this->sites = null;
  230. }
  231. /**
  232. * Clears the list of sites stored in the database.
  233. *
  234. * @see SiteStore::clear()
  235. *
  236. * @return bool Success
  237. */
  238. public function clear() {
  239. $dbw = $this->dbLoadBalancer->getConnectionRef( DB_MASTER );
  240. $dbw->startAtomic( __METHOD__ );
  241. $ok = $dbw->delete( 'sites', '*', __METHOD__ );
  242. $ok = $dbw->delete( 'site_identifiers', '*', __METHOD__ ) && $ok;
  243. $dbw->endAtomic( __METHOD__ );
  244. $this->reset();
  245. return $ok;
  246. }
  247. }