Mercurial > hg > cmmr2012-drupal-site
comparison vendor/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php @ 5:12f9dff5fda9 tip
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:34:47 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
4:a9cd425dd02b | 5:12f9dff5fda9 |
---|---|
1 <?php | |
2 namespace TYPO3\PharStreamWrapper\Resolver; | |
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 use TYPO3\PharStreamWrapper\Helper; | |
15 use TYPO3\PharStreamWrapper\Manager; | |
16 use TYPO3\PharStreamWrapper\Phar\Reader; | |
17 use TYPO3\PharStreamWrapper\Resolvable; | |
18 | |
19 class PharInvocationResolver implements Resolvable | |
20 { | |
21 const RESOLVE_REALPATH = 1; | |
22 const RESOLVE_ALIAS = 2; | |
23 const ASSERT_INTERNAL_INVOCATION = 32; | |
24 | |
25 /** | |
26 * @var string[] | |
27 */ | |
28 private $invocationFunctionNames = array( | |
29 'include', | |
30 'include_once', | |
31 'require', | |
32 'require_once' | |
33 ); | |
34 | |
35 /** | |
36 * Contains resolved base names in order to reduce file IO. | |
37 * | |
38 * @var string[] | |
39 */ | |
40 private $baseNames = array(); | |
41 | |
42 /** | |
43 * Resolves PharInvocation value object (baseName and optional alias). | |
44 * | |
45 * Phar aliases are intended to be used only inside Phar archives, however | |
46 * PharStreamWrapper needs this information exposed outside of Phar as well | |
47 * It is possible that same alias is used for different $baseName values. | |
48 * That's why PharInvocationCollection behaves like a stack when resolving | |
49 * base-name for a given alias. On the other hand it is not possible that | |
50 * one $baseName is referring to multiple aliases. | |
51 * @see https://secure.php.net/manual/en/phar.setalias.php | |
52 * @see https://secure.php.net/manual/en/phar.mapphar.php | |
53 * | |
54 * @param string $path | |
55 * @param int|null $flags | |
56 * @return null|PharInvocation | |
57 */ | |
58 public function resolve($path, $flags = null) | |
59 { | |
60 $hasPharPrefix = Helper::hasPharPrefix($path); | |
61 if ($flags === null) { | |
62 $flags = static::RESOLVE_REALPATH | static::RESOLVE_ALIAS | static::ASSERT_INTERNAL_INVOCATION; | |
63 } | |
64 | |
65 if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) { | |
66 $invocation = $this->findByAlias($path); | |
67 if ($invocation !== null) { | |
68 return $invocation; | |
69 } | |
70 } | |
71 | |
72 $baseName = $this->resolveBaseName($path, $flags); | |
73 if ($baseName === null) { | |
74 return null; | |
75 } | |
76 | |
77 if ($flags & static::RESOLVE_REALPATH) { | |
78 $baseName = $this->baseNames[$baseName]; | |
79 } | |
80 | |
81 return $this->retrieveInvocation($baseName, $flags); | |
82 } | |
83 | |
84 /** | |
85 * Retrieves PharInvocation, either existing in collection or created on demand | |
86 * with resolving a potential alias name used in the according Phar archive. | |
87 * | |
88 * @param string $baseName | |
89 * @param int $flags | |
90 * @return PharInvocation | |
91 */ | |
92 private function retrieveInvocation($baseName, $flags) | |
93 { | |
94 $invocation = $this->findByBaseName($baseName); | |
95 if ($invocation !== null) { | |
96 return $invocation; | |
97 } | |
98 | |
99 if ($flags & static::RESOLVE_ALIAS) { | |
100 $reader = new Reader($baseName); | |
101 $alias = $reader->resolveContainer()->getAlias(); | |
102 } else { | |
103 $alias = ''; | |
104 } | |
105 // add unconfirmed(!) new invocation to collection | |
106 $invocation = new PharInvocation($baseName, $alias); | |
107 Manager::instance()->getCollection()->collect($invocation); | |
108 return $invocation; | |
109 } | |
110 | |
111 /** | |
112 * @param string $path | |
113 * @param int $flags | |
114 * @return null|string | |
115 */ | |
116 private function resolveBaseName($path, $flags) | |
117 { | |
118 $baseName = $this->findInBaseNames($path); | |
119 if ($baseName !== null) { | |
120 return $baseName; | |
121 } | |
122 | |
123 $baseName = Helper::determineBaseFile($path); | |
124 if ($baseName !== null) { | |
125 $this->addBaseName($baseName); | |
126 return $baseName; | |
127 } | |
128 | |
129 $possibleAlias = $this->resolvePossibleAlias($path); | |
130 if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) { | |
131 return null; | |
132 } | |
133 | |
134 $trace = debug_backtrace(); | |
135 foreach ($trace as $item) { | |
136 if (!isset($item['function']) || !isset($item['args'][0]) | |
137 || !in_array($item['function'], $this->invocationFunctionNames, true)) { | |
138 continue; | |
139 } | |
140 $currentPath = $item['args'][0]; | |
141 if (Helper::hasPharPrefix($currentPath)) { | |
142 continue; | |
143 } | |
144 $currentBaseName = Helper::determineBaseFile($currentPath); | |
145 if ($currentBaseName === null) { | |
146 continue; | |
147 } | |
148 // ensure the possible alias name (how we have been called initially) matches | |
149 // the resolved alias name that was retrieved by the current possible base name | |
150 $reader = new Reader($currentBaseName); | |
151 $currentAlias = $reader->resolveContainer()->getAlias(); | |
152 if ($currentAlias !== $possibleAlias) { | |
153 continue; | |
154 } | |
155 $this->addBaseName($currentBaseName); | |
156 return $currentBaseName; | |
157 } | |
158 | |
159 return null; | |
160 } | |
161 | |
162 /** | |
163 * @param string $path | |
164 * @return null|string | |
165 */ | |
166 private function resolvePossibleAlias($path) | |
167 { | |
168 $normalizedPath = Helper::normalizePath($path); | |
169 return strstr($normalizedPath, '/', true) ?: null; | |
170 } | |
171 | |
172 /** | |
173 * @param string $baseName | |
174 * @return null|PharInvocation | |
175 */ | |
176 private function findByBaseName($baseName) | |
177 { | |
178 return Manager::instance()->getCollection()->findByCallback( | |
179 function (PharInvocation $candidate) use ($baseName) { | |
180 return $candidate->getBaseName() === $baseName; | |
181 }, | |
182 true | |
183 ); | |
184 } | |
185 | |
186 /** | |
187 * @param string $path | |
188 * @return null|string | |
189 */ | |
190 private function findInBaseNames($path) | |
191 { | |
192 // return directly if the resolved base name was submitted | |
193 if (in_array($path, $this->baseNames, true)) { | |
194 return $path; | |
195 } | |
196 | |
197 $parts = explode('/', Helper::normalizePath($path)); | |
198 | |
199 while (count($parts)) { | |
200 $currentPath = implode('/', $parts); | |
201 if (isset($this->baseNames[$currentPath])) { | |
202 return $currentPath; | |
203 } | |
204 array_pop($parts); | |
205 } | |
206 | |
207 return null; | |
208 } | |
209 | |
210 /** | |
211 * @param string $baseName | |
212 */ | |
213 private function addBaseName($baseName) | |
214 { | |
215 if (isset($this->baseNames[$baseName])) { | |
216 return; | |
217 } | |
218 $this->baseNames[$baseName] = realpath($baseName); | |
219 } | |
220 | |
221 /** | |
222 * Finds confirmed(!) invocations by alias. | |
223 * | |
224 * @param string $path | |
225 * @return null|PharInvocation | |
226 * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation() | |
227 */ | |
228 private function findByAlias($path) | |
229 { | |
230 $possibleAlias = $this->resolvePossibleAlias($path); | |
231 if ($possibleAlias === null) { | |
232 return null; | |
233 } | |
234 return Manager::instance()->getCollection()->findByCallback( | |
235 function (PharInvocation $candidate) use ($possibleAlias) { | |
236 return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias; | |
237 }, | |
238 true | |
239 ); | |
240 } | |
241 } |