ci4

Форк
0
/
File.php 
192 строки · 6.0 Кб
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Files;
15

16
use CodeIgniter\Files\Exceptions\FileException;
17
use CodeIgniter\Files\Exceptions\FileNotFoundException;
18
use CodeIgniter\I18n\Time;
19
use Config\Mimes;
20
use ReturnTypeWillChange;
21
use SplFileInfo;
22

23
/**
24
 * Wrapper for PHP's built-in SplFileInfo, with goodies.
25
 *
26
 * @see \CodeIgniter\Files\FileTest
27
 */
28
class File extends SplFileInfo
29
{
30
    /**
31
     * The files size in bytes
32
     *
33
     * @var int
34
     */
35
    protected $size;
36

37
    /**
38
     * @var string|null
39
     */
40
    protected $originalMimeType;
41

42
    /**
43
     * Run our SplFileInfo constructor with an optional verification
44
     * that the path is really a file.
45
     *
46
     * @throws FileNotFoundException
47
     */
48
    public function __construct(string $path, bool $checkFile = false)
49
    {
50
        if ($checkFile && ! is_file($path)) {
51
            throw FileNotFoundException::forFileNotFound($path);
52
        }
53

54
        parent::__construct($path);
55
    }
56

57
    /**
58
     * Retrieve the file size.
59
     *
60
     * Implementations SHOULD return the value stored in the "size" key of
61
     * the file in the $_FILES array if available, as PHP calculates this based
62
     * on the actual size transmitted. A RuntimeException will be thrown if the file
63
     * does not exist or an error occurs.
64
     *
65
     * @return false|int The file size in bytes, or false on failure
66
     */
67
    #[ReturnTypeWillChange]
68
    public function getSize()
69
    {
70
        return $this->size ?? ($this->size = parent::getSize());
71
    }
72

73
    /**
74
     * Retrieve the file size by unit.
75
     *
76
     * @return false|int|string
77
     */
78
    public function getSizeByUnit(string $unit = 'b')
79
    {
80
        return match (strtolower($unit)) {
81
            'kb'    => number_format($this->getSize() / 1024, 3),
82
            'mb'    => number_format(($this->getSize() / 1024) / 1024, 3),
83
            default => $this->getSize(),
84
        };
85
    }
86

87
    /**
88
     * Attempts to determine the file extension based on the trusted
89
     * getType() method. If the mime type is unknown, will return null.
90
     */
91
    public function guessExtension(): ?string
92
    {
93
        // naively get the path extension using pathinfo
94
        $pathinfo = pathinfo($this->getRealPath() ?: $this->__toString()) + ['extension' => ''];
95

96
        $proposedExtension = $pathinfo['extension'];
97

98
        return Mimes::guessExtensionFromType($this->getMimeType(), $proposedExtension);
99
    }
100

101
    /**
102
     * Retrieve the media type of the file. SHOULD not use information from
103
     * the $_FILES array, but should use other methods to more accurately
104
     * determine the type of file, like finfo, or mime_content_type().
105
     *
106
     * @return string The media type we determined it to be.
107
     */
108
    public function getMimeType(): string
109
    {
110
        if (! function_exists('finfo_open')) {
111
            return $this->originalMimeType ?? 'application/octet-stream'; // @codeCoverageIgnore
112
        }
113

114
        $finfo    = finfo_open(FILEINFO_MIME_TYPE);
115
        $mimeType = finfo_file($finfo, $this->getRealPath() ?: $this->__toString());
116
        finfo_close($finfo);
117

118
        return $mimeType;
119
    }
120

121
    /**
122
     * Generates a random names based on a simple hash and the time, with
123
     * the correct file extension attached.
124
     */
125
    public function getRandomName(): string
126
    {
127
        $extension = $this->getExtension();
128
        $extension = empty($extension) ? '' : '.' . $extension;
129

130
        return Time::now()->getTimestamp() . '_' . bin2hex(random_bytes(10)) . $extension;
131
    }
132

133
    /**
134
     * Moves a file to a new location.
135
     *
136
     * @return File
137
     */
138
    public function move(string $targetPath, ?string $name = null, bool $overwrite = false)
139
    {
140
        $targetPath = rtrim($targetPath, '/') . '/';
141
        $name ??= $this->getBasename();
142
        $destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name);
143

144
        $oldName = $this->getRealPath() ?: $this->__toString();
145

146
        if (! @rename($oldName, $destination)) {
147
            $error = error_get_last();
148

149
            throw FileException::forUnableToMove($this->getBasename(), $targetPath, strip_tags($error['message']));
150
        }
151

152
        @chmod($destination, 0777 & ~umask());
153

154
        return new self($destination);
155
    }
156

157
    /**
158
     * Returns the destination path for the move operation where overwriting is not expected.
159
     *
160
     * First, it checks whether the delimiter is present in the filename, if it is, then it checks whether the
161
     * last element is an integer as there may be cases that the delimiter may be present in the filename.
162
     * For the all other cases, it appends an integer starting from zero before the file's extension.
163
     */
164
    public function getDestination(string $destination, string $delimiter = '_', int $i = 0): string
165
    {
166
        if ($delimiter === '') {
167
            $delimiter = '_';
168
        }
169

170
        while (is_file($destination)) {
171
            $info      = pathinfo($destination);
172
            $extension = isset($info['extension']) ? '.' . $info['extension'] : '';
173

174
            if (str_contains($info['filename'], $delimiter)) {
175
                $parts = explode($delimiter, $info['filename']);
176

177
                if (is_numeric(end($parts))) {
178
                    $i = end($parts);
179
                    array_pop($parts);
180
                    $parts[]     = ++$i;
181
                    $destination = $info['dirname'] . DIRECTORY_SEPARATOR . implode($delimiter, $parts) . $extension;
182
                } else {
183
                    $destination = $info['dirname'] . DIRECTORY_SEPARATOR . $info['filename'] . $delimiter . ++$i . $extension;
184
                }
185
            } else {
186
                $destination = $info['dirname'] . DIRECTORY_SEPARATOR . $info['filename'] . $delimiter . ++$i . $extension;
187
            }
188
        }
189

190
        return $destination;
191
    }
192
}
193

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.