1<?php 2 3set_error_handler(function($_, $msg) { 4 throw new Exception($msg); 5}); 6 7$rootDir = __DIR__ . '/../..'; 8$it = new RecursiveIteratorIterator( 9 new RecursiveDirectoryIterator($rootDir), 10 RecursiveIteratorIterator::LEAVES_ONLY 11); 12 13$excludes = [ 14 // Bundled libraries / files. 15 'ext/bcmath/libbcmath/', 16 'ext/date/lib/', 17 'ext/fileinfo/data_file.c', 18 'ext/fileinfo/libmagic/', 19 'ext/gd/libgd/', 20 'ext/hash/sha3/', 21 'ext/hash/hash_whirlpool.c', 22 'ext/hash/php_hash_whirlpool_tables.h', 23 'ext/mbstring/libmbfl/', 24 'ext/mbstring/unicode_data.h', 25 'ext/pcre/pcre2lib/', 26 'ext/standard/html_tables/html_table_gen.php', 27 'ext/xmlrpc/libxmlrpc/', 28 'sapi/cli/php_http_parser.c', 29 'sapi/cli/php_http_parser.h', 30 'sapi/litespeed/', 31 // Not a PHP file. 32 'ext/zlib/tests/data.inc', 33 // Flexible HEREDOC/NOWDOC tests are likely whitespace sensitive. 34 // TODO: Properly classify them. 35 'Zend/tests/flexible-', 36]; 37 38foreach ($it as $file) { 39 if (!$file->isFile()) { 40 continue; 41 } 42 43 $path = $file->getPathName(); 44 foreach ($excludes as $exclude) { 45 if (strpos($path, $exclude) !== false) { 46 continue 2; 47 } 48 } 49 50 $lang = getLanguageFromExtension($file->getExtension()); 51 if ($lang === null) { 52 continue; 53 } 54 55 $origCode = $code = file_get_contents($path); 56 57 if ($lang === 'c') { 58 $code = stripTrailingWhitespace($code); 59 // TODO: Avoid this for now. 60 // $code = reindentToTabs($code); 61 } else if ($lang === 'php') { 62 $code = stripTrailingWhitespace($code); 63 $code = reindentToSpaces($code); 64 } else if ($lang === 'phpt') { 65 // TODO: Don't reformat .phpt on PHP-7.4. 66 /*$code = transformTestCode($code, function(string $code) { 67 $code = stripTrailingWhitespace($code); 68 $code = reindentToSpaces($code); 69 return $code; 70 });*/ 71 } 72 73 if ($origCode !== $code) { 74 file_put_contents($path, $code); 75 } 76} 77 78function stripTrailingWhitespace(string $code): string { 79 return preg_replace('/\h+$/m', '', $code); 80} 81 82function reindentToTabs(string $code): string { 83 return preg_replace_callback('/^ +/m', function(array $matches) { 84 $tabSize = 4; 85 $spaces = strlen($matches[0]); 86 $tabs = intdiv($spaces, $tabSize); 87 $spaces -= $tabs * $tabSize; 88 return str_repeat("\t", $tabs) . str_repeat(" ", $spaces); 89 }, $code); 90} 91 92function reindentToSpaces(string $code): string { 93 return preg_replace_callback('/^[ \t]+/m', function(array $matches) { 94 $tabSize = 4; 95 $indent = 0; 96 foreach (str_split($matches[0]) as $char) { 97 if ($char === ' ') { 98 $indent++; 99 } else { 100 $partialIndent = $indent % $tabSize; 101 if ($partialIndent === 0) { 102 $indent += $tabSize; 103 } else { 104 $indent += $tabSize - $partialIndent; 105 } 106 } 107 } 108 return str_repeat(" ", $indent); 109 }, $code); 110} 111 112function transformTestCode(string $code, callable $transformer): string { 113 // Don't transform whitespace-sensitive tests. 114 if (strpos($code, '--WHITESPACE_SENSITIVE--') !== false) { 115 return $code; 116 } 117 118 return preg_replace_callback( 119 '/(--FILE--)(.+?)(--[A-Z_]+--)/s', 120 function(array $matches) use($transformer) { 121 return $matches[1] . $transformer($matches[2]) . $matches[3]; 122 }, 123 $code 124 ); 125} 126 127function getLanguageFromExtension(string $ext): ?string { 128 switch ($ext) { 129 case 'c': 130 case 'h': 131 case 'cpp': 132 case 'y': 133 case 'l': 134 case 're': 135 return 'c'; 136 case 'php': 137 // TODO: Reformat .inc files. 138 //case 'inc': 139 return 'php'; 140 case 'phpt': 141 return 'phpt'; 142 default: 143 return null; 144 } 145} 146