Completed
Push — master ( 9d2c7e...85264a )
by Jan Philipp
11s
created

ScriptLoader::removeFromStart()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php declare(strict_types=1);
2
3
4
namespace Shopware\Psh\ScriptRuntime;
5
6
use Shopware\Psh\Listing\Script;
7
8
/**
9
 * Load scripts and parse it into commands
10
 */
11
class ScriptLoader
12
{
13
    const MODIFIER_IS_TTY = 'TTY: ';
14
15
    const MODIFIER_IGNORE_ERROR_PREFIX = 'I: ';
16
17
    const INCLUDE_STATEMENT_PREFIX = 'INCLUDE: ';
18
19
    const TEMPLATE_STATEMENT_PREFIX = 'TEMPLATE: ';
20
21
    const CONCATENATE_PREFIX = '   ';
22
23
    /**
24
     * @var CommandBuilder
25
     */
26
    private $commandBuilder;
27
28
    /**
29
     * @param CommandBuilder $commandBuilder
30
     */
31
    public function __construct(CommandBuilder $commandBuilder)
32
    {
33
        $this->commandBuilder = $commandBuilder;
34
    }
35
36
    /**
37
     * @param Script $script
38
     * @return array
39
     */
40
    public function loadScript(Script $script): array
41
    {
42
        $content = $this->loadFileContents($script->getPath());
43
        $lines = explode("\n", $content);
44
45
        foreach ($lines as $lineNumber => $currentLine) {
46
            $ignoreError = false;
47
            $tty = false;
48
49
            if (!$this->isExecutableLine($currentLine)) {
50
                continue;
51
            }
52
53
            if ($this->startsWith(self::CONCATENATE_PREFIX, $currentLine)) {
54
                $this->commandBuilder->add($currentLine);
55
                continue;
56
            }
57
58
            if ($this->startsWith(self::INCLUDE_STATEMENT_PREFIX, $currentLine)) {
59
                $path = $this->findInclude($script, $this->removeFromStart(self::INCLUDE_STATEMENT_PREFIX, $currentLine));
60
                $includeScript = new Script(pathinfo($path, PATHINFO_DIRNAME), pathinfo($path, PATHINFO_BASENAME));
61
62
                $commands = $this->loadScript($includeScript);
63
                $this->commandBuilder->setCommands($commands);
64
65
                continue;
66
            }
67
68
            if ($this->startsWith(self::TEMPLATE_STATEMENT_PREFIX, $currentLine)) {
69
                $definition = $this->removeFromStart(self::TEMPLATE_STATEMENT_PREFIX, $currentLine);
70
                list($rawSource, $rawDestination) = explode(':', $definition);
71
72
                $source = $script->getDirectory() . '/' . $rawSource;
73
                $destination = $script->getDirectory() . '/' . $rawDestination;
74
75
                $this->commandBuilder
76
                    ->addTemplateCommand($source, $destination, $lineNumber);
77
78
                continue;
79
            }
80
81 View Code Duplication
            if ($this->startsWith(self::MODIFIER_IGNORE_ERROR_PREFIX, $currentLine)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
82
                $currentLine = $this->removeFromStart(self::MODIFIER_IGNORE_ERROR_PREFIX, $currentLine);
83
                $ignoreError = true;
84
            }
85
86 View Code Duplication
            if ($this->startsWith(self::MODIFIER_IS_TTY, $currentLine)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
                $currentLine = $this->removeFromStart(self::MODIFIER_IS_TTY, $currentLine);
88
                $tty = true;
89
            }
90
91
            $this->commandBuilder
92
                ->next($currentLine, $lineNumber, $ignoreError, $tty);
93
        }
94
95
        return $this->commandBuilder->getAll();
96
    }
97
98
    /**
99
     * @param Script $fromScript
100
     * @param string $includeStatement
101
     * @return string
102
     */
103
    private function findInclude(Script $fromScript, string $includeStatement): string
104
    {
105
        if (file_exists($includeStatement)) {
106
            return $includeStatement;
107
        }
108
109
        if (file_exists($fromScript->getDirectory() . '/' . $includeStatement)) {
110
            return $fromScript->getDirectory() . '/' . $includeStatement;
111
        }
112
113
        throw new \RuntimeException('Unable to parse include statement "' . $includeStatement . '" in "' . $fromScript->getPath() . '"');
114
    }
115
116
    /**
117
     * @param string $command
118
     * @return bool
119
     */
120
    private function isExecutableLine(string $command): bool
121
    {
122
        if (!strlen(trim($command))) {
123
            return false;
124
        }
125
126
        if ($this->startsWith('#', trim($command))) {
127
            return false;
128
        }
129
130
        return true;
131
    }
132
133
    /**
134
     * @param string $needle
135
     * @param string $haystack
136
     * @return string
137
     */
138
    private function removeFromStart(string $needle, string $haystack): string
139
    {
140
        return substr($haystack, strlen($needle));
141
    }
142
143
    /**
144
     * @param string $needle
145
     * @param string $haystack
146
     * @return bool
147
     */
148
    private function startsWith(string $needle, string $haystack): bool
149
    {
150
        return strpos($haystack, $needle) === 0;
151
    }
152
153
154
    /**
155
     * @param string $file
156
     * @return string
157
     */
158
    protected function loadFileContents(string $file): string
159
    {
160
        $contents = file_get_contents($file);
161
162
        if (false === $contents) {
163
            throw new \RuntimeException('Unable to load config data - read failed in "' . $file . '".');
164
        }
165
166
        return $contents;
167
    }
168
}
169