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