thecodingmachine /
classname-mapper
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | namespace Mouf\Composer; |
||
| 3 | |||
| 4 | /** |
||
| 5 | * The class maps a class name to one or many possible file names according to PSR-0 or PSR-4 rules. |
||
| 6 | * |
||
| 7 | * @author David Négrier <[email protected]> |
||
| 8 | */ |
||
| 9 | class ClassNameMapper |
||
| 10 | { |
||
| 11 | /** |
||
| 12 | * |
||
| 13 | * @var array<namespace, path[]> |
||
| 14 | */ |
||
| 15 | private $psr0Namespaces = array(); |
||
| 16 | |||
| 17 | /** |
||
| 18 | * |
||
| 19 | * @var array<namespace, path[]> |
||
| 20 | */ |
||
| 21 | private $psr4Namespaces = array(); |
||
| 22 | |||
| 23 | /** |
||
| 24 | * Registers a PSR-0 namespace. |
||
| 25 | * |
||
| 26 | * @param string $namespace The namespace to register |
||
| 27 | * @param string|array $path The path on the filesystem (or an array of paths) |
||
| 28 | */ |
||
| 29 | View Code Duplication | public function registerPsr0Namespace($namespace, $path) { |
|
| 30 | // A namespace always ends with a \ |
||
| 31 | $namespace = trim($namespace, '\\').'\\'; |
||
| 32 | if ($namespace === '\\') { |
||
| 33 | $namespace = ''; |
||
| 34 | } |
||
| 35 | |||
| 36 | if (!is_array($path)) { |
||
| 37 | $path = [$path]; |
||
| 38 | } |
||
| 39 | // Paths always end with a / |
||
| 40 | $paths = array_map([self::class, 'normalizeDirectory'], $path); |
||
| 41 | |||
| 42 | if (!isset($this->psr0Namespaces[$namespace])) { |
||
| 43 | $this->psr0Namespaces[$namespace] = $paths; |
||
| 44 | } else { |
||
| 45 | $this->psr0Namespaces[$namespace] = array_merge($this->psr0Namespaces[$namespace], $paths); |
||
| 46 | } |
||
| 47 | } |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Registers a PSR-4 namespace. |
||
| 51 | * |
||
| 52 | * @param string $namespace The namespace to register |
||
| 53 | * @param string|array $path The path on the filesystem (or an array of paths) |
||
| 54 | */ |
||
| 55 | View Code Duplication | public function registerPsr4Namespace($namespace, $path) { |
|
| 56 | // A namespace always ends with a \ |
||
| 57 | $namespace = trim($namespace, '\\').'\\'; |
||
| 58 | if ($namespace === '\\') { |
||
| 59 | $namespace = ''; |
||
| 60 | } |
||
| 61 | |||
| 62 | if (!is_array($path)) { |
||
| 63 | $path = [$path]; |
||
| 64 | } |
||
| 65 | // Paths always end with a / |
||
| 66 | $paths = array_map([self::class, 'normalizeDirectory'], $path); |
||
| 67 | |||
| 68 | if (!isset($this->psr4Namespaces[$namespace])) { |
||
| 69 | $this->psr4Namespaces[$namespace] = $paths; |
||
| 70 | } else { |
||
| 71 | $this->psr4Namespaces[$namespace] = array_merge($this->psr4Namespaces[$namespace], $paths); |
||
| 72 | } |
||
| 73 | } |
||
| 74 | |||
| 75 | /** |
||
| 76 | * @param string $composerJsonPath |
||
| 77 | * @param string $rootPath |
||
| 78 | * @param bool $useAutoloadDev |
||
| 79 | * @return ClassNameMapper |
||
| 80 | */ |
||
| 81 | public static function createFromComposerFile($composerJsonPath = null, $rootPath = null, $useAutoloadDev = false) { |
||
| 82 | $classNameMapper = new ClassNameMapper(); |
||
| 83 | |||
| 84 | if ($composerJsonPath === null) { |
||
| 85 | $composerJsonPath = __DIR__.'/../../../../composer.json'; |
||
| 86 | } |
||
| 87 | |||
| 88 | $classNameMapper->loadComposerFile($composerJsonPath, $rootPath, $useAutoloadDev); |
||
| 89 | |||
| 90 | return $classNameMapper; |
||
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * |
||
| 95 | * @param string $composerJsonPath Path to the composer file |
||
| 96 | * @param string $rootPath Root path of the project (or null) |
||
| 97 | */ |
||
| 98 | public function loadComposerFile($composerJsonPath, $rootPath = null, $useAutoloadDev = false) { |
||
| 99 | $composer = json_decode(file_get_contents($composerJsonPath), true); |
||
| 100 | |||
| 101 | if ($rootPath) { |
||
| 102 | $relativePath = self::makePathRelative(dirname($composerJsonPath), $rootPath); |
||
| 103 | } else { |
||
| 104 | $relativePath = null; |
||
| 105 | } |
||
| 106 | |||
| 107 | View Code Duplication | if (isset($composer["autoload"]["psr-0"])) { |
|
| 108 | $psr0 = $composer["autoload"]["psr-0"]; |
||
| 109 | foreach ($psr0 as $namespace => $paths) { |
||
| 110 | if ($relativePath != null) { |
||
| 111 | if (!is_array($paths)) { |
||
| 112 | $paths = [$paths]; |
||
| 113 | } |
||
| 114 | $paths = array_map(function($path) use ($relativePath) { |
||
| 115 | return rtrim($relativePath,'\\/').'/'.ltrim($path, '\\/'); |
||
| 116 | }, $paths); |
||
| 117 | } |
||
| 118 | $this->registerPsr0Namespace($namespace, $paths); |
||
| 119 | } |
||
| 120 | } |
||
| 121 | |||
| 122 | View Code Duplication | if (isset($composer["autoload"]["psr-4"])) { |
|
| 123 | $psr4 = $composer["autoload"]["psr-4"]; |
||
| 124 | foreach ($psr4 as $namespace => $paths) { |
||
| 125 | if ($relativePath != null) { |
||
| 126 | if (!is_array($paths)) { |
||
| 127 | $paths = [$paths]; |
||
| 128 | } |
||
| 129 | $paths = array_map(function($path) use ($relativePath) { |
||
| 130 | return rtrim($relativePath,'\\/').'/'.ltrim($path, '\\/'); |
||
| 131 | }, $paths); |
||
| 132 | } |
||
| 133 | $this->registerPsr4Namespace($namespace, $paths); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | if ($useAutoloadDev) { |
||
| 138 | View Code Duplication | if (isset($composer["autoload-dev"]["psr-0"])) { |
|
| 139 | $psr0 = $composer["autoload-dev"]["psr-0"]; |
||
| 140 | foreach ($psr0 as $namespace => $paths) { |
||
| 141 | if ($relativePath != null) { |
||
| 142 | if (!is_array($paths)) { |
||
| 143 | $paths = [$paths]; |
||
| 144 | } |
||
| 145 | $paths = array_map(function($path) use ($relativePath) { |
||
| 146 | return rtrim($relativePath,'\\/').'/'.ltrim($path, '\\/'); |
||
| 147 | }, $paths); |
||
| 148 | } |
||
| 149 | $this->registerPsr0Namespace($namespace, $paths); |
||
| 150 | } |
||
| 151 | } |
||
| 152 | |||
| 153 | View Code Duplication | if (isset($composer["autoload-dev"]["psr-4"])) { |
|
| 154 | $psr4 = $composer["autoload-dev"]["psr-4"]; |
||
| 155 | foreach ($psr4 as $namespace => $paths) { |
||
| 156 | if ($relativePath != null) { |
||
| 157 | if (!is_array($paths)) { |
||
| 158 | $paths = [$paths]; |
||
| 159 | } |
||
| 160 | $paths = array_map(function($path) use ($relativePath) { |
||
| 161 | return rtrim($relativePath,'\\/').'/'.ltrim($path, '\\/'); |
||
| 162 | }, $paths); |
||
| 163 | } |
||
| 164 | $this->registerPsr4Namespace($namespace, $paths); |
||
| 165 | } |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | return $this; |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Given an existing path, convert it to a path relative to a given starting path. |
||
| 174 | * Shamelessly borrowed to Symfony :). Thanks guys. |
||
| 175 | * Note: we do not include Symfony's "FileSystem" component to avoid adding too many dependencies. |
||
| 176 | * |
||
| 177 | * @param string $endPath Absolute path of target |
||
| 178 | * @param string $startPath Absolute path where traversal begins |
||
| 179 | * |
||
| 180 | * @return string Path of target relative to starting path |
||
| 181 | */ |
||
| 182 | private static function makePathRelative($endPath, $startPath) |
||
| 183 | { |
||
| 184 | // Normalize separators on Windows |
||
| 185 | if ('\\' === DIRECTORY_SEPARATOR) { |
||
| 186 | $endPath = strtr($endPath, '\\', '/'); |
||
| 187 | $startPath = strtr($startPath, '\\', '/'); |
||
| 188 | } |
||
| 189 | // Split the paths into arrays |
||
| 190 | $startPathArr = explode('/', trim($startPath, '/')); |
||
| 191 | $endPathArr = explode('/', trim($endPath, '/')); |
||
| 192 | // Find for which directory the common path stops |
||
| 193 | $index = 0; |
||
| 194 | while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { |
||
| 195 | $index++; |
||
| 196 | } |
||
| 197 | // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) |
||
| 198 | $depth = count($startPathArr) - $index; |
||
| 199 | // Repeated "../" for each level need to reach the common path |
||
| 200 | $traverser = str_repeat('../', $depth); |
||
| 201 | $endPathRemainder = implode('/', array_slice($endPathArr, $index)); |
||
| 202 | // Construct $endPath from traversing to the common path, then to the remaining $endPath |
||
| 203 | $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : ''); |
||
| 204 | return (strlen($relativePath) === 0) ? './' : $relativePath; |
||
| 205 | } |
||
| 206 | |||
| 207 | /** |
||
| 208 | * Returns a list of all namespaces that are managed by the ClassNameMapper. |
||
| 209 | * |
||
| 210 | * @return string[] |
||
| 211 | */ |
||
| 212 | public function getManagedNamespaces() { |
||
| 213 | return array_keys(array_merge($this->psr0Namespaces, $this->psr4Namespaces)); |
||
| 214 | } |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Returns a list of paths that can be used to store $className. |
||
| 218 | * |
||
| 219 | * @param string $className |
||
| 220 | * @return string[] |
||
| 221 | */ |
||
| 222 | public function getPossibleFileNames($className) { |
||
| 223 | $possibleFileNames = array(); |
||
| 224 | $className = ltrim($className, '\\'); |
||
| 225 | |||
| 226 | $psr0unfactorizedAutoload = $this->unfactorizeAutoload($this->psr0Namespaces); |
||
| 227 | |||
| 228 | foreach ($psr0unfactorizedAutoload as $result) { |
||
| 229 | $namespace = $result['namespace']; |
||
| 230 | $directory = $result['directory']; |
||
| 231 | |||
| 232 | if ($namespace === '') { |
||
| 233 | $tmpClassName = $className; |
||
| 234 | if ($lastNsPos = strripos($tmpClassName, '\\')) { |
||
| 235 | $namespace = substr($tmpClassName, 0, $lastNsPos); |
||
| 236 | $tmpClassName = substr($tmpClassName, $lastNsPos + 1); |
||
| 237 | } |
||
| 238 | |||
| 239 | $fileName = str_replace('\\', '/', $namespace) . '/' . str_replace('_', '/', $tmpClassName) . '.php'; |
||
| 240 | $possibleFileNames[] = $directory.$fileName; |
||
| 241 | } else { |
||
| 242 | if (strpos($className, $namespace) === 0) { |
||
| 243 | $tmpClassName = $className; |
||
| 244 | $fileName = ''; |
||
| 245 | if ($lastNsPos = strripos($tmpClassName, '\\')) { |
||
| 246 | $namespace = substr($tmpClassName, 0, $lastNsPos); |
||
| 247 | $tmpClassName = substr($tmpClassName, $lastNsPos + 1); |
||
| 248 | $fileName = str_replace('\\', '/', $namespace) . '/'; |
||
| 249 | } |
||
| 250 | $fileName .= str_replace('_', '/', $tmpClassName) . '.php'; |
||
| 251 | |||
| 252 | $possibleFileNames[] = $directory.$fileName; |
||
| 253 | } |
||
| 254 | } |
||
| 255 | } |
||
| 256 | |||
| 257 | $psr4unfactorizedAutoload = $this->unfactorizeAutoload($this->psr4Namespaces); |
||
| 258 | |||
| 259 | foreach ($psr4unfactorizedAutoload as $result) { |
||
| 260 | $namespace = $result['namespace']; |
||
| 261 | $directory = $result['directory']; |
||
| 262 | |||
| 263 | if ($namespace === '') { |
||
| 264 | $fileName = str_replace('\\', '/', $className) . '.php'; |
||
| 265 | $possibleFileNames[] = $directory.$fileName; |
||
| 266 | } else { |
||
| 267 | if (strpos($className, $namespace) === 0) { |
||
| 268 | $shortenedClassName = substr($className, strlen($namespace)); |
||
| 269 | |||
| 270 | if ($lastNsPos = strripos($shortenedClassName, '\\')) { |
||
| 271 | $namespace = substr($shortenedClassName, 0, $lastNsPos); |
||
| 272 | $shortenedClassName = substr($shortenedClassName, $lastNsPos + 1); |
||
| 273 | $fileName = str_replace('\\', '/', $namespace) . '/' . $shortenedClassName; |
||
| 274 | } else { |
||
| 275 | $fileName = $shortenedClassName; |
||
| 276 | } |
||
| 277 | $fileName .= '.php'; |
||
| 278 | |||
| 279 | $possibleFileNames[] = $directory . $fileName; |
||
| 280 | } |
||
| 281 | } |
||
| 282 | } |
||
| 283 | |||
| 284 | return $possibleFileNames; |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Takes in parameter an array like |
||
| 289 | * [{ "Mouf": "src/" }] or [{ "Mouf": ["src/", "src2/"] }] . |
||
| 290 | * returns |
||
| 291 | * [ |
||
| 292 | * {"namespace"=> "Mouf", "directory"=>"src/"}, |
||
| 293 | * {"namespace"=> "Mouf", "directory"=>"src2/"} |
||
| 294 | * ] |
||
| 295 | * |
||
| 296 | * @param array $autoload |
||
| 297 | * @return array<int, array<string, string>> |
||
|
0 ignored issues
–
show
|
|||
| 298 | */ |
||
| 299 | private static function unfactorizeAutoload(array $autoload) { |
||
| 300 | $result = array(); |
||
| 301 | foreach ($autoload as $namespace => $directories) { |
||
| 302 | if (!is_array($directories)) { |
||
| 303 | $result[] = array( |
||
| 304 | "namespace" => $namespace, |
||
| 305 | "directory" => self::normalizeDirectory($directories) |
||
| 306 | ); |
||
| 307 | } else { |
||
| 308 | foreach ($directories as $dir) { |
||
| 309 | $result[] = array( |
||
| 310 | "namespace" => $namespace, |
||
| 311 | "directory" => self::normalizeDirectory($dir) |
||
| 312 | ); |
||
| 313 | } |
||
| 314 | } |
||
| 315 | } |
||
| 316 | return $result; |
||
| 317 | } |
||
| 318 | |||
| 319 | /** |
||
| 320 | * Makes sure the directory ends with a / (unless the string is empty) |
||
| 321 | * |
||
| 322 | * @param string $dir |
||
| 323 | * @return string |
||
| 324 | */ |
||
| 325 | private static function normalizeDirectory($dir) { |
||
| 326 | return $dir === '' ? '' : rtrim($dir, '\\/').'/'; |
||
| 327 | } |
||
| 328 | } |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.