Passed
Branch test (2117b7)
by Tom
02:24
created

EnvResolver   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 136
ccs 48
cts 48
cp 1
rs 10
c 0
b 0
f 0
wmc 18

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getValue() 0 5 2
A __construct() 0 3 1
A addArguments() 0 10 3
A addFile() 0 12 3
A resolveString() 0 16 3
A __invoke() 0 7 2
A addDefinition() 0 18 4
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Runner;
6
7
use InvalidArgumentException;
8
use Ktomk\Pipelines\Cli\Args\Args;
9
use Ktomk\Pipelines\Cli\Args\OptionFilterIterator;
10
use UnexpectedValueException;
11
12
/**
13
 * resolve environment variables against docker --env & --env-file arguments
14
 *
15
 * some string values in the bitbucket-pipelines.yml file might need resolution
16
 * against a part of the current host environment but only if set for the
17
 * container as well (--env-file, -e, --env)
18
 *
19
 * @package Ktomk\Pipelines\Runner
20
 */
21
class EnvResolver
22
{
23
24
    /**
25
     * @var array host environment (that exports)
26
     */
27
    private $environment;
28
29
30
    /**
31
     * @var array container environment (partial w/o bitbucket environment)
32
     */
33
    private $variables;
34
35
    /**
36
     * EnvResolver constructor.
37
     * @param array|string[] $environment host environment variables (strings)
38
     */
39 6
    public function __construct(array $environment)
40
    {
41 6
        $this->environment = array_filter($environment, 'is_string');
42 6
    }
43
44 1
    public function addArguments(Args $args)
45
    {
46 1
        $files = new OptionFilterIterator($args, 'env-file');
47 1
        foreach ($files->getArguments() as $file) {
48 1
            $this->addFile($file);
49
        }
50
51 1
        $definitions = new OptionFilterIterator($args, array('e', 'env'));
52 1
        foreach ($definitions->getArguments() as $definition) {
53 1
            $this->addDefinition($definition);
54
        }
55 1
    }
56
57
    /**
58
     * add a file (--env-file option)
59
     *
60
     * @param string $file path to file
61
     */
62 2
    public function addFile($file)
63
    {
64 2
        $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
65 2
        if (false === $lines) {
66 1
            throw new InvalidArgumentException(sprintf(
67 1
                    "File read error: '%s'", $file
68
                )
69
            );
70
        }
71 1
        $definitions = preg_grep('~^(\s*#.*|\s*)$~', $lines, PREG_GREP_INVERT);
72 1
        foreach ($definitions as $definition) {
73 1
            $this->addDefinition($definition);
74
        }
75 1
    }
76
77
    /**
78
     * add a variable definition (-e, --env option)
79
     *
80
     * @param string $definition variable definition, either name only or w/ equal sign
81
     */
82 4
    public function addDefinition($definition)
83
    {
84 4
        $pattern = '~^([^$={}\\x0-\\x20\\x7f-\\xff-]+)(?:=(.*))?$~';
85
86 4
        $result = preg_match($pattern, $definition, $matches);
87 4
        if (0 === $result) {
88 1
            throw new InvalidArgumentException(sprintf(
89 1
                "Variable definition error: '%s'", $definition
90
            ));
91
        }
92
93 3
        list(, $name, $value) = $matches + array(2 => null);
94
95 3
        if (null === $value && isset($this->environment[$name])) {
96 3
            $value = $this->environment[$name];
97
        }
98
99 3
        $this->variables[$name] = $value;
100 3
    }
101
102
    /**
103
     * get value of variable
104
     *
105
     * @param string $name of variable to obtain value from
106
     * @return string|null value, null if unset
107
     */
108 3
    public function getValue($name)
109
    {
110 3
        return isset($this->variables[$name])
111 3
            ? $this->variables[$name]
112 3
            : null;
113
    }
114
115
    /**
116
     * replace variable with its content if it is a portable, Shell and
117
     * Utilities variable name (see POSIX).
118
     *
119
     * zero-length string if the variable is undefined in the resolver
120
     * context.
121
     *
122
     * @param $string
123
     * @return string
124
     */
125 2
    public function resolveString($string)
126
    {
127 2
        $pattern = '~^\$([A-Z_]+[0-9A-Z_])*$~';
128 2
        $result = preg_match($pattern, $string, $matches);
129 2
        if (false === $result) {
0 ignored issues
show
introduced by
The condition false === $result can never be true.
Loading history...
130
            throw new UnexpectedValueException('regex pattern error'); // @codeCoverageIgnore
131
        }
132
133 2
        if (0 === $result) {
134 2
            return $string;
135
        }
136
137 2
        list(, $name) = $matches;
138 2
        $value = $this->getValue($name);
139
140 2
        return (string)$value;
141
    }
142
143
    /**
144
     * resolve a string or an array of strings
145
     *
146
     * @param string|array $stringOrArray
147
     * @return string|array
148
     * @see resolveString
149
     */
150 1
    public function __invoke($stringOrArray)
151
    {
152 1
        if (is_array($stringOrArray)) {
153 1
            return array_map(array($this, 'resolveString'), $stringOrArray);
154
        }
155
156 1
        return $this->resolveString($stringOrArray);
157
    }
158
}
159