PageRenderTime 47ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/vlucas/phpdotenv/src/Loader.php

https://gitlab.com/btkm/correct_fb
PHP | 371 lines | 163 code | 37 blank | 171 comment | 16 complexity | 438f0d39d51aa144be6f96bd4486c228 MD5 | raw file
  1. <?php
  2. namespace Dotenv;
  3. use Dotenv\Exception\InvalidFileException;
  4. use Dotenv\Exception\InvalidPathException;
  5. /**
  6. * This is the loaded class.
  7. *
  8. * It's responsible for loading variables by reading a file from disk and:
  9. * - stripping comments beginning with a `#`,
  10. * - parsing lines that look shell variable setters, e.g `export key = value`, `key="value"`.
  11. */
  12. class Loader
  13. {
  14. /**
  15. * The file path.
  16. *
  17. * @var string
  18. */
  19. protected $filePath;
  20. /**
  21. * Are we immutable?
  22. *
  23. * @var bool
  24. */
  25. protected $immutable;
  26. /**
  27. * Create a new loader instance.
  28. *
  29. * @param string $filePath
  30. * @param bool $immutable
  31. *
  32. * @return void
  33. */
  34. public function __construct($filePath, $immutable = false)
  35. {
  36. $this->filePath = $filePath;
  37. $this->immutable = $immutable;
  38. }
  39. /**
  40. * Load `.env` file in given directory.
  41. *
  42. * @return array
  43. */
  44. public function load()
  45. {
  46. $this->ensureFileIsReadable();
  47. $filePath = $this->filePath;
  48. $lines = $this->readLinesFromFile($filePath);
  49. foreach ($lines as $line) {
  50. if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
  51. $this->setEnvironmentVariable($line);
  52. }
  53. }
  54. return $lines;
  55. }
  56. /**
  57. * Ensures the given filePath is readable.
  58. *
  59. * @throws \Dotenv\Exception\InvalidPathException
  60. *
  61. * @return void
  62. */
  63. protected function ensureFileIsReadable()
  64. {
  65. if (!is_readable($this->filePath) || !is_file($this->filePath)) {
  66. throw new InvalidPathException(sprintf('Unable to read the environment file at %s.', $this->filePath));
  67. }
  68. }
  69. /**
  70. * Normalise the given environment variable.
  71. *
  72. * Takes value as passed in by developer and:
  73. * - ensures we're dealing with a separate name and value, breaking apart the name string if needed,
  74. * - cleaning the value of quotes,
  75. * - cleaning the name of quotes,
  76. * - resolving nested variables.
  77. *
  78. * @param string $name
  79. * @param string $value
  80. *
  81. * @return array
  82. */
  83. protected function normaliseEnvironmentVariable($name, $value)
  84. {
  85. list($name, $value) = $this->splitCompoundStringIntoParts($name, $value);
  86. list($name, $value) = $this->sanitiseVariableName($name, $value);
  87. list($name, $value) = $this->sanitiseVariableValue($name, $value);
  88. $value = $this->resolveNestedVariables($value);
  89. return array($name, $value);
  90. }
  91. /**
  92. * Process the runtime filters.
  93. *
  94. * Called from the `VariableFactory`, passed as a callback in `$this->loadFromFile()`.
  95. *
  96. * @param string $name
  97. * @param string $value
  98. *
  99. * @return array
  100. */
  101. public function processFilters($name, $value)
  102. {
  103. list($name, $value) = $this->splitCompoundStringIntoParts($name, $value);
  104. list($name, $value) = $this->sanitiseVariableName($name, $value);
  105. list($name, $value) = $this->sanitiseVariableValue($name, $value);
  106. return array($name, $value);
  107. }
  108. /**
  109. * Read lines from the file, auto detecting line endings.
  110. *
  111. * @param string $filePath
  112. *
  113. * @return array
  114. */
  115. protected function readLinesFromFile($filePath)
  116. {
  117. // Read file into an array of lines with auto-detected line endings
  118. $autodetect = ini_get('auto_detect_line_endings');
  119. ini_set('auto_detect_line_endings', '1');
  120. $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  121. ini_set('auto_detect_line_endings', $autodetect);
  122. return $lines;
  123. }
  124. /**
  125. * Determine if the line in the file is a comment, e.g. begins with a #.
  126. *
  127. * @param string $line
  128. *
  129. * @return bool
  130. */
  131. protected function isComment($line)
  132. {
  133. return strpos(trim($line), '#') === 0;
  134. }
  135. /**
  136. * Determine if the given line looks like it's setting a variable.
  137. *
  138. * @param string $line
  139. *
  140. * @return bool
  141. */
  142. protected function looksLikeSetter($line)
  143. {
  144. return strpos($line, '=') !== false;
  145. }
  146. /**
  147. * Split the compound string into parts.
  148. *
  149. * If the `$name` contains an `=` sign, then we split it into 2 parts, a `name` & `value`
  150. * disregarding the `$value` passed in.
  151. *
  152. * @param string $name
  153. * @param string $value
  154. *
  155. * @return array
  156. */
  157. protected function splitCompoundStringIntoParts($name, $value)
  158. {
  159. if (strpos($name, '=') !== false) {
  160. list($name, $value) = array_map('trim', explode('=', $name, 2));
  161. }
  162. return array($name, $value);
  163. }
  164. /**
  165. * Strips quotes from the environment variable value.
  166. *
  167. * @param string $name
  168. * @param string $value
  169. *
  170. * @throws \Dotenv\Exception\InvalidFileException
  171. *
  172. * @return array
  173. */
  174. protected function sanitiseVariableValue($name, $value)
  175. {
  176. $value = trim($value);
  177. if (!$value) {
  178. return array($name, $value);
  179. }
  180. if ($this->beginsWithAQuote($value)) { // value starts with a quote
  181. $quote = $value[0];
  182. $regexPattern = sprintf(
  183. '/^
  184. %1$s # match a quote at the start of the value
  185. ( # capturing sub-pattern used
  186. (?: # we do not need to capture this
  187. [^%1$s\\\\] # any character other than a quote or backslash
  188. |\\\\\\\\ # or two backslashes together
  189. |\\\\%1$s # or an escaped quote e.g \"
  190. )* # as many characters that match the previous rules
  191. ) # end of the capturing sub-pattern
  192. %1$s # and the closing quote
  193. .*$ # and discard any string after the closing quote
  194. /mx',
  195. $quote
  196. );
  197. $value = preg_replace($regexPattern, '$1', $value);
  198. $value = str_replace("\\$quote", $quote, $value);
  199. $value = str_replace('\\\\', '\\', $value);
  200. } else {
  201. $parts = explode(' #', $value, 2);
  202. $value = trim($parts[0]);
  203. // Unquoted values cannot contain whitespace
  204. if (preg_match('/\s+/', $value) > 0) {
  205. throw new InvalidFileException('Dotenv values containing spaces must be surrounded by quotes.');
  206. }
  207. }
  208. return array($name, trim($value));
  209. }
  210. /**
  211. * Resolve the nested variables.
  212. *
  213. * Look for {$varname} patterns in the variable value and replace with an existing
  214. * environment variable.
  215. *
  216. * @param string $value
  217. *
  218. * @return mixed
  219. */
  220. protected function resolveNestedVariables($value)
  221. {
  222. if (strpos($value, '$') !== false) {
  223. $loader = $this;
  224. $value = preg_replace_callback(
  225. '/\${([a-zA-Z0-9_]+)}/',
  226. function ($matchedPatterns) use ($loader) {
  227. $nestedVariable = $loader->getEnvironmentVariable($matchedPatterns[1]);
  228. if ($nestedVariable === null) {
  229. return $matchedPatterns[0];
  230. } else {
  231. return $nestedVariable;
  232. }
  233. },
  234. $value
  235. );
  236. }
  237. return $value;
  238. }
  239. /**
  240. * Strips quotes and the optional leading "export " from the environment variable name.
  241. *
  242. * @param string $name
  243. * @param string $value
  244. *
  245. * @return array
  246. */
  247. protected function sanitiseVariableName($name, $value)
  248. {
  249. $name = trim(str_replace(array('export ', '\'', '"'), '', $name));
  250. return array($name, $value);
  251. }
  252. /**
  253. * Determine if the given string begins with a quote.
  254. *
  255. * @param string $value
  256. *
  257. * @return bool
  258. */
  259. protected function beginsWithAQuote($value)
  260. {
  261. return strpbrk($value[0], '"\'') !== false;
  262. }
  263. /**
  264. * Search the different places for environment variables and return first value found.
  265. *
  266. * @param string $name
  267. *
  268. * @return string
  269. */
  270. public function getEnvironmentVariable($name)
  271. {
  272. switch (true) {
  273. case array_key_exists($name, $_ENV):
  274. return $_ENV[$name];
  275. case array_key_exists($name, $_SERVER):
  276. return $_SERVER[$name];
  277. default:
  278. $value = getenv($name);
  279. return $value === false ? null : $value; // switch getenv default to null
  280. }
  281. }
  282. /**
  283. * Set an environment variable.
  284. *
  285. * This is done using:
  286. * - putenv,
  287. * - $_ENV,
  288. * - $_SERVER.
  289. *
  290. * The environment variable value is stripped of single and double quotes.
  291. *
  292. * @param string $name
  293. * @param string|null $value
  294. *
  295. * @return void
  296. */
  297. public function setEnvironmentVariable($name, $value = null)
  298. {
  299. list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
  300. // Don't overwrite existing environment variables if we're immutable
  301. // Ruby's dotenv does this with `ENV[key] ||= value`.
  302. if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
  303. return;
  304. }
  305. putenv("$name=$value");
  306. $_ENV[$name] = $value;
  307. $_SERVER[$name] = $value;
  308. }
  309. /**
  310. * Clear an environment variable.
  311. *
  312. * This is not (currently) used by Dotenv but is provided as a utility
  313. * method for 3rd party code.
  314. *
  315. * This is done using:
  316. * - putenv,
  317. * - unset($_ENV, $_SERVER).
  318. *
  319. * @param string $name
  320. *
  321. * @see setEnvironmentVariable()
  322. *
  323. * @return void
  324. */
  325. public function clearEnvironmentVariable($name)
  326. {
  327. // Don't clear anything if we're immutable.
  328. if ($this->immutable) {
  329. return;
  330. }
  331. putenv($name);
  332. unset($_ENV[$name], $_SERVER[$name]);
  333. }
  334. }