@@ -31,13 +31,22 @@ public function processFile(ParserInterface $file): array
3131 return [];
3232 }
3333
34- // Check UTF-8 encoding
34+ // Early exit for empty files
35+ if ('' === $ content ) {
36+ return [];
37+ }
38+
39+ // Check UTF-8 encoding first - if invalid, other checks may fail
3540 if (!$ this ->isValidUtf8 ($ content )) {
3641 $ issues ['encoding ' ] = 'File is not valid UTF-8 encoded ' ;
42+
43+ // Skip other checks for invalid UTF-8 content
44+ return $ issues ;
3745 }
3846
39- // Check for BOM
40- if ($ this ->hasByteOrderMark ($ content )) {
47+ // Check for BOM (fast byte check)
48+ $ hasBom = $ this ->hasByteOrderMark ($ content );
49+ if ($ hasBom ) {
4150 $ issues ['bom ' ] = 'File contains UTF-8 Byte Order Mark (BOM) ' ;
4251 }
4352
@@ -50,15 +59,13 @@ public function processFile(ParserInterface $file): array
5059 );
5160 }
5261
53- // Check Unicode normalization
62+ // Check Unicode normalization (expensive, only if intl available)
5463 if ($ this ->hasUnicodeNormalizationIssues ($ content )) {
5564 $ issues ['unicode_normalization ' ] = 'File contains non-NFC normalized Unicode characters ' ;
5665 }
5766
58- // JSON-specific validation for JSON files
59- if ($ file instanceof JsonParser && !$ this ->isValidJsonStructure ($ content )) {
60- $ issues ['json_syntax ' ] = 'File contains invalid JSON syntax ' ;
61- }
67+ // Note: JSON syntax validation is handled by JsonParser constructor
68+ // Invalid JSON files will throw exceptions before reaching this validator
6269
6370 return $ issues ;
6471 }
@@ -112,19 +119,29 @@ private function findInvisibleCharacters(string $content): array
112119 {
113120 $ problematicChars = [];
114121
115- // Check for various problematic characters
116- $ checks = [
117- 'Zero-width space ' => "\u{200B}" ,
118- 'Zero-width non-joiner ' => "\u{200C}" ,
119- 'Zero-width joiner ' => "\u{200D}" ,
120- 'Word joiner ' => "\u{2060}" ,
121- 'Zero-width no-break space ' => "\u{FEFF}" ,
122- 'Left-to-right mark ' => "\u{200E}" ,
123- 'Right-to-left mark ' => "\u{200F}" ,
124- 'Soft hyphen ' => "\u{00AD}" ,
122+ // Early exit for ASCII-only content (performance optimization)
123+ if (mb_check_encoding ($ content , 'ASCII ' )) {
124+ // Only check for control characters in ASCII content
125+ if (preg_match ('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/ ' , $ content )) {
126+ $ problematicChars [] = 'Control characters ' ;
127+ }
128+
129+ return $ problematicChars ;
130+ }
131+
132+ // Check for problematic Unicode characters individually for better performance
133+ $ charMap = [
134+ "\u{200B}" => 'Zero-width space ' ,
135+ "\u{200C}" => 'Zero-width non-joiner ' ,
136+ "\u{200D}" => 'Zero-width joiner ' ,
137+ "\u{2060}" => 'Word joiner ' ,
138+ "\u{FEFF}" => 'Zero-width no-break space ' ,
139+ "\u{200E}" => 'Left-to-right mark ' ,
140+ "\u{200F}" => 'Right-to-left mark ' ,
141+ "\u{00AD}" => 'Soft hyphen ' ,
125142 ];
126143
127- foreach ($ checks as $ name => $ char ) {
144+ foreach ($ charMap as $ char => $ name ) {
128145 if (str_contains ($ content , $ char )) {
129146 $ problematicChars [] = $ name ;
130147 }
@@ -141,25 +158,11 @@ private function findInvisibleCharacters(string $content): array
141158 private function hasUnicodeNormalizationIssues (string $ content ): bool
142159 {
143160 if (!class_exists ('Normalizer ' )) {
144- // If intl extension is not available, skip this check
145161 return false ;
146162 }
147163
148- // Check if content is not in NFC (Canonical Decomposition followed by Canonical Composition)
149164 $ normalized = \Normalizer::normalize ($ content , \Normalizer::FORM_C );
150165
151166 return false !== $ normalized && $ content !== $ normalized ;
152167 }
153-
154- private function isValidJsonStructure (string $ content ): bool
155- {
156- // Remove BOM if present for JSON validation
157- $ cleanContent = $ this ->hasByteOrderMark ($ content )
158- ? substr ($ content , 3 )
159- : $ content ;
160-
161- json_decode ($ cleanContent );
162-
163- return JSON_ERROR_NONE === json_last_error ();
164- }
165168}
0 commit comments