Projet

Général

Profil

Paste
Télécharger (7,68 ko) Statistiques
| Branche: | Révision:

root / drupal7 / misc / typo3 / phar-stream-wrapper / src / Phar / Reader.php @ 6b24a280

1
<?php
2
namespace TYPO3\PharStreamWrapper\Phar;
3

    
4
/*
5
 * This file is part of the TYPO3 project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under the terms
8
 * of the MIT License (MIT). For the full copyright and license information,
9
 * please read the LICENSE file that was distributed with this source code.
10
 *
11
 * The TYPO3 project - inspiring people to share!
12
 */
13

    
14
class Reader
15
{
16
    /**
17
     * @var string
18
     */
19
    private $fileName;
20

    
21
    /**
22
     * Mime-type in order to use zlib, bzip2 or no compression.
23
     * In case ext-fileinfo is not present only the relevant types
24
     * 'application/x-gzip' and 'application/x-bzip2' are assigned
25
     * to this class property.
26
     *
27
     * @var string
28
     */
29
    private $fileType;
30

    
31
    /**
32
     * @param string $fileName
33
     */
34
    public function __construct($fileName)
35
    {
36
        if (strpos($fileName, '://') !== false) {
37
            throw new ReaderException(
38
                'File name must not contain stream prefix',
39
                1539623708
40
            );
41
        }
42

    
43
        $this->fileName = $fileName;
44
        $this->fileType = $this->determineFileType();
45
    }
46

    
47
    /**
48
     * @return Container
49
     */
50
    public function resolveContainer()
51
    {
52
        $data = $this->extractData($this->resolveStream() . $this->fileName);
53

    
54
        if ($data['stubContent'] === null) {
55
            throw new ReaderException(
56
                'Cannot resolve stub',
57
                1547807881
58
            );
59
        }
60
        if ($data['manifestContent'] === null || $data['manifestLength'] === null) {
61
            throw new ReaderException(
62
                'Cannot resolve manifest',
63
                1547807882
64
            );
65
        }
66
        if (strlen($data['manifestContent']) < $data['manifestLength']) {
67
            throw new ReaderException(
68
                sprintf(
69
                    'Exected manifest length %d, got %d',
70
                    strlen($data['manifestContent']),
71
                    $data['manifestLength']
72
                ),
73
                1547807883
74
            );
75
        }
76

    
77
        return new Container(
78
            Stub::fromContent($data['stubContent']),
79
            Manifest::fromContent($data['manifestContent'])
80
        );
81
    }
82

    
83
    /**
84
     * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar'
85
     * @return array
86
     */
87
    private function extractData($fileName)
88
    {
89
        $stubContent = null;
90
        $manifestContent = null;
91
        $manifestLength = null;
92

    
93
        $resource = fopen($fileName, 'r');
94
        if (!is_resource($resource)) {
95
            throw new ReaderException(
96
                sprintf('Resource %s could not be opened', $fileName),
97
                1547902055
98
            );
99
        }
100

    
101
        while (!feof($resource)) {
102
            $line = fgets($resource);
103
            // stop reading file when manifest can be extracted
104
            if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) {
105
                break;
106
            }
107

    
108
            $manifestPosition = strpos($line, '__HALT_COMPILER();');
109

    
110
            // first line contains start of manifest
111
            if ($stubContent === null && $manifestContent === null && $manifestPosition !== false) {
112
                $stubContent = substr($line, 0, $manifestPosition - 1);
113
                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
114
                $manifestLength = $this->resolveManifestLength($manifestContent);
115
            // line contains start of stub
116
            } elseif ($stubContent === null) {
117
                $stubContent = $line;
118
            // line contains start of manifest
119
            } elseif ($manifestContent === null && $manifestPosition !== false) {
120
                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
121
                $manifestLength = $this->resolveManifestLength($manifestContent);
122
            // manifest has been started (thus is cannot be stub anymore), add content
123
            } elseif ($manifestContent !== null) {
124
                $manifestContent .= $line;
125
                $manifestLength = $this->resolveManifestLength($manifestContent);
126
            // stub has been started (thus cannot be manifest here, yet), add content
127
            } elseif ($stubContent !== null) {
128
                $stubContent .= $line;
129
            }
130
        }
131
        fclose($resource);
132

    
133
        return array(
134
            'stubContent' => $stubContent,
135
            'manifestContent' => $manifestContent,
136
            'manifestLength' => $manifestLength,
137
        );
138
    }
139

    
140
    /**
141
     * Resolves stream in order to handle compressed Phar archives.
142
     *
143
     * @return string
144
     */
145
    private function resolveStream()
146
    {
147
        if ($this->fileType === 'application/x-gzip' || $this->fileType === 'application/gzip') {
148
            return 'compress.zlib://';
149
        } elseif ($this->fileType === 'application/x-bzip2') {
150
            return 'compress.bzip2://';
151
        }
152
        return '';
153
    }
154

    
155
    /**
156
     * @return string
157
     */
158
    private function determineFileType()
159
    {
160
        if (class_exists('\\finfo')) {
161
            $fileInfo = new \finfo();
162
            return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE);
163
        }
164
        return $this->determineFileTypeByHeader();
165
    }
166

    
167
    /**
168
     * In case ext-fileinfo is not present only the relevant types
169
     * 'application/x-gzip' and 'application/x-bzip2' are resolved.
170
     *
171
     * @return string
172
     */
173
    private function determineFileTypeByHeader()
174
    {
175
        $resource = fopen($this->fileName, 'r');
176
        if (!is_resource($resource)) {
177
            throw new ReaderException(
178
                sprintf('Resource %s could not be opened', $this->fileName),
179
                1557753055
180
            );
181
        }
182
        $header = fgets($resource, 4);
183
        fclose($resource);
184
        $mimeType = '';
185
        if (strpos($header, "\x42\x5a\x68") === 0) {
186
            $mimeType = 'application/x-bzip2';
187
        } elseif (strpos($header, "\x1f\x8b") === 0) {
188
            $mimeType = 'application/x-gzip';
189
        }
190
        return $mimeType;
191
    }
192

    
193
    /**
194
     * @param string $content
195
     * @return int|null
196
     */
197
    private function resolveManifestLength($content)
198
    {
199
        if (strlen($content) < 4) {
200
            return null;
201
        }
202
        return static::resolveFourByteLittleEndian($content, 0);
203
    }
204

    
205
    /**
206
     * @param string $content
207
     * @param int $start
208
     * @return int
209
     */
210
    public static function resolveFourByteLittleEndian($content, $start)
211
    {
212
        $payload = substr($content, $start, 4);
213
        if (!is_string($payload)) {
214
            throw new ReaderException(
215
                sprintf('Cannot resolve value at offset %d', $start),
216
                1539614260
217
            );
218
        }
219

    
220
        $value = unpack('V', $payload);
221
        if (!isset($value[1])) {
222
            throw new ReaderException(
223
                sprintf('Cannot resolve value at offset %d', $start),
224
                1539614261
225
            );
226
        }
227
        return $value[1];
228
    }
229

    
230
    /**
231
     * @param string $content
232
     * @param int $start
233
     * @return int
234
     */
235
    public static function resolveTwoByteBigEndian($content, $start)
236
    {
237
        $payload = substr($content, $start, 2);
238
        if (!is_string($payload)) {
239
            throw new ReaderException(
240
                sprintf('Cannot resolve value at offset %d', $start),
241
                1539614263
242
            );
243
        }
244

    
245
        $value = unpack('n', $payload);
246
        if (!isset($value[1])) {
247
            throw new ReaderException(
248
                sprintf('Cannot resolve value at offset %d', $start),
249
                1539614264
250
            );
251
        }
252
        return $value[1];
253
    }
254
}