Lister::createOutput()   B
last analyzed

Complexity

Conditions 11
Paths 32

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 11

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 21
c 6
b 0
f 0
dl 0
loc 31
ccs 21
cts 21
cp 1
rs 7.3166
cc 11
nc 32
nop 3
crap 11

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace clipr;
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\Output\TPrettyTable;
10
use kalanis\kw_clipr\Tasks\ATask;
11
use kalanis\kw_paths\ArrayPath;
12
use kalanis\kw_paths\PathsException;
13
14
15
/**
16
 * Class Lister
17
 * @package clipr
18
 * @property string $path
19
 */
20
class Lister extends ATask
21
{
22
    use TPrettyTable;
23
24
    protected ArrayPath $arrPt;
25
26 8
    public function __construct()
27
    {
28 8
        $this->arrPt = new ArrayPath();
29 8
    }
30
31 8
    protected function startup(): void
32
    {
33 8
        parent::startup();
34 8
        $this->params->addParam('path', 'path', null, null, null, 'Specify own path to tasks');
35 8
    }
36
37 2
    public function desc(): string
38
    {
39 2
        return 'Render list of tasks available in paths defined for lookup';
40
    }
41
42 8
    public function process(): int
43
    {
44 8
        $this->writeLn('<yellow><bluebg>+====================================+</bluebg></yellow>');
45 8
        $this->writeLn('<yellow><bluebg>|              kw_clipr              |</bluebg></yellow>');
46 8
        $this->writeLn('<yellow><bluebg>+====================================+</bluebg></yellow>');
47 8
        $this->writeLn('<yellow><bluebg>| List all tasks available by lookup |</bluebg></yellow>');
48 8
        $this->writeLn('<yellow><bluebg>+====================================+</bluebg></yellow>');
49
50 8
        if (empty($this->loader)) {
51 1
            $this->sendErrorMessage('Need any loader to get tasks!');
52 1
            return static::STATUS_LIB_ERROR;
53
        }
54
55 7
        $this->setTableHeaders(['Task name', 'Call target', 'Description']);
56 7
        $this->setTableColors(['lgreen', 'lcyan', '']);
57
58
        try {
59 7
            $paths = $this->getPathsFromSubLoaders($this->loader);
60
61 7
            if ($this->path) {
62 4
                if (false === $real = realpath($this->path)) { // from relative to absolute path
63 1
                    throw new CliprException(sprintf('<redbg> !!! </redbg> Path leads to something unreadable. Path: <yellow>%s</yellow>', $this->path), static::STATUS_BAD_CONFIG);
64
                }
65 3
                $this->createOutput($paths, $this->arrPt->setString($real)->getArray());
66
            } else {
67 5
                foreach ($paths as $namespace => $path) {
68 2
                    if (false !== $real = realpath(DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $path))) { // from relative to absolute path
69 2
                        $this->createOutput($paths, $this->arrPt->setString($real)->getArray());
70
                    }
71
                }
72
            }
73 3
        } catch (CliprException | PathsException $ex) {
74 3
            $this->writeLn($ex->getMessage());
75 3
            return $ex->getCode() ?: static::STATUS_ERROR;
76
        }
77 4
        $this->dumpTable();
78 4
        return static::STATUS_SUCCESS;
79
    }
80
81
    /**
82
     * Paths known by internal loaders
83
     * @param Interfaces\ILoader $loader
84
     * @throws CliprException
85
     * @return array<string, array<string>>
86
     */
87 7
    protected function getPathsFromSubLoaders(Interfaces\ILoader $loader): array
88
    {
89 7
        $paths = [];
90 7
        if ($loader instanceof Interfaces\ISubLoaders) {
91 1
            foreach ($loader->getLoaders() as $single) {
92 1
                $paths = array_merge($paths, $this->getPathsFromSubLoaders($single));
93
            }
94
        }
95 7
        if ($loader instanceof Interfaces\ITargetDirs) {
96 4
            $paths = array_merge($paths, $loader->getPaths());
97
        }
98 7
        return $paths;
99
    }
100
101
    /**
102
     * @param array<string, array<string>> $pathsForNamespaces
103
     * @param string[] $currentPath
104
     * @param bool $skipEmpty
105
     * @throws CliprException
106
     * @throws PathsException
107
     */
108 5
    protected function createOutput(array $pathsForNamespaces, array $currentPath, bool $skipEmpty = false): void
109
    {
110 5
        $known = realpath($full = DIRECTORY_SEPARATOR . $this->arrPt->setArray($currentPath)->getString());
111 5
        if (!is_dir($known)) {
112 1
            throw new CliprException(sprintf('<redbg> !!! </redbg> Path leads to something other than directory. Path: <yellow>%s</yellow>', $known), static::STATUS_BAD_CONFIG);
113
        }
114
115 4
        $allFiles = array_diff((array) scandir($known), [false, '', '.', '..']);
116 4
        $files = array_filter($allFiles, [$this, 'onlyPhp']);
117 4
        if (empty($files) && !$skipEmpty) {
118 1
            throw new CliprException(sprintf('<redbg> !!! </redbg> No usable files returned. Path: <yellow>%s</yellow>', $known), static::STATUS_NO_TARGET_RESOURCE);
119
        }
120
121 3
        foreach ($files as $fileName) {
122 3
            $className = $this->realFileToClass($pathsForNamespaces, $currentPath, $fileName);
123 3
            if ($className) {
124
                try {
125 2
                    $task = $this->loader ? $this->loader->getTask($className) : null;
126 2
                } catch (CliprException $ex) {
127 2
                    $task = null;
128
                }
129 2
                if (!$task) {
130 2
                    continue;
131
                }
132 2
                $task->initTask($this->translator, $this->inputs, $this->loader);
133 2
                $this->setTableDataLine([$className, Useful::getTaskCall($task), $task->desc()]);
134
            }
135
        }
136 3
        foreach ($allFiles as $fileName) {
137 3
            if (is_dir($known . DIRECTORY_SEPARATOR . $fileName)) {
138 3
                $this->createOutput($pathsForNamespaces, array_merge($currentPath, [$fileName]), true);
139
            }
140
        }
141 3
    }
142
143
    /**
144
     * @param array<string, array<string>> $availablePaths
145
     * @param string[] $dir
146
     * @param string $file
147
     * @throws PathsException
148
     * @return string|null
149
     */
150 3
    protected function realFileToClass(array $availablePaths, array $dir, string $file): ?string
151
    {
152 3
        $dir = DIRECTORY_SEPARATOR . $this->arrPt->setArray($dir)->getString();
153 3
        $dirLen = mb_strlen($dir);
154 3
        foreach ($availablePaths as $namespace => $path) {
155
            // got some path
156 2
            $pt = implode(DIRECTORY_SEPARATOR, $path);
157 2
            $compLen = min($dirLen, mb_strlen($pt));
158 2
            $lgPath = mb_substr(Useful::mb_str_pad($pt, $compLen, '-'), 0, $compLen);
159 2
            $lgDir = mb_substr(Useful::mb_str_pad($dir, $compLen, '-'), 0, $compLen);
160 2
            if ($lgDir == $lgPath) {
161
                // rewrite namespace
162 2
                $lcDir = DIRECTORY_SEPARATOR == $dir[0] ? $dir : DIRECTORY_SEPARATOR . $dir;
163 2
                $end = $namespace . mb_substr($lcDir, $compLen);
164
                // change slashes
165 2
                $namespaced = DIRECTORY_SEPARATOR == mb_substr($end, -1) ? $end : $end . DIRECTORY_SEPARATOR;
166 2
                $namespaced = strtr($namespaced, DIRECTORY_SEPARATOR, '\\');
167
                // remove ext
168 2
                $withExt = mb_strripos($file, Interfaces\ISources::EXT_PHP);
169 2
                $withoutExt = (false !== $withExt) ? mb_substr($file, 0, $withExt) : $file ;
170
                // return named class
171 2
                return $namespaced . $withoutExt;
172
            }
173
        }
174 1
        return null;
175
    }
176
177 4
    public function onlyPhp(string $filename): bool
178
    {
179 4
        $extPos = mb_strripos($filename, Interfaces\ISources::EXT_PHP);
180 4
        return mb_substr($filename, 0, $extPos) . Interfaces\ISources::EXT_PHP == $filename; // something more than ext - and die!
181
    }
182
}
183