ConfigFactory.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. /**
  3. * Copyright 2014
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. use Wikimedia\Assert\Assert;
  23. use Wikimedia\Services\SalvageableService;
  24. /**
  25. * Factory class to create Config objects
  26. *
  27. * @since 1.23
  28. */
  29. class ConfigFactory implements SalvageableService {
  30. /**
  31. * Map of config name => callback
  32. * @var array
  33. */
  34. protected $factoryFunctions = [];
  35. /**
  36. * Config objects that have already been created
  37. * name => Config object
  38. * @var array
  39. */
  40. protected $configs = [];
  41. /**
  42. * @deprecated since 1.27, use MediaWikiServices::getInstance()->getConfigFactory() instead.
  43. *
  44. * @return ConfigFactory
  45. */
  46. public static function getDefaultInstance() {
  47. return \MediaWiki\MediaWikiServices::getInstance()->getConfigFactory();
  48. }
  49. /**
  50. * Re-uses existing Cache objects from $other. Cache objects are only re-used if the
  51. * registered factory function for both is the same. Cache config is not copied,
  52. * and only instances of caches defined on this instance with the same config
  53. * are copied.
  54. *
  55. * @see SalvageableService::salvage()
  56. *
  57. * @param SalvageableService $other The object to salvage state from. $other must have the
  58. * exact same type as $this.
  59. */
  60. public function salvage( SalvageableService $other ) {
  61. Assert::parameterType( self::class, $other, '$other' );
  62. /** @var self $other */
  63. '@phan-var self $other';
  64. foreach ( $other->factoryFunctions as $name => $otherFunc ) {
  65. if ( !isset( $this->factoryFunctions[$name] ) ) {
  66. continue;
  67. }
  68. // if the callback function is the same, salvage the Cache object
  69. // XXX: Closures are never equal!
  70. if ( isset( $other->configs[$name] )
  71. && $this->factoryFunctions[$name] == $otherFunc
  72. ) {
  73. $this->configs[$name] = $other->configs[$name];
  74. unset( $other->configs[$name] );
  75. }
  76. }
  77. // disable $other
  78. $other->factoryFunctions = [];
  79. $other->configs = [];
  80. }
  81. /**
  82. * @return string[]
  83. */
  84. public function getConfigNames() {
  85. return array_keys( $this->factoryFunctions );
  86. }
  87. /**
  88. * Register a new config factory function.
  89. * Will override if it's already registered.
  90. * Use "*" for $name to provide a fallback config for all unknown names.
  91. * @param string $name
  92. * @param callable|Config $callback A factory callback that takes this ConfigFactory
  93. * as an argument and returns a Config instance, or an existing Config instance.
  94. * @throws InvalidArgumentException If an invalid callback is provided
  95. */
  96. public function register( $name, $callback ) {
  97. if ( !is_callable( $callback ) && !( $callback instanceof Config ) ) {
  98. if ( is_array( $callback ) ) {
  99. $callback = '[ ' . implode( ', ', $callback ) . ' ]';
  100. } elseif ( is_object( $callback ) ) {
  101. $callback = 'instanceof ' . get_class( $callback );
  102. }
  103. throw new InvalidArgumentException( 'Invalid callback \'' . $callback . '\' provided' );
  104. }
  105. unset( $this->configs[$name] );
  106. $this->factoryFunctions[$name] = $callback;
  107. }
  108. /**
  109. * Create a given Config using the registered callback for $name.
  110. * If an object was already created, the same Config object is returned.
  111. * @param string $name Name of the extension/component you want a Config object for
  112. * 'main' is used for core
  113. * @throws ConfigException If a factory function isn't registered for $name
  114. * @throws UnexpectedValueException If the factory function returns a non-Config object
  115. * @return Config
  116. */
  117. public function makeConfig( $name ) {
  118. if ( !isset( $this->configs[$name] ) ) {
  119. $key = $name;
  120. if ( !isset( $this->factoryFunctions[$key] ) ) {
  121. $key = '*';
  122. }
  123. if ( !isset( $this->factoryFunctions[$key] ) ) {
  124. throw new ConfigException( "No registered builder available for $name." );
  125. }
  126. if ( $this->factoryFunctions[$key] instanceof Config ) {
  127. $conf = $this->factoryFunctions[$key];
  128. } else {
  129. $conf = call_user_func( $this->factoryFunctions[$key], $this );
  130. }
  131. if ( $conf instanceof Config ) {
  132. $this->configs[$name] = $conf;
  133. } else {
  134. throw new UnexpectedValueException( "The builder for $name returned a non-Config object." );
  135. }
  136. }
  137. return $this->configs[$name];
  138. }
  139. }