Passed
Push — master ( 5b8585...da319f )
by Petr
07:39
created

KwLoader::classPathToRealFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 10
cc 2
nc 2
nop 2
crap 2
1
<?php
2
3
namespace kalanis\kw_clipr\Loaders;
4
5
6
use kalanis\kw_clipr\Clipr\Useful;
7
use kalanis\kw_clipr\CliprException;
8
use kalanis\kw_clipr\Interfaces;
9
use kalanis\kw_clipr\Tasks\ATask;
10
use ReflectionException;
11
12
13
/**
14
 * Class TaskFactory
15
 * @package kalanis\kw_clipr\Tasks
16
 * Factory for creating tasks/commands from obtained name
17
 * In reality it runs like autoloader of own
18
 */
19
class KwLoader implements Interfaces\ITargetDirs
20
{
21
    /** @var array<string, array<string>> */
22
    protected $paths = [];
23
24
    /**
25
     * @param array<string, array<string>> $paths
26
     * @throws CliprException
27
     */
28 19
    public function __construct(array $paths = [])
29
    {
30 19
        foreach ($paths as $namespace => $path) {
31 17
            $pt = implode(DIRECTORY_SEPARATOR, $path);
32 17
            if (false === $real = realpath($pt)) {
33 1
                throw new CliprException(sprintf('Unknown path *%s*!', $pt), Interfaces\IStatuses::STATUS_BAD_CONFIG);
34
            }
35 16
            $this->paths[$namespace] = explode(DIRECTORY_SEPARATOR, $real);
36
        }
37 18
    }
38
39
    /**
40
     * @param string $classFromParam
41
     * @throws CliprException
42
     * @throws ReflectionException
43
     * @return ATask|null
44
     * For making instances from more than one path
45
     * Now it's possible to read from different paths as namespace sources
46
     * Also each class will be loaded only once
47
     */
48 10
    public function getTask(string $classFromParam): ?ATask
49
    {
50 10
        $classPath = $this->removeExt(Useful::sanitizeClass($classFromParam));
51 10
        foreach ($this->paths as $namespace => $path) {
52 10
            if ($this->containsPath($classPath, $namespace)) {
53 9
                $translatedPath = $this->classPathToRealFile($classPath, $namespace);
54 9
                $realPath = $this->makeRealFilePath($path, $translatedPath);
55 9
                if (is_null($realPath)) {
56 1
                    return null;
57
                }
58 8
                require_once $realPath;
59 8
                if (!class_exists($classPath)) {
60
                    // that file contains none wanted class
61 3
                    return null;
62
                }
63 7
                $reflection = new \ReflectionClass($classPath);
64 7
                if (!$reflection->isInstantiable()) {
65
                    // cannot initialize the class - abstract one, interface, trait, ...
66 3
                    return null;
67
                }
68 6
                $class = $reflection->newInstance();
69 6
                if (!$class instanceof ATask) {
70
                    // the class inside is not an instance of ATask necessary to run
71 3
                    throw new CliprException(sprintf('Class *%s* is not instance of ATask - check interface or query.', $classPath), Interfaces\IStatuses::STATUS_LIB_ERROR);
72
                }
73 5
                return $class;
74
            }
75
        }
76 1
        return null;
77
    }
78
79 10
    protected function containsPath(string $classPath, string $namespace): bool
80
    {
81 10
        return (0 === mb_strpos($classPath, $namespace));
82
    }
83
84 10
    protected function removeExt(string $classPath): string
85
    {
86
        // remove ext
87 10
        $withExt = mb_strripos($classPath, Interfaces\ISources::EXT_PHP);
88 10
        return (false !== $withExt)
89 10
        && (mb_strlen($classPath) == $withExt + mb_strlen(Interfaces\ISources::EXT_PHP))
90 1
            ? mb_substr($classPath, 0, $withExt)
91 10
            : $classPath;
92
    }
93
94 9
    protected function classPathToRealFile(string $classPath, string $namespace): string
95
    {
96
        // change slashes
97 9
        $classNoExt = strtr($classPath, ['\\' => DIRECTORY_SEPARATOR, '/' => DIRECTORY_SEPARATOR, ':' => DIRECTORY_SEPARATOR]);
98
        // remove slash from start
99 9
        $classNoStartSlash = (DIRECTORY_SEPARATOR == $classNoExt[0]) ? mb_substr($classNoExt, mb_strlen(DIRECTORY_SEPARATOR)) : $classNoExt;
100
        // rewrite namespace
101 9
        return mb_substr($classNoStartSlash, mb_strlen($namespace));
102
    }
103
104
    /**
105
     * @param string[] $namespacePath
106
     * @param string $classPath
107
     * @-throws CliprException
108
     * @return string|null
109
     */
110 9
    protected function makeRealFilePath(array $namespacePath, string $classPath): ?string
111
    {
112 9
        $setPath = implode(DIRECTORY_SEPARATOR, $namespacePath) . $classPath . Interfaces\ISources::EXT_PHP;
113 9
        $realPath = realpath($setPath);
114 9
        if (empty($realPath)) {
115 1
            return null;
116
//            throw new CliprException(sprintf('There is problem with path *%s* - it does not exists!', $setPath), Interfaces\IStatuses::STATUS_BAD_CONFIG);
117
        }
118 8
        return $realPath;
119
    }
120
121 5
    public function getPaths(): array
122
    {
123 5
        return $this->paths;
124
    }
125
}
126