Passed
Push — master ( 7c61fd...4bc778 )
by Sebastian
02:04
created

Formatter::computedPlaceholder()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 17
rs 9.9666
cc 3
nc 3
nop 1
1
<?php
2
3
/**
4
 * This file is part of CaptainHook.
5
 *
6
 * (c) Sebastian Feldmann <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CaptainHook\App\Runner\Action\Cli\Command;
13
14
use SebastianFeldmann\Git\Repository;
15
16
/**
17
 * Class Formatter
18
 *
19
 * @package CaptainHook
20
 * @author  Sebastian Feldmann <[email protected]>
21
 * @link    https://github.com/captainhookphp/captainhook
22
 * @since   Class available since Release 5.0.0
23
 */
24
class Formatter
25
{
26
    /**
27
     * Cache storage for computed placeholder values
28
     *
29
     * @var array<string, string>
30
     */
31
    private static $cache = [];
32
33
    /**
34
     * List of available placeholders
35
     *
36
     * @var array<string, string>
37
     */
38
    private static $placeholders = [
39
        'staged_files' => '\\CaptainHook\\App\\Runner\\Action\\Cli\\Command\\Placeholder\\StagedFiles'
40
    ];
41
42
    /**
43
     * Git repository
44
     *
45
     * @var \SebastianFeldmann\Git\Repository
46
     */
47
    private $repository;
48
49
    /**
50
     * Original hook arguments
51
     *
52
     * @var array<string, string>
53
     */
54
    private $arguments;
55
56
    /**
57
     * Formatter constructor
58
     *
59
     * @param \SebastianFeldmann\Git\Repository $repository
60
     * @param array<string, string>             $arguments
61
     */
62
    public function __construct(Repository $repository, array $arguments)
63
    {
64
        $this->repository = $repository;
65
        $this->arguments  = $arguments;
66
    }
67
68
    /**
69
     * Replaces all placeholders in a cli command
70
     *
71
     * @param  string $command
72
     * @return string
73
     */
74
    public function format(string $command): string
75
    {
76
        // find all replacements {SOMETHING}
77
        $placeholders = $this->findAllPlaceholders($command);
78
        foreach ($placeholders as $placeholder) {
79
            $command = str_replace('{$' . $placeholder . '}', $this->replace($placeholder), $command);
80
        }
81
82
        return $command;
83
    }
84
85
    /**
86
     * Returns al list of all placeholders
87
     *
88
     * @param  string $command
89
     * @return array<int, string>
90
     */
91
    private function findAllPlaceholders(string $command): array
92
    {
93
        $placeholders = [];
94
        $matches      = [];
95
96
        if (preg_match_all('#{\$([a-z_]+(\|[a-z\-]+:.*)?)}#i', $command, $matches)) {
97
            foreach ($matches[1] as $match) {
98
                $placeholders[] = $match;
99
            }
100
        }
101
102
        return $placeholders;
103
    }
104
105
    /**
106
     * Return a given placeholder value
107
     *
108
     * @param  string $placeholder
109
     * @return string
110
     */
111
    private function replace(string $placeholder): string
112
    {
113
        // if placeholder references an original hook argument return the argument
114
        // otherwise compute the placeholder
115
        return $this->arguments[strtolower($placeholder)] ?? $this->computedPlaceholder($placeholder);
116
    }
117
118
    /**
119
     * Compute the placeholder value
120
     *
121
     * @param  string $rawPlaceholder Placeholder syntax {$NAME[|OPTION:VALUE]...}
122
     * @return string
123
     */
124
    private function computedPlaceholder(string $rawPlaceholder): string
125
    {
126
        // to not compute the same placeholder multiple times
127
        if (!$this->isCached($rawPlaceholder)) {
128
            // extract placeholder name and options
129
            $parts       = explode('|', $rawPlaceholder);
130
            $placeholder = strtolower($parts[0]);
131
            $options     = $this->parseOptions(array_slice($parts, 1));
132
133
            if (!$this->isPlaceholderValid($placeholder)) {
134
                return '';
135
            }
136
137
            $processor = $this->createPlaceholder($placeholder);
138
            $this->cache($rawPlaceholder, $processor->replacement($options));
139
        }
140
        return $this->cached($rawPlaceholder);
141
    }
142
143
    /**
144
     * Placeholder factory method
145
     *
146
     * @param  string $placeholder
147
     * @return \CaptainHook\App\Runner\Action\Cli\Command\Placeholder
148
     */
149
    private function createPlaceholder(string $placeholder): Placeholder
150
    {
151
        $class = self::$placeholders[$placeholder];
152
        return new $class($this->repository);
153
    }
154
155
    /**
156
     * Checks if a placeholder is available for computation
157
     *
158
     * @param  string $placeholder
159
     * @return bool
160
     */
161
    private function isPlaceholderValid(string $placeholder): bool
162
    {
163
        return isset(self::$placeholders[$placeholder]);
164
    }
165
166
    /**
167
     * Parse options from ["name:'value'", "name:'value'"] to ["name" => "value", "name" => "value"]
168
     *
169
     * @param  array $raw
170
     * @return array
171
     */
172
    private function parseOptions(array $raw): array
173
    {
174
        $options = [];
175
        foreach ($raw as $rawOption) {
176
            $matches = [];
177
            if (preg_match('#^([a-z_\-]+):(.*)?$#i', $rawOption, $matches)) {
178
                $options[strtolower($matches[1])] = $matches[2];
179
            }
180
        }
181
        return $options;
182
    }
183
184
    /**
185
     * Check if a placeholder is cached already
186
     *
187
     * @param  string $placeholder
188
     * @return bool
189
     */
190
    private static function isCached(string $placeholder): bool
191
    {
192
        return isset(self::$cache[$placeholder]);
193
    }
194
195
    /**
196
     * Cache a given placeholder value
197
     *
198
     * @param string $placeholder
199
     * @param string $replacement
200
     */
201
    private static function cache(string $placeholder, string $replacement): void
202
    {
203
        self::$cache[$placeholder] = $replacement;
204
    }
205
206
    /**
207
     * Return cached value for given placeholder
208
     *
209
     * @param  string $placeholder
210
     * @return string
211
     */
212
    private static function cached(string $placeholder): string
213
    {
214
        return self::$cache[$placeholder] ?? '';
215
    }
216
}
217