PageRenderTime 43ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/backend/classes/dotenv/Loader.php

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