Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
44 / 44 |
Psr4ClassLocator | |
100.00% |
1 / 1 |
|
100.00% |
5 / 5 |
29 | |
100.00% |
44 / 44 |
__construct | |
100.00% |
1 / 1 |
6 | |
100.00% |
11 / 11 |
|||
getClasses | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
scanDirectory | |
100.00% |
1 / 1 |
14 | |
100.00% |
18 / 18 |
|||
mapClassNameToFileName | |
100.00% |
1 / 1 |
4 | |
100.00% |
7 / 7 |
|||
mapFileNameToClassName | |
100.00% |
1 / 1 |
4 | |
100.00% |
6 / 6 |
<?php declare(strict_types = 1); | |
/* | |
* Copyright (c) 2019, Josef Kufner <josef@kufner.cz> | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
namespace Smalldb\ClassLocator; | |
use Smalldb\StateMachine\InvalidArgumentException; | |
class Psr4ClassLocator implements ClassLocator | |
{ | |
use BrokenClassHandlerTrait; | |
private string $namespace; | |
private string $directory; | |
private RealPathList $includePaths; | |
private PathList $excludePaths; | |
public function __construct(string $namespace, string $directory, $includePaths = [], $excludePaths = []) | |
{ | |
$this->namespace = $namespace === '' ? '\\' : ($namespace[-1] === "\\" ? $namespace : $namespace . "\\"); | |
$this->directory = $directory; | |
if (!is_dir($this->directory)) { | |
throw new InvalidArgumentException("Not a directory: $this->directory"); | |
} | |
if ($includePaths instanceof RealPathList) { | |
$this->includePaths = $includePaths; | |
} else { | |
$this->includePaths = new RealPathList($directory, $includePaths ?? []); | |
} | |
if ($excludePaths instanceof RealPathList) { | |
$this->excludePaths = $excludePaths; | |
} else { | |
$this->excludePaths = new RealPathList($directory, $excludePaths ?? []); | |
} | |
} | |
public function getClasses(): \Generator | |
{ | |
yield from $this->scanDirectory($this->namespace, $this->directory); | |
} | |
private function scanDirectory($namespace, $absPath): \Generator | |
{ | |
foreach (scandir($absPath) as $f) { | |
if ($f[0] === '.') { | |
continue; | |
} | |
$fileAbsPath = "$absPath/$f"; | |
if ($this->excludePaths->containsExact($fileAbsPath)) { | |
continue; | |
} | |
$dotPos = strpos($f, '.'); | |
$className = $namespace . ($dotPos ? substr($f, 0, $dotPos) : $f); | |
if (is_dir($fileAbsPath)) { | |
yield from $this->scanDirectory($className . "\\", $fileAbsPath); | |
} else if ($dotPos && substr($f, $dotPos) === '.php') { | |
if (!$this->includePaths->isEmpty() && !$this->includePaths->contains(dirname($fileAbsPath))) { | |
continue; | |
} | |
try { | |
if (class_exists($className) || interface_exists($className, false) || trait_exists($className, false)) { | |
yield $fileAbsPath => $className; | |
} | |
} | |
catch (\Throwable $ex) { | |
// Ignore errors, but keep track of them if desired. | |
$this->handleBrokenClass($className, $fileAbsPath, $ex); | |
} | |
} | |
} | |
} | |
public function mapClassNameToFileName(string $className): ?string | |
{ | |
if (str_starts_with($className, $this->namespace)) { | |
$relPath = substr($className, strlen($this->namespace)); | |
if (!preg_match('/^[A-Za-z0-9_\\\\]+$/', $relPath)) { | |
throw new InvalidArgumentException("Invalid class name: " . $className); | |
} | |
return $this->directory . DIRECTORY_SEPARATOR | |
. str_replace('\\', DIRECTORY_SEPARATOR, $relPath) . '.php'; | |
} else { | |
return null; | |
} | |
} | |
public function mapFileNameToClassName(string $fileName): ?string | |
{ | |
if (str_starts_with($fileName, $this->directory)) { | |
$relPath = substr($fileName, strlen($this->directory)); | |
if (!preg_match('/^[A-Za-z0-9_\\/\\\\]+\\.php$/', $relPath)) { | |
throw new InvalidArgumentException("Invalid filename: " . $fileName); | |
} | |
return $this->namespace . trim(str_replace(['.php', '\\', '/'], ['', '\\', '\\'], $relPath), '\\'); | |
} else { | |
return null; | |
} | |
} | |
} |