validateDirectoryCanBeUsed()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 4
nop 1
dl 0
loc 11
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Console\Commands;
6
7
use Hyde\Hyde;
8
use Hyde\Facades\Config;
9
use Hyde\Pages\HtmlPage;
10
use Hyde\Pages\BladePage;
11
use Hyde\Facades\Filesystem;
12
use Hyde\Pages\MarkdownPage;
13
use Hyde\Pages\MarkdownPost;
14
use Hyde\Pages\DocumentationPage;
15
use Hyde\Console\Concerns\Command;
16
use InvalidArgumentException;
17
18
use function array_unique;
19
use function str_contains;
20
use function str_replace;
21
use function basename;
22
use function realpath;
23
24
/**
25
 * Change the source directory for your project.
26
 */
27
class ChangeSourceDirectoryCommand extends Command
28
{
29
    /** @var string */
30
    protected $signature = 'change:sourceDirectory {name : The new source directory name}';
31
32
    /** @var string */
33
    protected $description = 'Change the source directory for your project';
34
35
    protected $hidden = true;
36
37
    public function handle(): int
38
    {
39
        try {
40
            $newDirectoryName = $this->getValidatedName((string) $this->argument('name'));
41
        } catch (InvalidArgumentException $exception) {
42
            $this->error($exception->getMessage());
43
44
            return 409;
45
        }
46
47
        $this->gray(' > Creating directory');
48
        Filesystem::ensureDirectoryExists($newDirectoryName);
49
50
        $this->gray(' > Moving source directories');
51
        foreach ($this->getPageDirectories() as $directory) {
52
            Filesystem::moveDirectory($directory, $this->assembleSubdirectoryPath($newDirectoryName, $directory));
53
        }
54
55
        $this->gray(' > Updating configuration file');
56
        $this->updateConfigurationFile($newDirectoryName, Config::getString('hyde.source_root', ''));
57
58
        // We could also check if there are any more page classes (from packages) and add a note that they may need manual attention
59
60
        $this->info('All done!');
61
62
        return Command::SUCCESS;
63
    }
64
65
    /** @return string[] */
66
    protected function getPageDirectories(): array
67
    {
68
        return array_unique([
69
            HtmlPage::sourceDirectory(),
70
            BladePage::sourceDirectory(),
71
            MarkdownPage::sourceDirectory(),
72
            MarkdownPost::sourceDirectory(),
73
            DocumentationPage::sourceDirectory(),
74
        ]);
75
    }
76
77
    protected function getValidatedName(string $name): string
78
    {
79
        $this->validateName($name);
80
        $this->validateDirectoryCanBeUsed($name);
81
        $this->infoComment("Setting [$name] as the project source directory!");
82
83
        return $name;
84
    }
85
86
    protected function validateName(string $name): void
87
    {
88
        if (realpath(Hyde::path($name)) === realpath(Hyde::path(Config::getString('hyde.source_root', '')))) {
0 ignored issues
show
Bug introduced by
The method path() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

88
        if (realpath(Hyde::/** @scrutinizer ignore-call */ path($name)) === realpath(Hyde::path(Config::getString('hyde.source_root', '')))) {
Loading history...
89
            throw new InvalidArgumentException("The directory '$name' is already set as the project source root!");
90
        }
91
    }
92
93
    protected function validateDirectoryCanBeUsed(string $name): void
94
    {
95
        if (Filesystem::isFile($name)) {
96
            throw new InvalidArgumentException('A file already exists at this path!');
97
        }
98
99
        if (Filesystem::isDirectory($name) && ! Filesystem::isEmptyDirectory($name)) {
100
            // If any of the subdirectories we want to move already exist, we need to abort as we don't want to overwrite any existing files
101
            // The reason we check these individually is mainly so that the change can be reverted (by setting the $name to '/')
102
            foreach ($this->getPageDirectories() as $directory) {
103
                $this->validateSubdirectoryCanBeUsed($this->assembleSubdirectoryPath($name, $directory));
104
            }
105
        }
106
    }
107
108
    protected function validateSubdirectoryCanBeUsed(string $subdirectoryPath): void
109
    {
110
        if (Filesystem::isFile($subdirectoryPath) || $this->directoryContainsFiles($subdirectoryPath)) {
111
            throw new InvalidArgumentException('Directory already exists!');
112
        }
113
    }
114
115
    protected function assembleSubdirectoryPath(string $name, string $subdirectory): string
116
    {
117
        return "$name/".basename($subdirectory);
118
    }
119
120
    protected function directoryContainsFiles(string $subdirectory): bool
121
    {
122
        return Filesystem::isDirectory($subdirectory) && ! Filesystem::isEmptyDirectory($subdirectory);
123
    }
124
125
    protected function updateConfigurationFile(string $newDirectoryName, string $currentDirectoryName): void
126
    {
127
        $search = "'source_root' => '$currentDirectoryName',";
128
129
        $config = Filesystem::getContents('config/hyde.php');
130
        if (str_contains($config, $search)) {
131
            $config = str_replace($search, "'source_root' => '$newDirectoryName',", $config);
132
            Filesystem::putContents('config/hyde.php', $config);
133
        } else {
134
            $this->warn("Warning: Automatic configuration update failed, to finalize the change, please set the `source_root` setting to '$newDirectoryName' in `config/hyde.php`");
135
        }
136
    }
137
}
138