DotenvTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. declare(strict_types=1);
  3. namespace Dotenv\Tests;
  4. use Dotenv\Dotenv;
  5. use Dotenv\Exception\InvalidEncodingException;
  6. use Dotenv\Exception\InvalidPathException;
  7. use Dotenv\Loader\Loader;
  8. use Dotenv\Parser\Parser;
  9. use Dotenv\Repository\RepositoryBuilder;
  10. use Dotenv\Store\StoreBuilder;
  11. use PHPUnit\Framework\TestCase;
  12. final class DotenvTest extends TestCase
  13. {
  14. /**
  15. * @var string
  16. */
  17. private static $folder;
  18. /**
  19. * @beforeClass
  20. *
  21. * @return void
  22. */
  23. public static function setFolder()
  24. {
  25. self::$folder = \dirname(__DIR__).'/fixtures/env';
  26. }
  27. public function testDotenvThrowsExceptionIfUnableToLoadFile()
  28. {
  29. $dotenv = Dotenv::createMutable(__DIR__);
  30. $this->expectException(InvalidPathException::class);
  31. $this->expectExceptionMessage('Unable to read any of the environment file(s) at');
  32. $dotenv->load();
  33. }
  34. public function testDotenvThrowsExceptionIfUnableToLoadFiles()
  35. {
  36. $dotenv = Dotenv::createMutable([__DIR__, __DIR__.'/foo/bar']);
  37. $this->expectException(InvalidPathException::class);
  38. $this->expectExceptionMessage('Unable to read any of the environment file(s) at');
  39. $dotenv->load();
  40. }
  41. public function testDotenvThrowsExceptionWhenNoFiles()
  42. {
  43. $dotenv = Dotenv::createMutable([]);
  44. $this->expectException(InvalidPathException::class);
  45. $this->expectExceptionMessage('At least one environment file path must be provided.');
  46. $dotenv->load();
  47. }
  48. public function testDotenvTriesPathsToLoad()
  49. {
  50. $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
  51. self::assertCount(4, $dotenv->load());
  52. }
  53. public function testDotenvTriesPathsToLoadTwice()
  54. {
  55. $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
  56. self::assertCount(4, $dotenv->load());
  57. $dotenv = Dotenv::createImmutable([__DIR__, self::$folder]);
  58. self::assertCount(0, $dotenv->load());
  59. }
  60. public function testDotenvTriesPathsToSafeLoad()
  61. {
  62. $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
  63. self::assertCount(4, $dotenv->safeLoad());
  64. }
  65. public function testDotenvSkipsLoadingIfFileIsMissing()
  66. {
  67. $dotenv = Dotenv::createMutable(__DIR__);
  68. self::assertSame([], $dotenv->safeLoad());
  69. }
  70. public function testDotenvLoadsEnvironmentVars()
  71. {
  72. $dotenv = Dotenv::createMutable(self::$folder);
  73. self::assertSame(
  74. ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => ''],
  75. $dotenv->load()
  76. );
  77. self::assertSame('bar', $_SERVER['FOO']);
  78. self::assertSame('baz', $_SERVER['BAR']);
  79. self::assertSame('with spaces', $_SERVER['SPACED']);
  80. self::assertEmpty($_SERVER['NULL']);
  81. }
  82. public function testDotenvLoadsEnvironmentVarsMultipleWithShortCircuitMode()
  83. {
  84. $dotenv = Dotenv::createMutable(self::$folder, ['.env', 'example.env']);
  85. self::assertSame(
  86. ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => ''],
  87. $dotenv->load()
  88. );
  89. }
  90. public function testDotenvLoadsEnvironmentVarsMultipleWithoutShortCircuitMode()
  91. {
  92. $dotenv = Dotenv::createMutable(self::$folder, ['.env', 'example.env'], false);
  93. self::assertSame(
  94. ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => '', 'EG' => 'example'],
  95. $dotenv->load()
  96. );
  97. }
  98. public function testCommentedDotenvLoadsEnvironmentVars()
  99. {
  100. $dotenv = Dotenv::createMutable(self::$folder, 'commented.env');
  101. $dotenv->load();
  102. self::assertSame('bar', $_SERVER['CFOO']);
  103. self::assertFalse(isset($_SERVER['CBAR']));
  104. self::assertFalse(isset($_SERVER['CZOO']));
  105. self::assertSame('with spaces', $_SERVER['CSPACED']);
  106. self::assertSame('a value with a # character', $_SERVER['CQUOTES']);
  107. self::assertSame('a value with a # character & a quote " character inside quotes', $_SERVER['CQUOTESWITHQUOTE']);
  108. self::assertEmpty($_SERVER['CNULL']);
  109. self::assertEmpty($_SERVER['EMPTY']);
  110. self::assertEmpty($_SERVER['EMPTY2']);
  111. self::assertSame('foo', $_SERVER['FOOO']);
  112. }
  113. public function testQuotedDotenvLoadsEnvironmentVars()
  114. {
  115. $dotenv = Dotenv::createMutable(self::$folder, 'quoted.env');
  116. $dotenv->load();
  117. self::assertSame('bar', $_SERVER['QFOO']);
  118. self::assertSame('baz', $_SERVER['QBAR']);
  119. self::assertSame('with spaces', $_SERVER['QSPACED']);
  120. self::assertEmpty(\getenv('QNULL'));
  121. self::assertSame('pgsql:host=localhost;dbname=test', $_SERVER['QEQUALS']);
  122. self::assertSame('test some escaped characters like a quote (") or maybe a backslash (\\)', $_SERVER['QESCAPED']);
  123. self::assertSame('iiiiviiiixiiiiviiii\\n', $_SERVER['QSLASH']);
  124. self::assertSame('iiiiviiiixiiiiviiii\\\\n', $_SERVER['SQSLASH']);
  125. }
  126. public function testLargeDotenvLoadsEnvironmentVars()
  127. {
  128. $dotenv = Dotenv::createMutable(self::$folder, 'large.env');
  129. $dotenv->load();
  130. self::assertSame(2730, \strlen($_SERVER['LARGE']));
  131. self::assertSame(8192, \strlen($_SERVER['HUGE']));
  132. }
  133. public function testDotenvLoadsMultibyteVars()
  134. {
  135. $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env');
  136. $dotenv->load();
  137. self::assertSame('Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě', $_SERVER['MB1']);
  138. self::assertSame('行内支付', $_SERVER['MB2']);
  139. self::assertSame('🚀', $_SERVER['APP_ENV']);
  140. }
  141. public function testDotenvLoadsMultibyteUTF8Vars()
  142. {
  143. $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env', false, 'UTF-8');
  144. $dotenv->load();
  145. self::assertSame('Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě', $_SERVER['MB1']);
  146. self::assertSame('行内支付', $_SERVER['MB2']);
  147. self::assertSame('🚀', $_SERVER['APP_ENV']);
  148. }
  149. public function testDotenvLoadWithInvalidEncoding()
  150. {
  151. $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env', false, 'UTF-88');
  152. $this->expectException(InvalidEncodingException::class);
  153. $this->expectExceptionMessage('Illegal character encoding [UTF-88] specified.');
  154. $dotenv->load();
  155. }
  156. public function testDotenvLoadsMultibyteWindowsVars()
  157. {
  158. $dotenv = Dotenv::createMutable(self::$folder, 'windows.env', false, 'Windows-1252');
  159. $dotenv->load();
  160. self::assertSame('ñá', $_SERVER['MBW']);
  161. }
  162. public function testMultipleDotenvLoadsEnvironmentVars()
  163. {
  164. $dotenv = Dotenv::createMutable(self::$folder, 'multiple.env');
  165. $dotenv->load();
  166. self::assertSame('bar', $_SERVER['MULTI1']);
  167. self::assertSame('foo', $_SERVER['MULTI2']);
  168. }
  169. public function testExportedDotenvLoadsEnvironmentVars()
  170. {
  171. $dotenv = Dotenv::createMutable(self::$folder, 'exported.env');
  172. $dotenv->load();
  173. self::assertSame('bar', $_SERVER['EFOO']);
  174. self::assertSame('baz', $_SERVER['EBAR']);
  175. self::assertSame('with spaces', $_SERVER['ESPACED']);
  176. self::assertSame('123', $_SERVER['EDQUOTED']);
  177. self::assertSame('456', $_SERVER['ESQUOTED']);
  178. self::assertEmpty($_SERVER['ENULL']);
  179. }
  180. public function testDotenvLoadsEnvGlobals()
  181. {
  182. $dotenv = Dotenv::createMutable(self::$folder);
  183. $dotenv->load();
  184. self::assertSame('bar', $_SERVER['FOO']);
  185. self::assertSame('baz', $_SERVER['BAR']);
  186. self::assertSame('with spaces', $_SERVER['SPACED']);
  187. self::assertEmpty($_SERVER['NULL']);
  188. }
  189. public function testDotenvLoadsServerGlobals()
  190. {
  191. $dotenv = Dotenv::createMutable(self::$folder);
  192. $dotenv->load();
  193. self::assertSame('bar', $_ENV['FOO']);
  194. self::assertSame('baz', $_ENV['BAR']);
  195. self::assertSame('with spaces', $_ENV['SPACED']);
  196. self::assertEmpty($_ENV['NULL']);
  197. }
  198. public function testDotenvNestedEnvironmentVars()
  199. {
  200. $dotenv = Dotenv::createMutable(self::$folder, 'nested.env');
  201. $dotenv->load();
  202. self::assertSame('{$NVAR1} {$NVAR2}', $_ENV['NVAR3']); // not resolved
  203. self::assertSame('Hellō World!', $_ENV['NVAR4']);
  204. self::assertSame('$NVAR1 {NVAR2}', $_ENV['NVAR5']); // not resolved
  205. self::assertSame('Special Value', $_ENV['N.VAR6']); // new '.' (dot) in var name
  206. self::assertSame('Special Value', $_ENV['NVAR7']); // nested '.' (dot) variable
  207. self::assertSame('', $_ENV['NVAR8']); // nested variable is empty string
  208. self::assertSame('', $_ENV['NVAR9']); // nested variable is empty string
  209. self::assertSame('${NVAR888}', $_ENV['NVAR10']); // nested variable is not set
  210. self::assertSame('NVAR1', $_ENV['NVAR11']);
  211. self::assertSame('Hellō', $_ENV['NVAR12']);
  212. self::assertSame('${${NVAR11}}', $_ENV['NVAR13']); // single quotes
  213. self::assertSame('${NVAR1} ${NVAR2}', $_ENV['NVAR14']); // single quotes
  214. self::assertSame('${NVAR1} ${NVAR2}', $_ENV['NVAR15']); // escaped
  215. }
  216. public function testDotenvNullFileArgumentUsesDefault()
  217. {
  218. $dotenv = Dotenv::createMutable(self::$folder, null);
  219. $dotenv->load();
  220. self::assertSame('bar', $_SERVER['FOO']);
  221. }
  222. /**
  223. * The fixture data has whitespace between the key and in the value string.
  224. *
  225. * Test that these keys are trimmed down.
  226. */
  227. public function testDotenvTrimmedKeys()
  228. {
  229. $dotenv = Dotenv::createMutable(self::$folder, 'quoted.env');
  230. $dotenv->load();
  231. self::assertSame('no space', $_SERVER['QWHITESPACE']);
  232. }
  233. public function testDotenvLoadDoesNotOverwriteEnv()
  234. {
  235. \putenv('IMMUTABLE=true');
  236. $dotenv = Dotenv::createImmutable(self::$folder, 'immutable.env');
  237. $dotenv->load();
  238. self::assertSame('true', \getenv('IMMUTABLE'));
  239. }
  240. public function testDotenvLoadAfterOverload()
  241. {
  242. \putenv('IMMUTABLE=true');
  243. $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'immutable.env');
  244. $dotenv->load();
  245. self::assertSame('false', \getenv('IMMUTABLE'));
  246. }
  247. public function testDotenvOverloadAfterLoad()
  248. {
  249. \putenv('IMMUTABLE=true');
  250. $dotenv = Dotenv::createUnsafeImmutable(self::$folder, 'immutable.env');
  251. $dotenv->load();
  252. self::assertSame('true', \getenv('IMMUTABLE'));
  253. }
  254. public function testDotenvOverloadDoesOverwriteEnv()
  255. {
  256. $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'mutable.env');
  257. $dotenv->load();
  258. self::assertSame('true', \getenv('MUTABLE'));
  259. }
  260. public function testDotenvAllowsSpecialCharacters()
  261. {
  262. $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'specialchars.env');
  263. $dotenv->load();
  264. self::assertSame('$a6^C7k%zs+e^.jvjXk', \getenv('SPVAR1'));
  265. self::assertSame('?BUty3koaV3%GA*hMAwH}B', \getenv('SPVAR2'));
  266. self::assertSame('jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r', \getenv('SPVAR3'));
  267. self::assertSame('22222:22#2^{', \getenv('SPVAR4'));
  268. self::assertSame('test some escaped characters like a quote " or maybe a backslash \\', \getenv('SPVAR5'));
  269. self::assertSame('secret!@', \getenv('SPVAR6'));
  270. self::assertSame('secret!@#', \getenv('SPVAR7'));
  271. self::assertSame('secret!@#', \getenv('SPVAR8'));
  272. }
  273. public function testMultilineLoading()
  274. {
  275. $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'multiline.env');
  276. $dotenv->load();
  277. self::assertSame("test\n test\"test\"\n test", \getenv('TEST'));
  278. self::assertSame("test\ntest", \getenv('TEST_ND'));
  279. self::assertSame('test\\ntest', \getenv('TEST_NS'));
  280. self::assertSame('https://vision.googleapis.com/v1/images:annotate?key=', \getenv('TEST_EQD'));
  281. self::assertSame('https://vision.googleapis.com/v1/images:annotate?key=', \getenv('TEST_EQS'));
  282. }
  283. public function testEmptyLoading()
  284. {
  285. $dotenv = Dotenv::createImmutable(self::$folder, 'empty.env');
  286. self::assertSame(['EMPTY_VAR' => null], $dotenv->load());
  287. }
  288. public function testUnicodeVarNames()
  289. {
  290. $dotenv = Dotenv::createImmutable(self::$folder, 'unicodevarnames.env');
  291. $dotenv->load();
  292. self::assertSame('Skybert', $_SERVER['AlbertÅberg']);
  293. self::assertSame('2022-04-01T00:00', $_SERVER['ДатаЗакрытияРасчетногоПериода']);
  294. }
  295. public function testDirectConstructor()
  296. {
  297. $repository = RepositoryBuilder::createWithDefaultAdapters()->make();
  298. $store = StoreBuilder::createWithDefaultName()->addPath(self::$folder)->make();
  299. $dotenv = new Dotenv($store, new Parser(), new Loader(), $repository);
  300. self::assertSame([
  301. 'FOO' => 'bar',
  302. 'BAR' => 'baz',
  303. 'SPACED' => 'with spaces',
  304. 'NULL' => '',
  305. ], $dotenv->load());
  306. }
  307. public function testDotenvParseExample1()
  308. {
  309. $output = Dotenv::parse(
  310. "BASE_DIR=\"/var/webroot/project-root\"\nCACHE_DIR=\"\${BASE_DIR}/cache\"\nTMP_DIR=\"\${BASE_DIR}/tmp\"\n"
  311. );
  312. self::assertSame($output, [
  313. 'BASE_DIR' => '/var/webroot/project-root',
  314. 'CACHE_DIR' => '/var/webroot/project-root/cache',
  315. 'TMP_DIR' => '/var/webroot/project-root/tmp',
  316. ]);
  317. }
  318. public function testDotenvParseExample2()
  319. {
  320. $output = Dotenv::parse("FOO=Bar\nBAZ=\"Hello \${FOO}\"");
  321. self::assertSame($output, ['FOO' => 'Bar', 'BAZ' => 'Hello Bar']);
  322. }
  323. public function testDotenvParseEmptyCase()
  324. {
  325. $output = Dotenv::parse('');
  326. self::assertSame($output, []);
  327. }
  328. }