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

PHP::execute()   A

Complexity

Conditions 5
Paths 15

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 16
c 2
b 0
f 0
dl 0
loc 28
ccs 9
cts 9
cp 1
rs 9.4222
cc 5
nc 15
nop 4
crap 5
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;
13
14
use CaptainHook\App\Config;
15
use CaptainHook\App\Console\IO;
16
use CaptainHook\App\Exception\ActionFailed;
17
use CaptainHook\App\Hook\Action;
18
use CaptainHook\App\Hook\Constrained;
19
use Exception;
20
use RuntimeException;
21
use SebastianFeldmann\Git\Repository;
22
23
/**
24
 * Class PHP
25
 *
26
 * @package CaptainHook
27
 * @author  Sebastian Feldmann <[email protected]>
28
 * @link    https://github.com/captainhookphp/captainhook
29
 * @since   Class available since Release 0.9.0
30
 */
31
class PHP
32
{
33
    /**
34
     * Name of the currently executed hook
35
     *
36
     * @var string
37
     */
38
    private $hook;
39 8
40
    /**
41 8
     * PHP constructor.
42
     *
43
     * @param string $hook Name of the currently executed hook
44 8
     */
45 3
    public function __construct(string $hook)
46 1
    {
47
        $this->hook = $hook;
48
    }
49 5
50 4
    /**
51 5
     * Execute the configured action
52 4
     *
53 4
     * @param  \CaptainHook\App\Config           $config
54 4
     * @param  \CaptainHook\App\Console\IO       $io
55
     * @param  \SebastianFeldmann\Git\Repository $repository
56 1
     * @param  \CaptainHook\App\Config\Action    $action
57 1
     * @return void
58
     * @throws \CaptainHook\App\Exception\ActionFailed
59 2
     */
60
    public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void
61
    {
62
        $class = $action->getAction();
63
64
        try {
65
            // if the configured action is a static php method display the captured output and exit
66
            if ($this->isStaticMethodCall($class)) {
67 3
                $io->write($this->executeStatic($class));
68
                return;
69 3
            }
70 3
71 1
            // if not static it has to be an 'Action' so let's instantiate
72
            $exe = $this->createAction($class);
73 2
            // check for any given restrictions
74 1
            if (!$this->isApplicable($exe)) {
75
                $io->write('Action skipped due to hook constraint', true, IO::VERBOSE);
76 1
                return;
77 1
            }
78 1
79
            // no restrictions run it!
80
            $exe->execute($config, $io, $repository, $action);
81
        } catch (Exception $e) {
82
            throw new ActionFailed(
83
                'Execution failed: ' . PHP_EOL .
84
                $e->getMessage() . ' in ' . $e->getFile() . ' line ' . $e->getLine()
85
            );
86
        } catch (\Error $e) {
87
            throw new ActionFailed('PHP Error:' . $e->getMessage());
88 5
        }
89
    }
90 5
91 5
    /**
92 1
     * Execute static method call and return its output
93 1
     *
94
     * @param  string $class
95
     * @return string
96 4
     */
97
    private function executeStatic(string $class): string
98
    {
99
        [$class, $method] = explode('::', $class);
100
        if (!class_exists($class)) {
101
            throw new RuntimeException('could not find class: ' . $class);
102
        }
103
        if (!method_exists($class, $method)) {
104
            throw new RuntimeException('could not find method in class: ' . $method);
105 8
        }
106
        ob_start();
107 8
        $class::$method();
108
        return (string)ob_get_clean();
109
    }
110
111
    /**
112
     * Create an action instance
113
     *
114
     * @param  string $class
115
     * @return \CaptainHook\App\Hook\Action
116
     * @throws \CaptainHook\App\Exception\ActionFailed
117
     */
118
    private function createAction(string $class): Action
119
    {
120
        $action = new $class();
121
        if (!$action instanceof Action) {
122
            throw new ActionFailed(
123
                'PHP class ' . $class . ' has to implement the \'Action\' interface'
124
            );
125
        }
126
        return $action;
127
    }
128
129
    /**
130
     * Is this a static method call
131
     *
132
     * @param  string $class
133
     * @return bool
134
     */
135
    private function isStaticMethodCall(string $class): bool
136
    {
137
        return (bool)preg_match('#^\\\\.+::.+$#i', $class);
138
    }
139
140
    /**
141
     * Make sure the action can be used during this hook
142
     *
143
     * @param  \CaptainHook\App\Hook\Action $action
144
     * @return bool
145
     */
146
    private function isApplicable(Action $action)
147
    {
148
        if ($action instanceof Constrained) {
149
            /** @var \CaptainHook\App\Hook\Constrained $action */
150
            return $action->getRestriction()->isApplicableFor($this->hook);
151
        }
152
        return true;
153
    }
154
}
155