Completed
Branch 09branch (a008bb)
by Anton
03:16
created

Environment::load()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 0
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Core;
10
11
use Spiral\Core\Environment\Parser;
12
use Spiral\Core\Exceptions\EnvironmentException;
13
use Spiral\Files\FilesInterface;
14
15
/**
16
 * Default environment implementation wraps at top of DotEnv package and caches env values into
17
 * application memory. Based on my tests it can speed up application in 1.4-1.9 times.
18
 *
19
 * Attention, this implementation works using global _ENV array.
20
 */
21
class Environment implements EnvironmentInterface
22
{
23
    /**
24
     * Environment section.
25
     */
26
    const MEMORY = 'environment';
27
28
    /**
29
     * Environment filename.
30
     *
31
     * @var string
32
     */
33
    private $filename = '';
34
35
    /**
36
     * @var string
37
     */
38
    private $id = '';
39
40
    /**
41
     * @var array
42
     */
43
    private $values = [];
44
45
    /**
46
     * @invisible
47
     * @var FilesInterface
48
     */
49
    protected $files = null;
50
51
    /**
52
     * @invisible
53
     * @var MemoryInterface
54
     */
55
    protected $memory = null;
56
57
    /**
58
     * @param string          $filename
59
     * @param FilesInterface  $files
60
     * @param MemoryInterface $memory
61
     *
62
     * @throws EnvironmentException
63
     */
64
    public function __construct(string $filename, FilesInterface $files, MemoryInterface $memory)
65
    {
66
        $this->filename = $filename;
67
        $this->files = $files;
68
        $this->memory = $memory;
69
70
        $this->load();
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public function getID(): string
77
    {
78
        return $this->id;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     *
84
     * @return $this
85
     */
86
    public function set(string $name, $value): Environment
87
    {
88
        $this->values[$name] = $_ENV[$name] = $value;
89
        putenv("$name=$value");
90
91
        return $this;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function get(string $name, $default = null)
98
    {
99
        if (array_key_exists($name, $this->values)) {
100
            return $this->values[$name];
101
        }
102
103
        return $default;
104
    }
105
106
    /**
107
     * Fetch environment values from .evn file.
108
     *
109
     * @param string $filename
110
     *
111
     * @return array
112
     */
113
    protected function parseValues($filename)
114
    {
115
        //Extends Dotenv loader
116
        $parser = new Parser($filename);
117
118
        return $parser->parse();
119
    }
120
121
    /**
122
     * Load environment data.
123
     *
124
     * @throws EnvironmentException
125
     */
126
    protected function load()
127
    {
128
        if (!$this->files->exists($this->filename)) {
129
            throw new EnvironmentException("Unable to load environment ({$this->filename})");
130
        }
131
132
        //Unique env file hash
133
        $this->id = $this->files->md5($this->filename);
134
135
        if (!empty($values = $this->memory->loadData(static::MEMORY . '.' . $this->id))) {
136
            //Restore from cache
137
            $this->initEnvironment($values);
0 ignored issues
show
Bug introduced by
It seems like $values defined by $this->memory->loadData(...MORY . '.' . $this->id) on line 135 can also be of type string; however, Spiral\Core\Environment::initEnvironment() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
138
139
            return;
140
        }
141
142
        //Load env values using DotEnv extension
143
        $values = $this->initEnvironment(
144
            $this->parseValues($this->filename)
145
        );
146
147
        $this->memory->saveData(static::MEMORY . '.' . $this->id, $values);
148
    }
149
150
    /**
151
     * Initiate environment values.
152
     *
153
     * @param array $values
154
     *
155
     * @return array
156
     */
157
    protected function initEnvironment(array $values): array
158
    {
159
        foreach ($values as $name => &$value) {
160
            $value = $this->normalize($value);
161
            $this->set($name, $value);
162
            unset($value);
163
        }
164
165
        return $values;
166
    }
167
168
    /**
169
     * Normalize env value.
170
     *
171
     * @param mixed $value
172
     *
173
     * @return bool|null|string
174
     */
175
    private function normalize($value)
176
    {
177
        switch (strtolower($value)) {
178
            case 'true':
179
            case '(true)':
180
                return true;
181
182
            case 'false':
183
            case '(false)':
184
                return false;
185
186
            case 'null':
187
            case '(null)':
188
                return null;
189
190
            case 'empty':
191
            case '(empty)':
192
                return '';
193
        }
194
195
        return $value;
196
    }
197
}