Shorthand   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 64
dl 0
loc 126
ccs 16
cts 16
cp 1
rs 10
c 4
b 0
f 0
wmc 7

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getConditionClass() 0 3 1
A getActionClass() 0 3 1
A getClass() 0 14 4
A isShorthand() 0 3 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;
13
14
use CaptainHook\App\Hook;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, CaptainHook\App\Runner\Hook. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use RuntimeException;
16
17
/**
18
 * Class Shorthand
19
 *
20
 * Defines some shorthands that can be used in the configuration file to not
21
 * clutter the configuration with the full classnames.
22
 *
23
 * @package CaptainHook
24
 * @author  Sebastian Feldmann <[email protected]>
25
 * @link    https://github.com/captainhook-git/captainhook
26
 * @since   Class available since Release 5.26.0
27
 */
28
class Shorthand
29
{
30
    /**
31
     * Shorthand to action mapping
32
     *
33
     * @var array<string, array<string, array<string, string>>>
34
     */
35
    private static array $map = [
36
        'action' => [
37
            'branch'  => [
38
                'namemustmatchregex'                 => Hook\Branch\Action\EnsureNaming::class,
39
                'preventpushoffixupandsquashcommits' => Hook\Branch\Action\BlockFixupAndSquashCommits::class,
40
            ],
41
            'tools'   => [
42
                'checkcomposerlockfile' => Hook\Composer\Action\CheckLockFile::class,
43
                'phplint'               => Hook\PHP\Action\Linting::class,
44
                'clovertestcoverage'    => Hook\PHP\Action\TestCoverage::class,
45
            ],
46
            'debug'   => [
47
                'fail' => Hook\Debug\Failure::class,
48
                'ok'   => Hook\Debug\Success::class,
49
            ],
50
            'file'    => [
51
                'blocksecrets'             => Hook\Diff\Action\BlockSecrets::class,
52
                'contentmustnotmatchregex' => Hook\File\Action\DoesNotContainRegex::class,
53
                'maxsize'                  => Hook\File\Action\MaxSize::class,
54
                'mustbeempty'              => Hook\File\Action\IsEmpty::class,
55
                'mustnotbeempty'           => Hook\File\Action\IsNotEmpty::class,
56
                'mustexist'                => Hook\File\Action\Exists::class,
57
            ],
58
            'message' => [
59
                'injectissuekeyfrombranch' => Hook\Message\Action\InjectIssueKeyFromBranch::class,
60
                'cacheonfail    '          => Hook\Message\Action\CacheOnFail::class,
61
                'mustfollowrules'          => Hook\Message\Action\Rules::class,
62
                'mustfollowbeamsrules'     => Hook\Message\Action\Beams::class,
63
                'mustmatchregex'           => Hook\Message\Action\Regex::class,
64
                'preparefromfile'          => Hook\Message\Action\PrepareFromFile::class,
65
                'prepare'                  => Hook\Message\Action\Prepare::class,
66
            ],
67
            'notify'  => [
68
                'gitnotify'       => Hook\Notify\Action\Notify::class,
69
                'askconfirmation' => Hook\UserInput\AskConfirmation::class,
70
            ],
71
        ],
72
        'condition' => [
73
            'config'      => [
74
                'customvalueistruthy' => Hook\Condition\Config\CustomValueIsTruthy::class,
75
                'customvalueisfalsy'  => Hook\Condition\Config\CustomValueIsFalsy::class,
76
            ],
77
            'filechanged' => [
78
                'any'         => Hook\Condition\FileChanged\Any::class,
79
                'all'         => Hook\Condition\FileChanged\All::class,
80
                'indirectory' => Hook\Condition\FileChanged\InDirectory::class,
81
                'oftype'      => Hook\Condition\FileChanged\OfType::class,
82
                'thatis'      => Hook\Condition\FileChanged\ThatIs::class,
83
            ],
84
            'filestaged'  => [
85
                'all'         => Hook\Condition\FileStaged\All::class,
86
                'any'         => Hook\Condition\FileStaged\Any::class,
87
                'indirectory' => Hook\Condition\FileStaged\InDirectory::class,
88
                'oftype'      => Hook\Condition\FileStaged\OfType::class,
89
                'thatis'      => Hook\Condition\FileStaged\ThatIs::class,
90
            ],
91
            'status'      => [
92
                'onbranch'            => Hook\Condition\Branch\On::class,
93
                'onmatchingbranch'    => Hook\Condition\Branch\OnMatching::class,
94
                'notonbranch'         => Hook\Condition\Branch\NotOn::class,
95
                'notonmatchingbranch' => Hook\Condition\Branch\NotOnMatching::class,
96
            ]
97
        ]
98
    ];
99
100
    /**
101
     * Check if a configured action or condition value is actually a shorthand for an internal action
102
     *
103
     * @param  string $shorthand
104
     * @return bool
105
     */
106 41
    public static function isShorthand(string $shorthand): bool
107
    {
108 41
        return (bool) preg_match('#^captainhook\.[a-z]+\.[a-z]+$#i', $shorthand);
109
    }
110
111
    /**
112
     * Return the matching action class for given action shorthand
113
     *
114
     * @param  string $shorthand
115
     * @return string
116
     */
117 5
    public static function getActionClass(string $shorthand): string
118
    {
119 5
        return Shorthand::getClass('action', $shorthand);
120
    }
121
122
    /**
123
     * Return the matching condition class for given condition shorthand
124
     *
125
     * @param  string $shorthand
126
     * @return string
127
     */
128 2
    public static function getConditionClass(string $shorthand): string
129
    {
130 2
        return Shorthand::getClass('condition', $shorthand);
131
    }
132
133
    /**
134
     * Returns the matching class for shorthand
135
     *
136
     * @param  string $type
137
     * @param  string $shorthand
138
     * @return string
139
     */
140 7
    private static function getClass(string $type, string $shorthand): string
141
    {
142 7
        $path = explode('.', strtolower($shorthand));
143 7
        if (count($path) !== 3) {
144 1
            throw new RuntimeException('Invalid ' . $type . ' shorthand: ' . $shorthand);
145
        }
146 6
        [$trigger, $group, $name] = $path;
147 6
        if (!isset(self::$map[$type][$group])) {
148 1
            throw new RuntimeException('Invalid ' . $type . ' group: ' . $group);
149
        }
150 5
        if (!isset(self::$map[$type][$group][$name])) {
151 2
            throw new RuntimeException('Invalid ' . $type . ' => ' . $name);
152
        }
153 3
        return self::$map[$type][$group][$name];
154
    }
155
}
156