@@ -34,6 +34,8 @@ public static function parse($entry)
3434 *
3535 * @param string $line
3636 *
37+ * @throws \Dotenv\Exception\InvalidFileException
38+ *
3739 * @return array
3840 */
3941 private static function splitStringIntoParts ($ line )
@@ -45,6 +47,12 @@ private static function splitStringIntoParts($line)
4547 list ($ name , $ value ) = array_map ('trim ' , explode ('= ' , $ line , 2 ));
4648 }
4749
50+ if ($ name === '' ) {
51+ throw new InvalidFileException (
52+ self ::getErrorMessage ('an unexpected equals ' , $ line )
53+ );
54+ }
55+
4856 return [$ name , $ value ];
4957 }
5058
@@ -53,15 +61,37 @@ private static function splitStringIntoParts($line)
5361 *
5462 * @param string $name
5563 *
64+ * @throws \Dotenv\Exception\InvalidFileException
65+ *
5666 * @return string
5767 */
5868 private static function sanitiseName ($ name )
5969 {
60- return trim (str_replace (['export ' , '\'' , '" ' ], '' , $ name ));
70+ $ name = trim (str_replace (['export ' , '\'' , '" ' ], '' , $ name ));
71+
72+ if (!self ::isValidName ($ name )) {
73+ throw new InvalidFileException (
74+ self ::getErrorMessage ('an invalid name ' , $ name )
75+ );
76+ }
77+
78+ return $ name ;
6179 }
6280
6381 /**
64- * Strips quotes from the environment variable value.
82+ * Is the given variable name valid?
83+ *
84+ * @param string $name
85+ *
86+ * @return bool
87+ */
88+ private static function isValidName ($ name )
89+ {
90+ return preg_match ('~\A[a-zA-Z0-9_.]+\z~ ' , $ name ) === 1 ;
91+ }
92+
93+ /**
94+ * Strips quotes and comments from the environment variable value.
6595 *
6696 * @param string|null $value
6797 *
@@ -75,46 +105,77 @@ private static function sanitiseValue($value)
75105 return $ value ;
76106 }
77107
78- if (self ::beginsWithAQuote ($ value )) { // value starts with a quote
79- $ quote = $ value [0 ];
80- $ regexPattern = sprintf (
81- '/^
82- %1$s # match a quote at the start of the value
83- ( # capturing sub-pattern used
84- (?: # we do not need to capture this
85- [^%1$s \\\\]+ # any character other than a quote or backslash
86- | \\\\\\\\ # or two backslashes together
87- | \\\\%1$s # or an escaped quote e.g \"
88- )* # as many characters that match the previous rules
89- ) # end of the capturing sub-pattern
90- %1$s # and the closing quote
91- .*$ # and discard any string after the closing quote
92- /mx ' ,
93- $ quote
94- );
95- $ value = preg_replace ($ regexPattern , '$1 ' , $ value );
96- $ value = str_replace ("\\$ quote " , $ quote , $ value );
97- $ value = str_replace ('\\\\' , '\\' , $ value );
98- } else {
99- $ parts = explode (' # ' , $ value , 2 );
100- $ value = $ parts [0 ];
101-
102- // Unquoted values cannot contain whitespace
103- if (preg_match ('/\s+/ ' , $ value ) > 0 ) {
104- // Check if value is a comment (usually triggered when empty value with comment)
105- if (preg_match ('/^#/ ' , $ value ) > 0 ) {
106- $ value = '' ;
107- } else {
108- throw new InvalidFileException (
109- 'Dotenv values containing spaces must be surrounded by quotes. '
110- );
111- }
108+ if (self ::beginsWithAQuote ($ value )) {
109+ return self ::processQuotedValue ($ value );
110+ }
111+
112+ // Strip comments from the left
113+ $ value = explode (' # ' , $ value , 2 )[0 ];
114+
115+ // Unquoted values cannot contain whitespace
116+ if (preg_match ('/\s+/ ' , $ value ) > 0 ) {
117+ // Check if value is a comment (usually triggered when empty value with comment)
118+ if (preg_match ('/^#/ ' , $ value ) > 0 ) {
119+ $ value = '' ;
120+ } else {
121+ throw new InvalidFileException (
122+ self ::getErrorMessage ('an unexpected space ' , $ value )
123+ );
112124 }
113125 }
114126
115127 return $ value ;
116128 }
117129
130+ /**
131+ * Strips quotes from the environment variable value.
132+ *
133+ * @param string $value
134+ *
135+ * @return string
136+ */
137+ private static function processQuotedValue ($ value )
138+ {
139+ $ quote = $ value [0 ];
140+
141+ $ pattern = sprintf (
142+ '/^
143+ %1$s # match a quote at the start of the value
144+ ( # capturing sub-pattern used
145+ (?: # we do not need to capture this
146+ [^%1$s \\\\]+ # any character other than a quote or backslash
147+ | \\\\\\\\ # or two backslashes together
148+ | \\\\%1$s # or an escaped quote e.g \"
149+ )* # as many characters that match the previous rules
150+ ) # end of the capturing sub-pattern
151+ %1$s # and the closing quote
152+ .*$ # and discard any string after the closing quote
153+ /mx ' ,
154+ $ quote
155+ );
156+
157+ $ value = preg_replace ($ pattern , '$1 ' , $ value );
158+
159+ return str_replace ('\\\\' , '\\' , str_replace ("\\$ quote " , $ quote , $ value ));
160+ }
161+
162+ /**
163+ * Generate a friendly error message.
164+ *
165+ * @param string $cause
166+ * @param string $subject
167+ *
168+ * @return string
169+ */
170+ private static function getErrorMessage ($ cause , $ subject )
171+ {
172+ return sprintf (
173+ 'Failed to parse dotenv file due to %s. Failed at [%s]. ' ,
174+ $ cause ,
175+ strtok ($ subject , "\n" )
176+ );
177+ }
178+
118179 /**
119180 * Determine if the given string begins with a quote.
120181 *
0 commit comments