ClassFileDetails::parseNamespace()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 8
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\Infrastructure\Console\ConsoleCommandLoader;
13
14
use Slick\WebStack\Infrastructure\Exception\InvalidCommandImplementation;
15
use SplFileInfo;
16
use Symfony\Component\Console\Command\Command;
17
18
/**
19
 * ClassFileDetails
20
 *
21
 * @package Slick\WebStack\Infrastructure\Console\ConsoleCommandLoader
22
 */
23
final class ClassFileDetails
24
{
25
26
    private ?string $namespace = null;
27
28
    /** @var class-string|null  */
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
29
    private ?string $className = null;
30
31
    private ?string $parentClass= null;
32
33
    /**
34
     * Constructs a ClassFileDetails
35
     *
36
     * @param SplFileInfo $fileInfo The SplFileInfo object representing the file to be parsed.
37
     */
38
    public function __construct(SplFileInfo $fileInfo)
39
    {
40
        $contents = file_get_contents($fileInfo->getRealPath());
41
        if ($contents !== false) {
42
            $this->parseFile($contents);
43
        }
44
    }
45
46
    /**
47
     * Get the FQ class name.
48
     *
49
     * @return class-string|null The class name if it exists, otherwise null.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
50
     */
51
    public function className(): ?string
52
    {
53
        return $this->className;
54
    }
55
56
    /**
57
     * Checks if the class is a Command class.
58
     *
59
     * @return bool Returns true if the class is a Command class, false otherwise.
60
     */
61
    public function isCommand(): bool
62
    {
63
        return $this->parentClass === Command::class;
64
    }
65
66
    /**
67
     * Returns the command name
68
     *
69
     * @return string The command name
70
     * @throws InvalidCommandImplementation If the class does not extend Command or if the command
71
     *                                      name cannot be determined.
72
     */
73
    public function commandName(): string
74
    {
75
        if (!$this->isCommand()) {
76
            throw new InvalidCommandImplementation(
77
                "Class does not extend Command. Please extend the command class from ".Command::class
78
            );
79
        }
80
81
        $callable = [$this->className, 'getDefaultName'];
82
        $name = !is_callable($callable) ? null : $callable();
83
        if (!is_string($name)) {
84
            throw new InvalidCommandImplementation(
85
                "Could not determine the command name. Did you add the #[AsCommand] attribute?]"
86
            );
87
        }
88
        return $name;
89
    }
90
91
    /**
92
     * Parses the content of a file to extract class details.
93
     *
94
     * @param string $content The content of the file to be parsed.
95
     *
96
     * @return void
97
     */
98
    private function parseFile(string $content): void
99
    {
100
        $regex = '/class (?<name>\w+)?(\sextends\s(?<parent>\w+))?/i';
101
        $success = preg_match($regex, $content, $matches);
102
        if ($success === false || !isset($matches['name'])) {
103
            return;
104
        }
105
106
        $this->parseNamespace($content);
107
        $name = trim($matches['name']);
108
        /** @var class-string $fullClassName */
109
        $fullClassName = "$this->namespace\\$name";
110
        $this->className = $fullClassName;
111
112
        if (!isset($matches['parent'])) {
113
            return;
114
        }
115
        $this->parseParent(trim($matches['parent']), $content);
116
    }
117
118
    /**
119
     * Parses the namespace from the given content.
120
     *
121
     * @param string $content The content to be parsed.
122
     * @return void
123
     */
124
    private function parseNamespace(string $content): void
125
    {
126
        $namespaceRegEx = '/namespace(?<namespace>(.*));/i';
127
        if (!preg_match($namespaceRegEx, $content, $found)) {
128
            return;
129
        }
130
131
        $this->namespace = trim($found['namespace']);
132
    }
133
134
    /**
135
     * Parses the parent class in the content and stores it in the property $parentClass.
136
     *
137
     * @param string $parent The name of the parent class to search for.
138
     * @param string $content The content to search for the parent class in.
139
     * @return void
140
     */
141
    private function parseParent(string $parent, string $content): void
142
    {
143
        $usesRegex = '/use(?<uses>(.*));/i';
144
        preg_match_all($usesRegex, $content, $matches);
145
        $uses = empty($matches['uses']) ? [] : $matches['uses'];
146
147
        foreach ($uses as $use) {
148
            if (str_ends_with(trim($use), "\\$parent")) {
149
                $this->parentClass = trim($use);
150
            }
151
        }
152
    }
153
}
154