Projet

Général

Profil

Paste
Télécharger (6,49 ko) Statistiques
| Branche: | Révision:

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

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
     * @var string
23
     */
24
    private $fileType;
25

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

    
38
        $this->fileName = $fileName;
39
        $this->fileType = $this->determineFileType();
40
    }
41

    
42
    /**
43
     * @return Container
44
     */
45
    public function resolveContainer()
46
    {
47
        $data = $this->extractData($this->resolveStream() . $this->fileName);
48

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

    
72
        return new Container(
73
            Stub::fromContent($data['stubContent']),
74
            Manifest::fromContent($data['manifestContent'])
75
        );
76
    }
77

    
78
    /**
79
     * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar'
80
     * @return array
81
     */
82
    private function extractData($fileName)
83
    {
84
        $stubContent = null;
85
        $manifestContent = null;
86
        $manifestLength = null;
87

    
88
        $resource = fopen($fileName, 'r');
89
        if (!is_resource($resource)) {
90
            throw new ReaderException(
91
                sprintf('Resource %s could not be opened', $fileName),
92
                1547902055
93
            );
94
        }
95

    
96
        while (!feof($resource)) {
97
            $line = fgets($resource);
98
            // stop reading file when manifest can be extracted
99
            if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) {
100
                break;
101
            }
102

    
103
            $manifestPosition = strpos($line, '__HALT_COMPILER();');
104

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

    
128
        return array(
129
            'stubContent' => $stubContent,
130
            'manifestContent' => $manifestContent,
131
            'manifestLength' => $manifestLength,
132
        );
133
    }
134

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

    
150
    /**
151
     * @return string
152
     */
153
    private function determineFileType()
154
    {
155
        $fileInfo = new \finfo();
156
        return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE);
157
    }
158

    
159
    /**
160
     * @param string $content
161
     * @return int|null
162
     */
163
    private function resolveManifestLength($content)
164
    {
165
        if (strlen($content) < 4) {
166
            return null;
167
        }
168
        return static::resolveFourByteLittleEndian($content, 0);
169
    }
170

    
171
    /**
172
     * @param string $content
173
     * @param int $start
174
     * @return int
175
     */
176
    public static function resolveFourByteLittleEndian($content, $start)
177
    {
178
        $payload = substr($content, $start, 4);
179
        if (!is_string($payload)) {
180
            throw new ReaderException(
181
                sprintf('Cannot resolve value at offset %d', $start),
182
                1539614260
183
            );
184
        }
185

    
186
        $value = unpack('V', $payload);
187
        if (!isset($value[1])) {
188
            throw new ReaderException(
189
                sprintf('Cannot resolve value at offset %d', $start),
190
                1539614261
191
            );
192
        }
193
        return $value[1];
194
    }
195

    
196
    /**
197
     * @param string $content
198
     * @param int $start
199
     * @return int
200
     */
201
    public static function resolveTwoByteBigEndian($content, $start)
202
    {
203
        $payload = substr($content, $start, 2);
204
        if (!is_string($payload)) {
205
            throw new ReaderException(
206
                sprintf('Cannot resolve value at offset %d', $start),
207
                1539614263
208
            );
209
        }
210

    
211
        $value = unpack('n', $payload);
212
        if (!isset($value[1])) {
213
            throw new ReaderException(
214
                sprintf('Cannot resolve value at offset %d', $start),
215
                1539614264
216
            );
217
        }
218
        return $value[1];
219
    }
220
}