Completed
Pull Request — master (#4)
by
unknown
06:08
created

Environment::__get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
declare(strict_types = 1);
3
4
/**
5
 * Micro
6
 *
7
 * @author    Fabian Jucker <[email protected]>
8
 * @copyright Copyright (c) 2017 gyselroth GmbH (https://gyselroth.com)
9
 * @license   MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro\Config;
13
14
use \Micro\Config;
15
use Micro\Config\Environment\EnvironmentNode;
16
17
class Environment implements ConfigInterface
18
{
19
    /**
20
     * DELIMITER
21
     *
22
     * @var string
23
     */
24
    private const DELIMITER = '_';
25
26
    /**
27
     * Store
28
     *
29
     * @var EnvironmentNode
30
     */
31
    private $store;
32
33
    /**
34
     * Environment
35
     *
36
     * @var string
37
     */
38
    private $environment;
39
40
    /**
41
     * Load config
42
     *
43
     * @param  string $config
44
     * @param  string $env
45
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
46
     */
47
    public function __construct(string $config = null, string $env = 'production')
48
    {
49
        $this->environment = $env;
50
        $environmentVars = array_merge($_ENV, $_SERVER);
51
        $this->store = $this->parseEnvironmentVars($environmentVars);
52
    }
53
54
55
    /**
56
     * Get raw format
57
     *
58
     * @return mixed
59
     */
60
    public function getRaw(): array
61
    {
62
        return $this->getRawRecursive([$this->store]);
63
    }
64
65
    /**
66
     * Get from config
67
     *
68
     * @param   EnvironmentNode[] $nodes
69
     * @return  array
70
     */
71
    protected function getRawRecursive(array $nodes): array
72
    {
73
        $raw = [];
74
        foreach ($nodes as $entry) {
75
            if ($entry->value) {
0 ignored issues
show
Documentation introduced by
The property $value is declared private in Micro\Config\Environment\EnvironmentNode. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
76
                $raw[$this->getEnvironmentPrefix() . $entry->getFullKey()] = $entry->value;
0 ignored issues
show
Documentation introduced by
The property $value is declared private in Micro\Config\Environment\EnvironmentNode. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
77
            }
78
            $raw = array_merge($raw, $this->getRawRecursive($entry->children));
0 ignored issues
show
Documentation introduced by
The property $children is declared private in Micro\Config\Environment\EnvironmentNode. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
79
        }
80
        return $raw;
81
    }
82
83
    /**
84
     * Get from config
85
     *
86
     * @param   string $name
87
     * @return  mixed
88
     */
89
    public function __get(string $name)
90
    {
91
        return $this->store->{$name};
92
    }
93
94
95
    /**
96
     * Add config tree and merge it
97
     *
98
     * @param   mixed $config
99
     * @return  ConfigInterface
100
     */
101
    public function merge($config): ConfigInterface
102
    {
103
        if (!$config instanceof Environment) {
104
            throw new Exception('not able to merge objects of other types than ' . static::class);
105
        }
106
        $raw = $this->getRaw();
107
108
        // ensure right env is set on variables
109
        foreach ($config->getRaw() as $key => $value) {
110
            $key = $this->getEnvironmentPrefix() . $config->removeEnvironmentPrefix($key);
111
            $raw[$key] = $value;
112
        }
113
        $this->store = $this->parseEnvironmentVars($raw);
114
        return $this;
115
    }
116
117
118
    /**
119
     * Get native config format as config instance
120
     *
121
     * @return  Config
122
     */
123
    public function map($environmentVariables = null): Config
124
    {
125
        if ($environmentVariables === null) {
126
            $envConfig = $this->store;
127
        } else {
128
            $envConfig = $this->parseEnvironmentVars($environmentVariables);
129
        }
130
131
        $config = new Config();
132
        foreach ($envConfig->children as $entry) {
0 ignored issues
show
Documentation introduced by
The property $children is declared private in Micro\Config\Environment\EnvironmentNode. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
133
            $config[$entry->key] = $entry->toConfig();
0 ignored issues
show
Documentation introduced by
The property $key is declared private in Micro\Config\Environment\EnvironmentNode. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
134
        }
135
        return $config;
136
    }
137
138
    /**
139
     * Filter environment variables for current environment and parse them into a tree
140
     *
141
     * @param   array $environmentVars
142
     * @return  EnvironmentNode
143
     */
144
    protected function parseEnvironmentVars(array $environmentVars)
145
    {
146
        // only parse environment variables with valid names
147
        $environmentVars = array_filter($environmentVars, function ($value, $key) {
148
            return $this->isValidEnvironmentVariable($key);
149
        }, ARRAY_FILTER_USE_BOTH);
150
        // transform to tree
151
        return $this->variablesToTree($environmentVars);
152
    }
153
154
    /**
155
     * Transform array of environment variables into a tree
156
     *
157
     * @param   array $environmentVars
158
     * @return  EnvironmentNode
159
     */
160
    protected function variablesToTree(array $environmentVars)
161
    {
162
        $root = new EnvironmentNode(EnvironmentNode::ROOT_KEY);
163
        foreach ($environmentVars as $name => $value) {
164
            // remove environment prefix from variable name
165
            $name = $this->removeEnvironmentPrefix(strtolower($name));
166
            // split variable name by delimiter
167
            $name = explode(self::DELIMITER, $name);
168
169
            $tree = $root;
170
            foreach ($name as $key) {
171
                if (!$tree->$key) {
172
                    $tree->addChild(new EnvironmentNode($key));
173
                }
174
                $tree = $tree->$key;
175
            }
176
            $tree->setValue($value);
177
        }
178
        return $root;
179
    }
180
181
    /**
182
     * Get prefix for current environment
183
     *
184
     * @return  string
185
     */
186
    protected function getEnvironmentPrefix(): string
187
    {
188
        return $this->environment . self::DELIMITER;
189
    }
190
191
    /**
192
     * Remove current environment prefix from given environment variable name
193
     *
194
     * @param   string $name
195
     * @return  string
196
     */
197
    public function removeEnvironmentPrefix(string $name): string
198
    {
199
        $prefix = $this->getEnvironmentPrefix();
200
        if (substr($name, 0, strlen($prefix)) == $prefix) {
201
            $name = substr($name, strlen($prefix));
202
        }
203
        return $name;
204
    }
205
206
    /**
207
     * Check if a given environment variable name begins with the current environment prefix
208
     *
209
     * @param   string $name
210
     * @return  bool
211
     */
212
    protected function hasEnvironmentPrefix(string $name): bool
213
    {
214
        $prefix = $this->getEnvironmentPrefix();
215
        return (0 === strpos($name, $prefix));
216
    }
217
218
    /**
219
     * Check if a given environment variable name is valid
220
     *
221
     * @param   string $name
222
     * @return  bool
223
     */
224
    protected function isValidEnvironmentVariable(string $name): bool
225
    {
226
        $name = strtolower($name);
227
        // variables are valid if their name has the right prefix and doesn't end with the delimiter
228
        return substr($name, -1) !== self::DELIMITER && $this->hasEnvironmentPrefix($name);
229
    }
230
}
231