Passed
Push — master ( 69774c...de7969 )
by Sebastian
02:46
created

Hook::beforeHook()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 0
c 0
b 0
f 0
dl 0
loc 2
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
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;
13
14
use CaptainHook\App\Config;
15
use CaptainHook\App\Console\IO;
16
use CaptainHook\App\Console\IOUtil;
17
use CaptainHook\App\Hooks;
18
use RuntimeException;
19
20
/**
21
 * Hook
22
 *
23
 * @package CaptainHook
24
 * @author  Sebastian Feldmann <[email protected]>
25
 * @link    https://github.com/captainhookphp/captainhook
26
 * @since   Class available since Release 0.9.0
27
 */
28
abstract class Hook extends RepositoryAware
29
{
30
    /**
31
     * Hook that should be handled.
32
     *
33
     * @var string
34
     */
35
    protected $hook;
36
37
    /**
38
     * Execute stuff before executing any actions
39
     *
40
     * @return void
41 9
     */
42
    public function beforeHook(): void
43
    {
44 9
        // empty template method
45
    }
46
47
    /**
48
     * Execute stuff before every actions
49
     *
50
     * @return void
51 1
     */
52
    public function beforeAction(): void
53
    {
54 1
        // empty template method
55
    }
56
57
    /**
58
     * Execute stuff after every actions
59
     *
60
     * @return void
61 1
     */
62
    public function afterAction(): void
63
    {
64 1
        //empty template method
65
    }
66
67
    /**
68
     * Execute stuff after all actions
69
     *
70
     * @return void
71 9
     */
72
    public function afterHook(): void
73
    {
74 9
        // empty template method
75
    }
76
77
    /**
78
     * Execute the hook and all its actions
79
     *
80
     * @return void
81
     * @throws \Exception
82 23
     */
83
    public function run(): void
84
    {
85 23
        $hookConfig = $this->config->getHookConfig($this->hook);
86 23
87
        // if hook is not enabled in captainhook configuration skip the execution
88
        if (!$hookConfig->isEnabled()) {
89 23
            $this->io->write($this->formatHookHeadline('Skip'), true, IO::VERBOSE);
90 8
            return;
91 8
        }
92
93
        $this->io->write($this->formatHookHeadline('Execute'), true, IO::VERBOSE);
94 15
95 4
        $actions = $this->getActionsToExecute($hookConfig);
96 4
97
        // if no actions are configured do nothing
98
        if (count($actions) === 0) {
99 11
            $this->io->write(['', '<info>No actions to execute</info>'], true, IO::VERBOSE);
100 11
            return;
101 9
        }
102 9
        $this->beforeHook();
103
        foreach ($actions as $action) {
104 9
            $this->handleAction($action);
105 9
        }
106
        $this->afterHook();
107
    }
108
109
    /**
110
     * Return all the actions to execute
111
     *
112
     * Returns all actions from the triggered hook but also any actions of virtual hooks that might be triggered.
113
     * E.g. 'post-rewrite' or 'post-checkout' trigger the virtual/artificial 'post-change' hook.
114 9
     * Virtual hooks are special hooks to simplify configuration.
115
     *
116 9
     * @param  \CaptainHook\App\Config\Hook $hookConfig
117
     * @return \CaptainHook\App\Config\Action[]
118 9
     */
119 1
    private function getActionsToExecute(Config\Hook $hookConfig)
120 1
    {
121 1
        $actions = $hookConfig->getActions();
122 1
        if (!Hooks::triggersVirtualHook($hookConfig->getName())) {
123
            return $actions;
124 1
        }
125
126
        $virtualHookConfig = $this->config->getHookConfig(Hooks::getVirtualHook($hookConfig->getName()));
127 8
        if (!$virtualHookConfig->isEnabled()) {
128 8
            return $actions;
129 8
        }
130
        return array_merge($actions, $virtualHookConfig->getActions());
131
    }
132
133
    /**
134
     * Executes a configured hook action
135
     *
136
     * @param  \CaptainHook\App\Config\Action $action
137
     * @return void
138 1
     * @throws \Exception
139
     */
140 1
    protected function handleAction(Config\Action $action): void
141 1
    {
142 1
        $this->io->write(['', 'Action: <comment>' . $action->getAction() . '</comment>'], true, IO::VERBOSE);
143 1
144 1
        if (!$this->doConditionsApply($action->getConditions())) {
145
            $this->io->write(
146
                ['', 'Skipped due to failing conditions'],
147
                true,
148
                IO::VERBOSE
149
            );
150
            return;
151
        }
152
153 7
        $execMethod = self::getExecMethod(Util::getExecType($action->getAction()));
154
        $this->{$execMethod}($action);
155
    }
156
157
    /**
158 7
     * Execute a php hook action
159 7
     *
160 7
     * @param  \CaptainHook\App\Config\Action $action
161
     * @return void
162
     * @throws \CaptainHook\App\Exception\ActionFailed
163
     */
164
    protected function executePhpAction(Config\Action $action): void
165
    {
166
        $this->beforeAction();
167
        $runner = new Action\PHP($this->hook);
168 10
        $runner->execute($this->config, $this->io, $this->repository, $action);
169
        $this->afterAction();
170 10
    }
171
172 10
    /**
173 1
     * Execute a cli hook action
174
     *
175 9
     * @param  \CaptainHook\App\Config\Action $action
176
     * @return void
177
     * @throws \CaptainHook\App\Exception\ActionFailed
178
     */
179
    protected function executeCliAction(Config\Action $action): void
180
    {
181
        // since the cli has no straight way to communicate back to php
182
        // cli hooks have to handle sync stuff by them self
183
        // so no 'beforeAction' or 'afterAction' is called here
184 9
        $runner = new Action\Cli();
185
        $runner->execute($this->io, $this->repository, $action);
186 9
    }
187 9
188 2
    /**
189 2
     * Return the right method name to execute an action
190
     *
191
     * @param  string $type
192 8
     * @return string
193
     */
194
    public static function getExecMethod(string $type): string
195
    {
196
        $valid = ['php' => 'executePhpAction', 'cli' => 'executeCliAction'];
197
198
        if (!isset($valid[$type])) {
199
            throw new RuntimeException('invalid action type: ' . $type);
200
        }
201 19
        return $valid[$type];
202
    }
203 19
204
    /**
205 19
     * Check if conditions apply
206 19
     *
207 19
     * @param  \CaptainHook\App\Config\Condition[] $conditions
208 19
     * @return bool
209
     */
210
    private function doConditionsApply(array $conditions): bool
211
    {
212
        $conditionRunner = new Condition($this->io, $this->repository, $this->hook);
213
        foreach ($conditions as $config) {
214
            if (!$conditionRunner->doesConditionApply($config)) {
215
                return false;
216
            }
217
        }
218
        return true;
219
    }
220
221
    /**
222
     * Some fancy output formatting
223
     *
224
     * @param  string $mode
225
     * @return string[]
226
     */
227
    private function formatHookHeadline(string $mode): array
228
    {
229
        $headline = ' ' . $mode . ' hook: <comment>' . $this->hook . '</comment> ';
230
        return [
231
            '',
232
            IOUtil::getLineSeparator(8) .
233
            $headline .
234
            IOUtil::getLineSeparator(80 - 8 - strlen(strip_tags($headline)))
235
        ];
236
    }
237
}
238