Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
UnregisteredLocalFile
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 14
992
0.00% covered (danger)
0.00%
0 / 1
 newFromPath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newFromTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
 cachePageDimensions
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 getWidth
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getHeight
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getMimeType
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getBitDepth
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getMetadata
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getMetadataArray
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getSizeAndMetadata
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 getURL
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getSize
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setLocalReference
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\FileRepo\File;
8
9use BadMethodCallException;
10use MediaWiki\FileRepo\FileRepo;
11use MediaWiki\Media\MediaHandler;
12use MediaWiki\MediaWikiServices;
13use MediaWiki\Title\Title;
14use Wikimedia\FileBackend\FSFile\FSFile;
15
16/**
17 * File without associated database record.
18 *
19 * Represents a standalone local file, or a file in a local repository
20 * with no database, for example a FileRepo repository.
21 *
22 * Read-only.
23 *
24 * @todo Currently it doesn't really work in the repository role, there are
25 * lots of functions missing. It is used by the WebStore extension in the
26 * standalone role.
27 *
28 * @ingroup FileAbstraction
29 */
30class UnregisteredLocalFile extends File {
31    /** @var Title */
32    protected $title;
33
34    /** @var string */
35    protected $path;
36
37    /** @var string|null */
38    protected $mime;
39
40    /** @var array[]|bool[] Dimension data */
41    protected $pageDims;
42
43    /** @var array|null */
44    protected $sizeAndMetadata;
45
46    /** @var MediaHandler */
47    public $handler;
48
49    /**
50     * @param string $path Storage path
51     * @param string $mime
52     */
53    public static function newFromPath( $path, $mime ): static {
54        return new static( false, false, $path, $mime );
55    }
56
57    /**
58     * @param Title $title
59     * @param FileRepo $repo
60     */
61    public static function newFromTitle( $title, $repo ): static {
62        return new static( $title, $repo, false, false );
63    }
64
65    /**
66     * Create an UnregisteredLocalFile based on a path or a (title,repo) pair.
67     * A FileRepo object is not required here, unlike most other File classes.
68     *
69     * @param Title|false $title
70     * @param FileRepo|false $repo
71     * @param string|false $path
72     * @param string|false $mime
73     */
74    public function __construct( $title = false, $repo = false, $path = false, $mime = false ) {
75        if ( !( $title && $repo ) && !$path ) {
76            throw new BadMethodCallException( __METHOD__ .
77                ': not enough parameters, must specify title and repo, or a full path' );
78        }
79        if ( $title instanceof Title ) {
80            $this->title = File::normalizeTitle( $title, 'exception' );
81            $this->name = $repo->getNameFromTitle( $title );
82        } else {
83            $this->name = basename( $path );
84            $this->title = File::normalizeTitle( $this->name, 'exception' );
85        }
86        $this->repo = $repo;
87        if ( $path ) {
88            $this->path = $path;
89        } else {
90            $this->assertRepoDefined();
91            $this->path = $repo->getRootDirectory() . '/' .
92                $repo->getHashPath( $this->name ) . $this->name;
93        }
94        if ( $mime ) {
95            $this->mime = $mime;
96        }
97        $this->pageDims = [];
98    }
99
100    /**
101     * @param int $page
102     * @return array|false
103     */
104    private function cachePageDimensions( $page = 1 ) {
105        $page = (int)$page;
106        if ( $page < 1 ) {
107            $page = 1;
108        }
109
110        if ( !isset( $this->pageDims[$page] ) ) {
111            if ( !$this->getHandler() ) {
112                return false;
113            }
114            if ( $this->getHandler()->isMultiPage( $this ) ) {
115                $this->pageDims[$page] = $this->handler->getPageDimensions( $this, $page );
116            } else {
117                $info = $this->getSizeAndMetadata();
118                return [
119                    'width' => $info['width'],
120                    'height' => $info['height']
121                ];
122            }
123        }
124
125        return $this->pageDims[$page];
126    }
127
128    /**
129     * @param int $page
130     * @return int
131     */
132    public function getWidth( $page = 1 ) {
133        $dim = $this->cachePageDimensions( $page );
134
135        return $dim['width'] ?? 0;
136    }
137
138    /**
139     * @param int $page
140     * @return int
141     */
142    public function getHeight( $page = 1 ) {
143        $dim = $this->cachePageDimensions( $page );
144
145        return $dim['height'] ?? 0;
146    }
147
148    /**
149     * @return string
150     */
151    public function getMimeType() {
152        if ( $this->mime === null ) {
153            $refPath = $this->getLocalRefPath();
154            if ( $refPath !== false ) {
155                $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
156                $this->mime = $magic->guessMimeType( $refPath );
157            } else {
158                $this->mime = 'unknown/unknown';
159            }
160        }
161
162        return $this->mime;
163    }
164
165    /**
166     * @return int
167     */
168    public function getBitDepth() {
169        $info = $this->getSizeAndMetadata();
170        return $info['bits'] ?? 0;
171    }
172
173    /**
174     * @return string|false
175     * @deprecated since 1.47 use getMetadataArray()
176     */
177    public function getMetadata() {
178        wfDeprecated( __METHOD__, '1.47' );
179        $info = $this->getSizeAndMetadata();
180        return $info['metadata'] ? serialize( $info['metadata'] ) : false;
181    }
182
183    public function getMetadataArray(): array {
184        $info = $this->getSizeAndMetadata();
185        return $info['metadata'];
186    }
187
188    private function getSizeAndMetadata(): array {
189        if ( $this->sizeAndMetadata === null ) {
190            $handler = $this->getHandler() ?: null;
191            $result = $handler?->getSizeAndMetadataWithFallback( $this, $this->getLocalRefPath() );
192            $this->sizeAndMetadata = $result ?: [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
193        }
194
195        return $this->sizeAndMetadata;
196    }
197
198    /**
199     * @return string|false
200     */
201    public function getURL() {
202        if ( $this->repo ) {
203            return $this->repo->getZoneUrl( 'public' ) . '/' .
204                $this->repo->getHashPath( $this->name ) . rawurlencode( $this->name );
205        } else {
206            return false;
207        }
208    }
209
210    /**
211     * @return false|int
212     */
213    public function getSize() {
214        $this->assertRepoDefined();
215
216        return $this->repo->getFileSize( $this->path );
217    }
218
219    /**
220     * Optimize getLocalRefPath() by using an existing local reference.
221     * The file at the path of $fsFile should not be deleted (or at least
222     * not until the end of the request). This is mostly a performance hack.
223     *
224     * @param FSFile $fsFile
225     * @return void
226     */
227    public function setLocalReference( FSFile $fsFile ) {
228        $this->fsFile = $fsFile;
229    }
230}
231
232/** @deprecated class alias since 1.44 */
233class_alias( UnregisteredLocalFile::class, 'UnregisteredLocalFile' );