Passed
Push — feature/config-shorthands ( e682c8 )
by Sebastian
05:07
created

Condition::getConditionClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of SebastianFeldmann\Git.
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\Hook\Condition as ConditionInterface;
17
use CaptainHook\App\Hook\Condition\Cli;
18
use CaptainHook\App\Hook\Constrained;
19
use SebastianFeldmann\Cli\Processor\ProcOpen as Processor;
20
use SebastianFeldmann\Git\Repository;
21
use RuntimeException;
22
23
/**
24
 * Condition Runner
25
 *
26
 * Executes a condition of an action by creating a `Condition` object from a condition configuration.
27
 *
28
 * @package CaptainHook
29
 * @author  Sebastian Feldmann <[email protected]>
30
 * @link    https://github.com/captainhook-git/captainhook
31
 * @since   Class available since Release 4.2.0
32
 * @internal
33
 */
34
class Condition
35
{
36
    /**
37
     * @var \CaptainHook\App\Console\IO
38
     */
39
    private IO $io;
40
41
    /**
42
     * @var \SebastianFeldmann\Git\Repository
43
     */
44
    private Repository $repository;
45
46
    /**
47
     * @var \CaptainHook\App\Config
48
     */
49
    private Config $config;
50
51
    /**
52
     * Currently executed hook
53
     *
54
     * @var string
55
     */
56
    private string $hook;
57
58
    /**
59
     * Condition constructor.
60
     *
61
     * @param \CaptainHook\App\Console\IO       $io
62
     * @param \SebastianFeldmann\Git\Repository $repository
63
     * @param \CaptainHook\App\Config           $config
64
     * @param string                            $hook
65
     */
66 26
    public function __construct(IO $io, Repository $repository, Config $config, string $hook)
67
    {
68 26
        $this->io         = $io;
69 26
        $this->repository = $repository;
70 26
        $this->config     = $config;
71 26
        $this->hook       = $hook;
72
    }
73
74
    /**
75
     * Creates the configured condition and evaluates it
76
     *
77
     * @param  \CaptainHook\App\Config\Condition $config
78
     * @return bool
79
     */
80 9
    public function doesConditionApply(Config\Condition $config): bool
81
    {
82 9
        $condition = $this->createCondition($config);
83
        // check all given restrictions
84 8
        if (!$this->isApplicable($condition)) {
85 1
            $this->io->write('Condition skipped due to hook constraint', true, IO::VERBOSE);
86 1
            return true;
87
        }
88 7
        return $condition->isTrue($this->io, $this->repository);
89
    }
90
91
    /**
92
     * Return the configured condition
93
     *
94
     * In case of a cli condition it returns an special condition class that deals with
95
     * the binary execution with implementing the same interface.
96
     *
97
     * @param  \CaptainHook\App\Config\Condition $config
98
     * @return \CaptainHook\App\Hook\Condition
99
     * @throws \RuntimeException
100
     */
101 9
    private function createCondition(Config\Condition $config): ConditionInterface
102
    {
103 9
        if ($this->isLogicCondition($config)) {
104 2
            return $this->createLogicCondition($config);
105
        }
106
107 9
        if (Util::getExecType($config->getExec()) === 'cli') {
108 3
            return new Cli(new Processor(), $config->getExec());
109
        }
110
111
        /** @var class-string<\CaptainHook\App\Hook\Condition> $class */
112 6
        $class = $this->getConditionClass($config->getExec());
113 6
        if (!class_exists($class)) {
114 1
            throw new RuntimeException('could not find condition class: ' . $class);
115
        }
116 5
        $condition = new $class(...$config->getArgs());
117 5
        if ($condition instanceof ConditionInterface\ConfigDependant) {
118 1
            $condition->setConfig($this->config);
119
        }
120 5
        return $condition;
121
    }
122
123
    /**
124
     * Create a logic condition with configures sub conditions
125
     *
126
     * @param  \CaptainHook\App\Config\Condition $config
127
     * @return \CaptainHook\App\Hook\Condition
128
     */
129 2
    private function createLogicCondition(Config\Condition $config): ConditionInterface
130
    {
131 2
        $class      = '\\CaptainHook\\App\\Hook\\Condition\\Logic\\Logic' . ucfirst(strtolower($config->getExec()));
132 2
        $conditions = [];
133 2
        foreach ($config->getArgs() as $conf) {
134 2
            $condition = $this->createCondition(new Config\Condition($conf['exec'], $conf['args'] ?? []));
135 2
            if (!$this->isApplicable($condition)) {
136 1
                $this->io->write('Condition skipped due to hook constraint', true, IO::VERBOSE);
137 1
                continue;
138
            }
139 2
            $conditions[] = $condition;
140
        }
141 2
        return $class::fromConditionsArray($conditions);
142
    }
143
144
    /**
145
     * Make sure the condition can be used during this hook
146
     *
147
     * @param  \CaptainHook\App\Hook\Condition $condition
148
     * @return bool
149
     */
150 8
    private function isApplicable(ConditionInterface $condition): bool
151
    {
152 8
        if ($condition instanceof Constrained) {
153 4
            return $condition->getRestriction()->isApplicableFor($this->hook);
154
        }
155 6
        return true;
156
    }
157
158
    /**
159
     * Is the condition a logic 'AND' or 'OR' condition
160
     *
161
     * @param \CaptainHook\App\Config\Condition $config
162
     * @return bool
163
     */
164 9
    private function isLogicCondition(Config\Condition $config): bool
165
    {
166 9
        return in_array(strtolower($config->getExec()), ['and', 'or']);
167
    }
168
169
    /**
170
     * Returns the condition class
171
     *
172
     * @param string $exec
173
     * @return string
174
     */
175 6
    private function getConditionClass(string $exec): string
176
    {
177 6
        return Shorthand::isShorthand($exec) ? Shorthand::getConditionClass($exec) : $exec;
178
    }
179
}
180