ExternalStore.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <?php
  2. /**
  3. * @defgroup ExternalStorage ExternalStorage
  4. */
  5. /**
  6. * Constructor class for data kept in external repositories
  7. *
  8. * External repositories might be populated by maintenance/async
  9. * scripts, thus partial moving of data may be possible, as well
  10. * as possibility to have any storage format (i.e. for archives)
  11. *
  12. * @ingroup ExternalStorage
  13. */
  14. class ExternalStore {
  15. /* Fetch data from given URL */
  16. static function fetchFromURL( $url ) {
  17. global $wgExternalStores;
  18. if( !$wgExternalStores )
  19. return false;
  20. @list( $proto, $path ) = explode( '://', $url, 2 );
  21. /* Bad URL */
  22. if( $path == '' )
  23. return false;
  24. $store = self::getStoreObject( $proto );
  25. if ( $store === false )
  26. return false;
  27. return $store->fetchFromURL( $url );
  28. }
  29. /**
  30. * Get an external store object of the given type
  31. */
  32. static function getStoreObject( $proto ) {
  33. global $wgExternalStores;
  34. if( !$wgExternalStores )
  35. return false;
  36. /* Protocol not enabled */
  37. if( !in_array( $proto, $wgExternalStores ) )
  38. return false;
  39. $class = 'ExternalStore' . ucfirst( $proto );
  40. /* Any custom modules should be added to $wgAutoLoadClasses for on-demand loading */
  41. if( !class_exists( $class ) ) {
  42. return false;
  43. }
  44. return new $class();
  45. }
  46. /**
  47. * Store a data item to an external store, identified by a partial URL
  48. * The protocol part is used to identify the class, the rest is passed to the
  49. * class itself as a parameter.
  50. * Returns the URL of the stored data item, or false on error
  51. */
  52. static function insert( $url, $data ) {
  53. list( $proto, $params ) = explode( '://', $url, 2 );
  54. $store = self::getStoreObject( $proto );
  55. if ( $store === false ) {
  56. return false;
  57. } else {
  58. return $store->store( $params, $data );
  59. }
  60. }
  61. /**
  62. * Like insert() above, but does more of the work for us.
  63. * This function does not need a url param, it builds it by
  64. * itself. It also fails-over to the next possible clusters.
  65. *
  66. * @param string $data
  67. * Returns the URL of the stored data item, or false on error
  68. */
  69. public static function insertToDefault( $data ) {
  70. global $wgDefaultExternalStore;
  71. $tryStores = (array)$wgDefaultExternalStore;
  72. $error = false;
  73. while ( count( $tryStores ) > 0 ) {
  74. $index = mt_rand(0, count( $tryStores ) - 1);
  75. $storeUrl = $tryStores[$index];
  76. wfDebug( __METHOD__.": trying $storeUrl\n" );
  77. list( $proto, $params ) = explode( '://', $storeUrl, 2 );
  78. $store = self::getStoreObject( $proto );
  79. if ( $store === false ) {
  80. throw new MWException( "Invalid external storage protocol - $storeUrl" );
  81. }
  82. try {
  83. $url = $store->store( $params, $data ); // Try to save the object
  84. } catch ( DBConnectionError $error ) {
  85. $url = false;
  86. } catch( DBQueryError $error ) {
  87. $url = false;
  88. }
  89. if ( $url ) {
  90. return $url; // Done!
  91. } else {
  92. unset( $tryStores[$index] ); // Don't try this one again!
  93. $tryStores = array_values( $tryStores ); // Must have consecutive keys
  94. wfDebugLog( 'ExternalStorage', "Unable to store text to external storage $storeUrl" );
  95. }
  96. }
  97. // All stores failed
  98. if ( $error ) {
  99. // Rethrow the last connection error
  100. throw $error;
  101. } else {
  102. throw new MWException( "Unable to store text to external storage" );
  103. }
  104. }
  105. }