Passed
Push — master ( c226ed...406fbf )
by Hong
03:47 queued 01:43
created

Environment::parseString()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
c 0
b 0
f 0
dl 0
loc 26
rs 9.6
cc 4
nc 2
nop 1
1
<?php
2
/**
3
 * Phoole (PHP7.2+)
4
 *
5
 * @category  Library
6
 * @package   Phoole\Env
7
 * @copyright Copyright (c) 2019 Hong Zhang
8
 */
9
declare(strict_types=1);
10
11
namespace Phoole\Env;
12
13
use Phoole\Base\Reference\{ReferenceTrait, ReferenceInterface};
14
15
/**
16
 * Load environment key/value pairs from certain path.
17
 *
18
 * @package Phoole\Env
19
 */
20
class Environment implements ReferenceInterface
21
{
22
    use ReferenceTrait;
23
24
    /**
25
     * Load environment variables from a .env file
26
     *
27
     * @param  string $path          full path of the .env file
28
     * @param  bool   $overwrite     overwrite existing values
29
     * @throws \RuntimeException     if $path not readable
30
     * @return object $this          able to chain
31
     */
32
    public function load(string $path, bool $overwrite = false): object
33
    {
34
        return $this->parse($this->loadPath($path), $overwrite);
35
    }
36
37
    /**
38
     * Parse an array to set environment variables
39
     *
40
     * @param  array  $arr          full path of the .env file
41
     * @param  bool   $overwrite    overwrite existing env values
42
     * @return object $this         able to chain
43
     */
44
    public function parse(array $arr, bool $overwrite = false): object
45
    {
46
        foreach ($arr as $key => $val) {
47
            $this->setEnv($key, $this->deReferenceString($val), $overwrite);
48
        }
49
        return $this;
50
    }
51
52
    /**
53
     * load content of a file into array
54
     *
55
     * @param  string $path        full path of the .env file
56
     * @throws \RuntimeException   if $path not readable
57
     * @return array
58
     */
59
    protected function loadPath(string $path): array
60
    {
61
        try {
62
            if ($str = \file_get_contents($path)) {
63
                return $this->parseString($str);
64
            }
65
            return [];
66
        } catch (\Throwable $e) {
67
            throw new \RuntimeException($e->getMessage());
68
        }
69
    }
70
71
    /**
72
     * Parse 'ENV = value' into pairs
73
     *
74
     * @param  string $str  string to parse
75
     * @return array
76
     */
77
    protected function parseString(string $str): array
78
    {
79
        $regex =
80
        '~^\s*+
81
            (?:
82
                (?:([^#\s=]++) \s*+ = \s*+
83
                    (?|
84
                        (([^"\'#\s][^#\n]*?)) |
85
                        (["\'])((?:\\\2|.)*?)\2
86
                    )?
87
                ) |
88
                (?: (\.|source) \s++ ([^#\n]*) )
89
            )\s*?(?:[#].*)?
90
        $~mx';
91
92
        $pairs = [];
93
        if (\preg_match_all($regex, $str, $matched, \PREG_SET_ORDER)) {
94
            foreach ($matched as $m) {
95
                if (isset($m[3])) {
96
                    $pairs[$m[1]] = $m[3];
97
                } else {
98
                    $pairs[$m[1]] = '';
99
                }
100
            }
101
        }
102
        return $pairs;
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108
    protected function getReference(string $name)
109
    {
110
        $default = '';
111
        if (false !== strpos($name, ':-')) {
112
            list($name, $default) = explode(':-', $name, 2);
113
        } elseif (false !== strpos($name, ':=')) {
114
            list($name, $default) = explode(':=', $name, 2);
115
            $this->setEnv($name, $default, false);
116
        }
117
        return getenv($name) === false ? $default : getenv($name);
118
    }
119
120
    /**
121
     * @param  string $key key to set
122
     * @param  string $val value to set
123
     * @param  bool   $overwrite
124
     * @return void
125
     */
126
    protected function setEnv(string $key, string $val, bool $overwrite): void
127
    {
128
        if ($overwrite || false === getenv($key)) {
129
            putenv("$key=$val");
130
        }
131
    }
132
}
133