Merge "Update weblinks in comments from HTTP to HTTPS"
[lhc/web/wiklou.git] / includes / libs / filebackend / FileBackend.php
index 0b9eee0..15f13b9 100644 (file)
@@ -28,6 +28,9 @@
  * @ingroup FileBackend
  * @author Aaron Schulz
  */
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Wikimedia\ScopedCallback;
 
 /**
  * @brief Base class for all file backend classes (including multi-write backends).
  * Outside callers can assume that all backends will have these functions.
  *
  * All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
- * The "backend" portion is unique name for MediaWiki to refer to a backend, while
+ * The "backend" portion is unique name for the application to refer to a backend, while
  * the "container" portion is a top-level directory of the backend. The "path" portion
  * is a relative path that uses UNIX file system (FS) notation, though any particular
  * backend may not actually be using a local filesystem. Therefore, the relative paths
  * are only virtual.
  *
- * Backend contents are stored under wiki-specific container names by default.
- * Global (qualified) backends are achieved by configuring the "wiki ID" to a constant.
+ * Backend contents are stored under "domain"-specific container names by default.
+ * A domain is simply a logical umbrella for entities, such as those belonging to a certain
+ * application or portion of a website, for example. A domain can be local or global.
+ * Global (qualified) backends are achieved by configuring the "domain ID" to a constant.
+ * Global domains are simpler, but local domains can be used by choosing a domain ID based on
+ * the current context, such as which language of a website is being used.
+ *
  * For legacy reasons, the FSFileBackend class allows manually setting the paths of
- * containers to ones that do not respect the "wiki ID".
+ * containers to ones that do not respect the "domain ID".
  *
  * In key/value (object) stores, containers are the only hierarchy (the rest is emulated).
  * FS-based backends are somewhat more restrictive due to the existence of real
  * @ingroup FileBackend
  * @since 1.19
  */
-abstract class FileBackend {
+abstract class FileBackend implements LoggerAwareInterface {
        /** @var string Unique backend name */
        protected $name;
 
-       /** @var string Unique wiki name */
-       protected $wikiId;
+       /** @var string Unique domain name */
+       protected $domainId;
 
        /** @var string Read-only explanation message */
        protected $readOnly;
@@ -103,10 +111,17 @@ abstract class FileBackend {
 
        /** @var LockManager */
        protected $lockManager;
-
        /** @var FileJournal */
        protected $fileJournal;
+       /** @var LoggerInterface */
+       protected $logger;
+       /** @var object|string Class name or object With profileIn/profileOut methods */
+       protected $profiler;
 
+       /** @var callable */
+       protected $obResetFunc;
+       /** @var callable */
+       protected $streamMimeFunc;
        /** @var callable */
        protected $statusWrapper;
 
@@ -120,34 +135,41 @@ abstract class FileBackend {
         * This should only be called from within FileBackendGroup.
         *
         * @param array $config Parameters include:
-        *   - name        : The unique name of this backend.
-        *                   This should consist of alphanumberic, '-', and '_' characters.
-        *                   This name should not be changed after use (e.g. with journaling).
-        *                   Note that the name is *not* used in actual container names.
-        *   - wikiId      : Prefix to container names that is unique to this backend.
-        *                   It should only consist of alphanumberic, '-', and '_' characters.
-        *                   This ID is what avoids collisions if multiple logical backends
-        *                   use the same storage system, so this should be set carefully.
+        *   - name : The unique name of this backend.
+        *      This should consist of alphanumberic, '-', and '_' characters.
+        *      This name should not be changed after use (e.g. with journaling).
+        *      Note that the name is *not* used in actual container names.
+        *   - domainId : Prefix to container names that is unique to this backend.
+        *      It should only consist of alphanumberic, '-', and '_' characters.
+        *      This ID is what avoids collisions if multiple logical backends
+        *      use the same storage system, so this should be set carefully.
         *   - lockManager : LockManager object to use for any file locking.
-        *                   If not provided, then no file locking will be enforced.
+        *      If not provided, then no file locking will be enforced.
         *   - fileJournal : FileJournal object to use for logging changes to files.
-        *                   If not provided, then change journaling will be disabled.
-        *   - readOnly    : Write operations are disallowed if this is a non-empty string.
-        *                   It should be an explanation for the backend being read-only.
+        *      If not provided, then change journaling will be disabled.
+        *   - readOnly : Write operations are disallowed if this is a non-empty string.
+        *      It should be an explanation for the backend being read-only.
         *   - parallelize : When to do file operations in parallel (when possible).
-        *                   Allowed values are "implicit", "explicit" and "off".
+        *      Allowed values are "implicit", "explicit" and "off".
         *   - concurrency : How many file operations can be done in parallel.
         *   - tmpDirectory : Directory to use for temporary files. If this is not set or null,
-        *                    then the backend will try to discover a usable temporary directory.
+        *      then the backend will try to discover a usable temporary directory.
+        *   - obResetFunc : alternative callback to clear the output buffer
+        *   - streamMimeFunc : alternative method to determine the content type from the path
+        *   - logger : Optional PSR logger object.
+        *   - profiler : Optional class name or object With profileIn/profileOut methods.
         * @throws InvalidArgumentException
         */
        public function __construct( array $config ) {
                $this->name = $config['name'];
-               $this->wikiId = $config['wikiId']; // e.g. "my_wiki-en_"
+               $this->domainId = isset( $config['domainId'] )
+                       ? $config['domainId'] // e.g. "my_wiki-en_"
+                       : $config['wikiId']; // b/c alias
                if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
                        throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
-               } elseif ( !is_string( $this->wikiId ) ) {
-                       throw new InvalidArgumentException( "Backend wiki ID not provided for '{$this->name}'." );
+               } elseif ( !is_string( $this->domainId ) ) {
+                       throw new InvalidArgumentException(
+                               "Backend domain ID not provided for '{$this->name}'." );
                }
                $this->lockManager = isset( $config['lockManager'] )
                        ? $config['lockManager']
@@ -164,10 +186,24 @@ abstract class FileBackend {
                $this->concurrency = isset( $config['concurrency'] )
                        ? (int)$config['concurrency']
                        : 50;
+               $this->obResetFunc = isset( $config['obResetFunc'] )
+                       ? $config['obResetFunc']
+                       : [ $this, 'resetOutputBuffer' ];
+               $this->streamMimeFunc = isset( $config['streamMimeFunc'] )
+                       ? $config['streamMimeFunc']
+                       : null;
+               $this->statusWrapper = isset( $config['statusWrapper'] ) ? $config['statusWrapper'] : null;
+
+               $this->profiler = isset( $config['profiler'] ) ? $config['profiler'] : null;
+               $this->logger = isset( $config['logger'] ) ? $config['logger'] : new \Psr\Log\NullLogger();
                $this->statusWrapper = isset( $config['statusWrapper'] ) ? $config['statusWrapper'] : null;
                $this->tmpDirectory = isset( $config['tmpDirectory'] ) ? $config['tmpDirectory'] : null;
        }
 
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
        /**
         * Get the unique backend name.
         * We may have multiple different backends of the same type.
@@ -180,14 +216,22 @@ abstract class FileBackend {
        }
 
        /**
-        * Get the wiki identifier used for this backend (possibly empty).
-        * Note that this might *not* be in the same format as wfWikiID().
+        * Get the domain identifier used for this backend (possibly empty).
         *
         * @return string
+        * @since 1.28
+        */
+       final public function getDomainId() {
+               return $this->domainId;
+       }
+
+       /**
+        * Alias to getDomainId()
+        * @return string
         * @since 1.20
         */
        final public function getWikiId() {
-               return $this->wikiId;
+               return $this->getDomainId();
        }
 
        /**
@@ -884,7 +928,7 @@ abstract class FileBackend {
         * @return ScopedCallback|null
         */
        final protected function getScopedPHPBehaviorForOps() {
-               if ( PHP_SAPI != 'cli' ) { // http://bugs.php.net/bug.php?id=47540
+               if ( PHP_SAPI != 'cli' ) { // https://bugs.php.net/bug.php?id=47540
                        $old = ignore_user_abort( true ); // avoid half-finished operations
                        return new ScopedCallback( function () use ( $old ) {
                                ignore_user_abort( $old );
@@ -1569,4 +1613,27 @@ abstract class FileBackend {
        final protected function wrapStatus( StatusValue $sv ) {
                return $this->statusWrapper ? call_user_func( $this->statusWrapper, $sv ) : $sv;
        }
+
+       /**
+        * @param string $section
+        * @return ScopedCallback|null
+        */
+       protected function scopedProfileSection( $section ) {
+               if ( $this->profiler ) {
+                       call_user_func( [ $this->profiler, 'profileIn' ], $section );
+                       return new ScopedCallback( [ $this->profiler, 'profileOut' ], [ $section ] );
+               }
+
+               return null;
+       }
+
+       protected function resetOutputBuffer() {
+               while ( ob_get_status() ) {
+                       if ( !ob_end_clean() ) {
+                               // Could not remove output buffer handler; abort now
+                               // to avoid getting in some kind of infinite loop.
+                               break;
+                       }
+               }
+       }
 }