LoadEnvTrait::clearPairs()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 1
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Env
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Env\Traits;
16
17
use Phossa2\Env\Message\Message;
18
use Phossa2\Env\Exception\LogicException;
19
20
/**
21
 * Collections of LOAD related methods
22
 *
23
 * @package Phossa2\Env
24
 * @author  Hong Zhang <[email protected]>
25
 * @version 2.0.2
26
 * @since   2.0.1 added
27
 * @since   2.0.2 added support for ${0} etc.
28
 * @since   2.0.3 changed ${0} to ${BASH_SOURCE}
29
 */
30
trait LoadEnvTrait
31
{
32
    /**
33
     * marker for sourcing another env file
34
     *
35
     * @var    string
36
     * @access private
37
     */
38
    private $source_marker = '__SOURCING_FILE__';
39
40
    /**
41
     * Load & parse, return the key/value pairs
42
     *
43
     * @param  string $path
44
     * @return array
45
     * @throws LogicException if $path not readable or failure
46
     * @access protected
47
     */
48
    protected function loadEnv(/*# string */ $path)/*# : array */
49
    {
50
        // read data
51
        $content = $this->readContent($path);
52
53
        // parse it
54
        $pairs = $this->parseString($content);
55
56
        // expand any '${__DIR__}' or '${__FILE__}'
57
        if (false !== strpos($content, '${BASH_SOURCE') ||
58
            false !== strpos($content, '${__')
59
        ) {
60
            $this->expandMagic($pairs, $path);
61
        }
62
63
        return $pairs;
64
    }
65
66
    /**
67
     * Read from a file, returns the content string
68
     *
69
     * @param  string $path
70
     * @throws LogicException if $path not readable or failure
71
     * @return string
72
     * @access protected
73
     */
74
    protected function readContent(/*# string */ $path)/*# : string */
75
    {
76
        $str = file_get_contents($path);
77
78
        if (is_string($str)) {
79
            return $str;
80
        } else {
81
            throw new LogicException(
82
                Message::get(Message::ENV_READ_FAIL, $path),
83
                Message::ENV_READ_FAIL
84
            );
85
        }
86
    }
87
88
    /**
89
     * Parse whole string into key/value pairs
90
     *
91
     * @param  string $string
92
     * @return array
93
     * @access protected
94
     */
95
    protected function parseString(/*# string */ $string)/*# : array */
96
    {
97
        $regex =
98
        '~^\s*+
99
            (?:
100
                (?:([^#\s=]++) \s*+ = \s*+
101
                    (?|
102
                        (([^"\'#\s][^#\n]*?)) |
103
                        (["\'])((?:\\\2|.)*?)\2
104
                    )?
105
                ) |
106
                (?: (\.|source) \s++ ([^#\n]*) )
107
            )\s*?(?:[#].*)?
108
        $~mx';
109
110
        if (preg_match_all($regex, $string, $matched, \PREG_SET_ORDER)) {
111
            return $this->clearPairs($matched);
112
        } else {
113
            return [];
114
        }
115
    }
116
117
    /**
118
     * Clean up, return key/value pairs
119
     *
120
     * @param  array $matched
121
     * @return array
122
     * @access protected
123
     */
124
    protected function clearPairs(/*# array */ $matched)/*# : array */
125
    {
126
        $pairs = [];
127
        foreach ($matched as $m) {
128
            // source another env file
129
            if (isset($m[5])) {
130
                $file = trim($m[5]);
131
                $pairs[$file] = $this->source_marker;
132
133
            // value found
134
            } elseif (isset($m[3])) {
135
                $pairs[$m[1]] = $m[3];
136
137
            // no value defined
138
            } else {
139
                $pairs[$m[1]] = '';
140
            }
141
        }
142
        return $pairs;
143
    }
144
145
    /**
146
     * Expand PATH/DIR/FILENAME in key & value
147
     *
148
     * @param  array &$data
149
     * @param  string $path
150
     * @access protected
151
     * @since  2.0.2 added support for ${0} etc.
152
     * @since  2.0.3 changed to ${BASH_SOURCE}
153
     */
154
    protected function expandMagic(array &$data, $path)
155
    {
156
        $srch = [
157
            '${BASH_SOURCE}', '${BASH_SOURCE%/*}', '${BASH_SOURCE##*/}',
158
            '${__PATH__}', '${__DIR__}', '${__FILE__}'
159
        ];
160
        $repl = [
161
            $path, dirname($path), basename($path),
162
            $path, dirname($path), basename($path)
163
        ];
164
165
        // expand both key and value
166
        foreach ($data as $key => $val) {
167
            $k2 = str_replace($srch, $repl, $key);
168
            $v2 = str_replace($srch, $repl, $val);
169
            if ($k2 !== $key) {
170
                unset($data[$key]);
171
                $key = $k2;
172
            }
173
            $data[$key] = $v2;
174
        }
175
    }
176
}
177