Lines.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. <?php
  2. declare(strict_types=1);
  3. namespace Dotenv\Parser;
  4. use Dotenv\Util\Regex;
  5. use Dotenv\Util\Str;
  6. final class Lines
  7. {
  8. /**
  9. * This class is a singleton.
  10. *
  11. * @codeCoverageIgnore
  12. *
  13. * @return void
  14. */
  15. private function __construct()
  16. {
  17. //
  18. }
  19. /**
  20. * Process the array of lines of environment variables.
  21. *
  22. * This will produce an array of raw entries, one per variable.
  23. *
  24. * @param string[] $lines
  25. *
  26. * @return string[]
  27. */
  28. public static function process(array $lines)
  29. {
  30. $output = [];
  31. $multiline = false;
  32. $multilineBuffer = [];
  33. foreach ($lines as $line) {
  34. [$multiline, $line, $multilineBuffer] = self::multilineProcess($multiline, $line, $multilineBuffer);
  35. if (!$multiline && !self::isCommentOrWhitespace($line)) {
  36. $output[] = $line;
  37. }
  38. }
  39. return $output;
  40. }
  41. /**
  42. * Used to make all multiline variable process.
  43. *
  44. * @param bool $multiline
  45. * @param string $line
  46. * @param string[] $buffer
  47. *
  48. * @return array{bool,string, string[]}
  49. */
  50. private static function multilineProcess(bool $multiline, string $line, array $buffer)
  51. {
  52. $startsOnCurrentLine = $multiline ? false : self::looksLikeMultilineStart($line);
  53. // check if $line can be multiline variable
  54. if ($startsOnCurrentLine) {
  55. $multiline = true;
  56. }
  57. if ($multiline) {
  58. \array_push($buffer, $line);
  59. if (self::looksLikeMultilineStop($line, $startsOnCurrentLine)) {
  60. $multiline = false;
  61. $line = \implode("\n", $buffer);
  62. $buffer = [];
  63. }
  64. }
  65. return [$multiline, $line, $buffer];
  66. }
  67. /**
  68. * Determine if the given line can be the start of a multiline variable.
  69. *
  70. * @param string $line
  71. *
  72. * @return bool
  73. */
  74. private static function looksLikeMultilineStart(string $line)
  75. {
  76. return Str::pos($line, '="')->map(static function () use ($line) {
  77. return self::looksLikeMultilineStop($line, true) === false;
  78. })->getOrElse(false);
  79. }
  80. /**
  81. * Determine if the given line can be the start of a multiline variable.
  82. *
  83. * @param string $line
  84. * @param bool $started
  85. *
  86. * @return bool
  87. */
  88. private static function looksLikeMultilineStop(string $line, bool $started)
  89. {
  90. if ($line === '"') {
  91. return true;
  92. }
  93. return Regex::occurrences('/(?=([^\\\\]"))/', \str_replace('\\\\', '', $line))->map(static function (int $count) use ($started) {
  94. return $started ? $count > 1 : $count >= 1;
  95. })->success()->getOrElse(false);
  96. }
  97. /**
  98. * Determine if the line in the file is a comment or whitespace.
  99. *
  100. * @param string $line
  101. *
  102. * @return bool
  103. */
  104. private static function isCommentOrWhitespace(string $line)
  105. {
  106. $line = \trim($line);
  107. return $line === '' || (isset($line[0]) && $line[0] === '#');
  108. }
  109. }