Completed
Pull Request — master (#82)
by Jan Philipp
02:07
created

PshScriptParser::createTokenHandler()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 60
rs 8.8727
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

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 declare(strict_types=1);
2
3
4
namespace Shopware\Psh\ScriptRuntime\ScriptLoader;
5
6
use Shopware\Psh\Listing\Script;
7
8
/**
9
 * Load scripts and parse it into commands
10
 */
11
class PshScriptParser implements ScriptParser
12
{
13
    const TOKEN_MODIFIER_TTY = 'TTY: ';
14
15
    const TOKEN_MODIFIER_IGNORE_ERROR = 'I: ';
16
17
    const TOKEN_MODIFIER_DEFERRED = 'D: ';
18
19
    const TOKEN_INCLUDE = 'INCLUDE: ';
20
21
    const TOKEN_WAIT = 'WAIT:';
22
23
    const TOKEN_TEMPLATE = 'TEMPLATE: ';
24
25
    const CONCATENATE_PREFIX = '   ';
26
27
    const TOKEN_WILDCARD = '*';
28
29
    /**
30
     * @var CommandBuilder
31
     */
32
    private $commandBuilder;
33
34
    /**
35
     * @param CommandBuilder $commandBuilder
36
     */
37
    public function __construct(CommandBuilder $commandBuilder)
38
    {
39
        $this->commandBuilder = $commandBuilder;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function parseContent(string $content, Script $script): array
46
    {
47
        $lines = $this->splitIntoLines($content);
48
        $tokenHandler = $this->createTokenHandler();
49
50
        foreach ($lines as $lineNumber => $currentLine) {
51
            foreach ($tokenHandler as $token => $handler) {
52
                if ($this->startsWith($token, $currentLine)) {
53
                    $currentLine = $handler($currentLine, $lineNumber, $script);
54
                }
55
56
                if ($currentLine === '') {
57
                    break;
58
                }
59
            }
60
        }
61
62
        return $this->commandBuilder->getAll();
63
    }
64
65
    private function createTokenHandler(): array
66
    {
67
        return [
68
            self::TOKEN_INCLUDE => function (string $currentLine, int $lineNumber, Script $script): string {
69
                $path = $this->findInclude($script, $this->removeFromStart(self::TOKEN_INCLUDE, $currentLine));
70
                $includeScript = new Script(pathinfo($path, PATHINFO_DIRNAME), pathinfo($path, PATHINFO_BASENAME));
71
72
                $commands = $this->parseContent($this->loadFileContents($includeScript->getPath()), $includeScript);
73
                $this->commandBuilder->replaceCommands($commands);
74
75
                return '';
76
            },
77
78
            self::TOKEN_TEMPLATE => function (string $currentLine, int $lineNumber, Script $script): string {
79
                $definition = $this->removeFromStart(self::TOKEN_TEMPLATE, $currentLine);
80
                list($rawSource, $rawDestination) = explode(':', $definition);
81
82
                $source = $script->getDirectory() . '/' . $rawSource;
83
                $destination = $script->getDirectory() . '/' . $rawDestination;
84
85
                $this->commandBuilder
86
                    ->addTemplateCommand($source, $destination, $lineNumber);
87
88
                return '';
89
            },
90
91
            self::TOKEN_WAIT => function (string $currentLine, int $lineNumber): string {
92
                $this->commandBuilder
93
                    ->addWaitCommand($lineNumber);
94
95
96
                return '';
97
            },
98
99
            self::TOKEN_MODIFIER_IGNORE_ERROR => function (string $currentLine): string {
100
                $this->commandBuilder->setIgnoreError();
101
102
                return $this->removeFromStart(self::TOKEN_MODIFIER_IGNORE_ERROR, $currentLine);
103
            },
104
105
            self::TOKEN_MODIFIER_TTY => function (string $currentLine): string {
106
                $this->commandBuilder->setTty();
107
108
                return  $this->removeFromStart(self::TOKEN_MODIFIER_TTY, $currentLine);
109
            },
110
111
            self::TOKEN_MODIFIER_DEFERRED => function (string $currentLine): string {
112
                $this->commandBuilder->setDeferredExecution();
113
114
                return $this->removeFromStart(self::TOKEN_MODIFIER_DEFERRED, $currentLine);
115
            },
116
117
            self::TOKEN_WILDCARD => function (string $currentLine, int $lineNumber): string {
118
                $this->commandBuilder
119
                    ->addProcessCommand($currentLine, $lineNumber);
120
121
                return '';
122
            },
123
        ];
124
    }
125
126
    /**
127
     * @param Script $fromScript
128
     * @param string $includeStatement
129
     * @return string
130
     */
131
    private function findInclude(Script $fromScript, string $includeStatement): string
132
    {
133
        if (file_exists($includeStatement)) {
134
            return $includeStatement;
135
        }
136
137
        if (file_exists($fromScript->getDirectory() . '/' . $includeStatement)) {
138
            return $fromScript->getDirectory() . '/' . $includeStatement;
139
        }
140
141
        throw new \RuntimeException('Unable to parse include statement "' . $includeStatement . '" in "' . $fromScript->getPath() . '"');
142
    }
143
144
    /**
145
     * @param string $command
146
     * @return bool
147
     */
148
    private function isExecutableLine(string $command): bool
149
    {
150
        $command = trim($command);
151
152
        if (!$command) {
153
            return false;
154
        }
155
156
        if ($this->startsWith('#', $command)) {
157
            return false;
158
        }
159
160
        return true;
161
    }
162
163
    /**
164
     * @param string $needle
165
     * @param string $haystack
166
     * @return string
167
     */
168
    private function removeFromStart(string $needle, string $haystack): string
169
    {
170
        return substr($haystack, strlen($needle));
171
    }
172
173
    /**
174
     * @param string $needle
175
     * @param string $haystack
176
     * @return bool
177
     */
178
    private function startsWith(string $needle, string $haystack): bool
179
    {
180
        return (self::TOKEN_WILDCARD === $needle && $haystack !== '') || strpos($haystack, $needle) === 0;
181
    }
182
183
    /**
184
     * @param string $file
185
     * @return string
186
     */
187
    protected function loadFileContents(string $file): string
188
    {
189
        return file_get_contents($file);
190
    }
191
192
    /**
193
     * @param string $contents
194
     * @return string[]
195
     */
196
    private function splitIntoLines(string $contents): array
197
    {
198
        $lines = [];
199
        $lineNumber = -1;
200
201
        foreach (explode("\n", $contents) as $line) {
202
            $lineNumber++;
203
204
            if (!$this->isExecutableLine($line)) {
205
                continue;
206
            }
207
208
            if ($this->startsWith(self::CONCATENATE_PREFIX, $line)) {
209
                $lastValue = array_pop($lines);
210
                $lines[] = $lastValue  . ' ' . trim($line);
211
212
                continue;
213
            }
214
215
            $lines[$lineNumber] = $line;
216
        }
217
218
        return $lines;
219
    }
220
}
221