Chris@18
|
1 <?php
|
Chris@18
|
2 namespace TYPO3\PharStreamWrapper\Phar;
|
Chris@18
|
3
|
Chris@18
|
4 /*
|
Chris@18
|
5 * This file is part of the TYPO3 project.
|
Chris@18
|
6 *
|
Chris@18
|
7 * It is free software; you can redistribute it and/or modify it under the terms
|
Chris@18
|
8 * of the MIT License (MIT). For the full copyright and license information,
|
Chris@18
|
9 * please read the LICENSE file that was distributed with this source code.
|
Chris@18
|
10 *
|
Chris@18
|
11 * The TYPO3 project - inspiring people to share!
|
Chris@18
|
12 */
|
Chris@18
|
13
|
Chris@18
|
14 class Reader
|
Chris@18
|
15 {
|
Chris@18
|
16 /**
|
Chris@18
|
17 * @var string
|
Chris@18
|
18 */
|
Chris@18
|
19 private $fileName;
|
Chris@18
|
20
|
Chris@18
|
21 /**
|
Chris@18
|
22 * @var string
|
Chris@18
|
23 */
|
Chris@18
|
24 private $fileType;
|
Chris@18
|
25
|
Chris@18
|
26 /**
|
Chris@18
|
27 * @param string $fileName
|
Chris@18
|
28 */
|
Chris@18
|
29 public function __construct($fileName)
|
Chris@18
|
30 {
|
Chris@18
|
31 if (strpos($fileName, '://') !== false) {
|
Chris@18
|
32 throw new ReaderException(
|
Chris@18
|
33 'File name must not contain stream prefix',
|
Chris@18
|
34 1539623708
|
Chris@18
|
35 );
|
Chris@18
|
36 }
|
Chris@18
|
37
|
Chris@18
|
38 $this->fileName = $fileName;
|
Chris@18
|
39 $this->fileType = $this->determineFileType();
|
Chris@18
|
40 }
|
Chris@18
|
41
|
Chris@18
|
42 /**
|
Chris@18
|
43 * @return Container
|
Chris@18
|
44 */
|
Chris@18
|
45 public function resolveContainer()
|
Chris@18
|
46 {
|
Chris@18
|
47 $data = $this->extractData($this->resolveStream() . $this->fileName);
|
Chris@18
|
48
|
Chris@18
|
49 if ($data['stubContent'] === null) {
|
Chris@18
|
50 throw new ReaderException(
|
Chris@18
|
51 'Cannot resolve stub',
|
Chris@18
|
52 1547807881
|
Chris@18
|
53 );
|
Chris@18
|
54 }
|
Chris@18
|
55 if ($data['manifestContent'] === null || $data['manifestLength'] === null) {
|
Chris@18
|
56 throw new ReaderException(
|
Chris@18
|
57 'Cannot resolve manifest',
|
Chris@18
|
58 1547807882
|
Chris@18
|
59 );
|
Chris@18
|
60 }
|
Chris@18
|
61 if (strlen($data['manifestContent']) < $data['manifestLength']) {
|
Chris@18
|
62 throw new ReaderException(
|
Chris@18
|
63 sprintf(
|
Chris@18
|
64 'Exected manifest length %d, got %d',
|
Chris@18
|
65 strlen($data['manifestContent']),
|
Chris@18
|
66 $data['manifestLength']
|
Chris@18
|
67 ),
|
Chris@18
|
68 1547807883
|
Chris@18
|
69 );
|
Chris@18
|
70 }
|
Chris@18
|
71
|
Chris@18
|
72 return new Container(
|
Chris@18
|
73 Stub::fromContent($data['stubContent']),
|
Chris@18
|
74 Manifest::fromContent($data['manifestContent'])
|
Chris@18
|
75 );
|
Chris@18
|
76 }
|
Chris@18
|
77
|
Chris@18
|
78 /**
|
Chris@18
|
79 * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar'
|
Chris@18
|
80 * @return array
|
Chris@18
|
81 */
|
Chris@18
|
82 private function extractData($fileName)
|
Chris@18
|
83 {
|
Chris@18
|
84 $stubContent = null;
|
Chris@18
|
85 $manifestContent = null;
|
Chris@18
|
86 $manifestLength = null;
|
Chris@18
|
87
|
Chris@18
|
88 $resource = fopen($fileName, 'r');
|
Chris@18
|
89 if (!is_resource($resource)) {
|
Chris@18
|
90 throw new ReaderException(
|
Chris@18
|
91 sprintf('Resource %s could not be opened', $fileName),
|
Chris@18
|
92 1547902055
|
Chris@18
|
93 );
|
Chris@18
|
94 }
|
Chris@18
|
95
|
Chris@18
|
96 while (!feof($resource)) {
|
Chris@18
|
97 $line = fgets($resource);
|
Chris@18
|
98 // stop reading file when manifest can be extracted
|
Chris@18
|
99 if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) {
|
Chris@18
|
100 break;
|
Chris@18
|
101 }
|
Chris@18
|
102
|
Chris@18
|
103 $manifestPosition = strpos($line, '__HALT_COMPILER();');
|
Chris@18
|
104
|
Chris@18
|
105 // first line contains start of manifest
|
Chris@18
|
106 if ($stubContent === null && $manifestContent === null && $manifestPosition !== false) {
|
Chris@18
|
107 $stubContent = substr($line, 0, $manifestPosition - 1);
|
Chris@18
|
108 $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
|
Chris@18
|
109 $manifestLength = $this->resolveManifestLength($manifestContent);
|
Chris@18
|
110 // line contains start of stub
|
Chris@18
|
111 } elseif ($stubContent === null) {
|
Chris@18
|
112 $stubContent = $line;
|
Chris@18
|
113 // line contains start of manifest
|
Chris@18
|
114 } elseif ($manifestContent === null && $manifestPosition !== false) {
|
Chris@18
|
115 $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
|
Chris@18
|
116 $manifestLength = $this->resolveManifestLength($manifestContent);
|
Chris@18
|
117 // manifest has been started (thus is cannot be stub anymore), add content
|
Chris@18
|
118 } elseif ($manifestContent !== null) {
|
Chris@18
|
119 $manifestContent .= $line;
|
Chris@18
|
120 $manifestLength = $this->resolveManifestLength($manifestContent);
|
Chris@18
|
121 // stub has been started (thus cannot be manifest here, yet), add content
|
Chris@18
|
122 } elseif ($stubContent !== null) {
|
Chris@18
|
123 $stubContent .= $line;
|
Chris@18
|
124 }
|
Chris@18
|
125 }
|
Chris@18
|
126 fclose($resource);
|
Chris@18
|
127
|
Chris@18
|
128 return array(
|
Chris@18
|
129 'stubContent' => $stubContent,
|
Chris@18
|
130 'manifestContent' => $manifestContent,
|
Chris@18
|
131 'manifestLength' => $manifestLength,
|
Chris@18
|
132 );
|
Chris@18
|
133 }
|
Chris@18
|
134
|
Chris@18
|
135 /**
|
Chris@18
|
136 * Resolves stream in order to handle compressed Phar archives.
|
Chris@18
|
137 *
|
Chris@18
|
138 * @return string
|
Chris@18
|
139 */
|
Chris@18
|
140 private function resolveStream()
|
Chris@18
|
141 {
|
Chris@18
|
142 if ($this->fileType === 'application/x-gzip') {
|
Chris@18
|
143 return 'compress.zlib://';
|
Chris@18
|
144 } elseif ($this->fileType === 'application/x-bzip2') {
|
Chris@18
|
145 return 'compress.bzip2://';
|
Chris@18
|
146 }
|
Chris@18
|
147 return '';
|
Chris@18
|
148 }
|
Chris@18
|
149
|
Chris@18
|
150 /**
|
Chris@18
|
151 * @return string
|
Chris@18
|
152 */
|
Chris@18
|
153 private function determineFileType()
|
Chris@18
|
154 {
|
Chris@18
|
155 $fileInfo = new \finfo();
|
Chris@18
|
156 return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE);
|
Chris@18
|
157 }
|
Chris@18
|
158
|
Chris@18
|
159 /**
|
Chris@18
|
160 * @param string $content
|
Chris@18
|
161 * @return int|null
|
Chris@18
|
162 */
|
Chris@18
|
163 private function resolveManifestLength($content)
|
Chris@18
|
164 {
|
Chris@18
|
165 if (strlen($content) < 4) {
|
Chris@18
|
166 return null;
|
Chris@18
|
167 }
|
Chris@18
|
168 return static::resolveFourByteLittleEndian($content, 0);
|
Chris@18
|
169 }
|
Chris@18
|
170
|
Chris@18
|
171 /**
|
Chris@18
|
172 * @param string $content
|
Chris@18
|
173 * @param int $start
|
Chris@18
|
174 * @return int
|
Chris@18
|
175 */
|
Chris@18
|
176 public static function resolveFourByteLittleEndian($content, $start)
|
Chris@18
|
177 {
|
Chris@18
|
178 $payload = substr($content, $start, 4);
|
Chris@18
|
179 if (!is_string($payload)) {
|
Chris@18
|
180 throw new ReaderException(
|
Chris@18
|
181 sprintf('Cannot resolve value at offset %d', $start),
|
Chris@18
|
182 1539614260
|
Chris@18
|
183 );
|
Chris@18
|
184 }
|
Chris@18
|
185
|
Chris@18
|
186 $value = unpack('V', $payload);
|
Chris@18
|
187 if (!isset($value[1])) {
|
Chris@18
|
188 throw new ReaderException(
|
Chris@18
|
189 sprintf('Cannot resolve value at offset %d', $start),
|
Chris@18
|
190 1539614261
|
Chris@18
|
191 );
|
Chris@18
|
192 }
|
Chris@18
|
193 return $value[1];
|
Chris@18
|
194 }
|
Chris@18
|
195
|
Chris@18
|
196 /**
|
Chris@18
|
197 * @param string $content
|
Chris@18
|
198 * @param int $start
|
Chris@18
|
199 * @return int
|
Chris@18
|
200 */
|
Chris@18
|
201 public static function resolveTwoByteBigEndian($content, $start)
|
Chris@18
|
202 {
|
Chris@18
|
203 $payload = substr($content, $start, 2);
|
Chris@18
|
204 if (!is_string($payload)) {
|
Chris@18
|
205 throw new ReaderException(
|
Chris@18
|
206 sprintf('Cannot resolve value at offset %d', $start),
|
Chris@18
|
207 1539614263
|
Chris@18
|
208 );
|
Chris@18
|
209 }
|
Chris@18
|
210
|
Chris@18
|
211 $value = unpack('n', $payload);
|
Chris@18
|
212 if (!isset($value[1])) {
|
Chris@18
|
213 throw new ReaderException(
|
Chris@18
|
214 sprintf('Cannot resolve value at offset %d', $start),
|
Chris@18
|
215 1539614264
|
Chris@18
|
216 );
|
Chris@18
|
217 }
|
Chris@18
|
218 return $value[1];
|
Chris@18
|
219 }
|
Chris@18
|
220 }
|