[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 declare(strict_types=1); 3 namespace TYPO3\PharStreamWrapper\Resolver; 4 5 /* 6 * This file is part of the TYPO3 project. 7 * 8 * It is free software; you can redistribute it and/or modify it under the terms 9 * of the MIT License (MIT). For the full copyright and license information, 10 * please read the LICENSE file that was distributed with this source code. 11 * 12 * The TYPO3 project - inspiring people to share! 13 */ 14 15 use TYPO3\PharStreamWrapper\Helper; 16 use TYPO3\PharStreamWrapper\Manager; 17 use TYPO3\PharStreamWrapper\Phar\Reader; 18 use TYPO3\PharStreamWrapper\Phar\ReaderException; 19 use TYPO3\PharStreamWrapper\Resolvable; 20 21 class PharInvocationResolver implements Resolvable 22 { 23 const RESOLVE_REALPATH = 1; 24 const RESOLVE_ALIAS = 2; 25 const ASSERT_INTERNAL_INVOCATION = 32; 26 27 /** 28 * @var string[] 29 */ 30 private $invocationFunctionNames = [ 31 'include', 32 'include_once', 33 'require', 34 'require_once' 35 ]; 36 37 /** 38 * Contains resolved base names in order to reduce file IO. 39 * 40 * @var string[] 41 */ 42 private $baseNames = []; 43 44 /** 45 * Resolves PharInvocation value object (baseName and optional alias). 46 * 47 * Phar aliases are intended to be used only inside Phar archives, however 48 * PharStreamWrapper needs this information exposed outside of Phar as well 49 * It is possible that same alias is used for different $baseName values. 50 * That's why PharInvocationCollection behaves like a stack when resolving 51 * base-name for a given alias. On the other hand it is not possible that 52 * one $baseName is referring to multiple aliases. 53 * @see https://secure.php.net/manual/en/phar.setalias.php 54 * @see https://secure.php.net/manual/en/phar.mapphar.php 55 * 56 * @param string $path 57 * @param int|null $flags 58 * @return null|PharInvocation 59 */ 60 public function resolve(string $path, int $flags = null) 61 { 62 $hasPharPrefix = Helper::hasPharPrefix($path); 63 $flags = $flags ?? static::RESOLVE_REALPATH | static::RESOLVE_ALIAS; 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(string $baseName, int $flags): PharInvocation 93 { 94 $invocation = $this->findByBaseName($baseName); 95 if ($invocation !== null) { 96 return $invocation; 97 } 98 99 if ($flags & static::RESOLVE_ALIAS) { 100 $alias = (new Reader($baseName))->resolveContainer()->getAlias(); 101 } else { 102 $alias = ''; 103 } 104 // add unconfirmed(!) new invocation to collection 105 $invocation = new PharInvocation($baseName, $alias); 106 Manager::instance()->getCollection()->collect($invocation); 107 return $invocation; 108 } 109 110 /** 111 * @param string $path 112 * @param int $flags 113 * @return null|string 114 */ 115 private function resolveBaseName(string $path, int $flags) 116 { 117 $baseName = $this->findInBaseNames($path); 118 if ($baseName !== null) { 119 return $baseName; 120 } 121 122 $baseName = Helper::determineBaseFile($path); 123 if ($baseName !== null) { 124 $this->addBaseName($baseName); 125 return $baseName; 126 } 127 128 $possibleAlias = $this->resolvePossibleAlias($path); 129 if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) { 130 return null; 131 } 132 133 $trace = debug_backtrace(); 134 foreach ($trace as $item) { 135 if (!isset($item['function']) || !isset($item['args'][0]) 136 || !in_array($item['function'], $this->invocationFunctionNames, true)) { 137 continue; 138 } 139 $currentPath = $item['args'][0]; 140 if (Helper::hasPharPrefix($currentPath)) { 141 continue; 142 } 143 $currentBaseName = Helper::determineBaseFile($currentPath); 144 if ($currentBaseName === null) { 145 continue; 146 } 147 // ensure the possible alias name (how we have been called initially) matches 148 // the resolved alias name that was retrieved by the current possible base name 149 try { 150 $currentAlias = (new Reader($currentBaseName))->resolveContainer()->getAlias(); 151 } catch (ReaderException $exception) { 152 // most probably that was not a Phar file 153 continue; 154 } 155 if (empty($currentAlias) || $currentAlias !== $possibleAlias) { 156 continue; 157 } 158 $this->addBaseName($currentBaseName); 159 return $currentBaseName; 160 } 161 162 return null; 163 } 164 165 /** 166 * @param string $path 167 * @return null|string 168 */ 169 private function resolvePossibleAlias(string $path) 170 { 171 $normalizedPath = Helper::normalizePath($path); 172 return strstr($normalizedPath, '/', true) ?: null; 173 } 174 175 /** 176 * @param string $baseName 177 * @return null|PharInvocation 178 */ 179 private function findByBaseName(string $baseName) 180 { 181 return Manager::instance()->getCollection()->findByCallback( 182 function (PharInvocation $candidate) use ($baseName) { 183 return $candidate->getBaseName() === $baseName; 184 }, 185 true 186 ); 187 } 188 189 /** 190 * @param string $path 191 * @return null|string 192 */ 193 private function findInBaseNames(string $path) 194 { 195 // return directly if the resolved base name was submitted 196 if (in_array($path, $this->baseNames, true)) { 197 return $path; 198 } 199 200 $parts = explode('/', Helper::normalizePath($path)); 201 202 while (count($parts)) { 203 $currentPath = implode('/', $parts); 204 if (isset($this->baseNames[$currentPath])) { 205 return $currentPath; 206 } 207 array_pop($parts); 208 } 209 210 return null; 211 } 212 213 /** 214 * @param string $baseName 215 */ 216 private function addBaseName(string $baseName) 217 { 218 if (isset($this->baseNames[$baseName])) { 219 return; 220 } 221 $this->baseNames[$baseName] = Helper::normalizeWindowsPath( 222 realpath($baseName) 223 ); 224 } 225 226 /** 227 * Finds confirmed(!) invocations by alias. 228 * 229 * @param string $path 230 * @return null|PharInvocation 231 * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation() 232 */ 233 private function findByAlias(string $path) 234 { 235 $possibleAlias = $this->resolvePossibleAlias($path); 236 if ($possibleAlias === null) { 237 return null; 238 } 239 return Manager::instance()->getCollection()->findByCallback( 240 function (PharInvocation $candidate) use ($possibleAlias) { 241 return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias; 242 }, 243 true 244 ); 245 } 246 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |