WhitespaceShell.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. App::uses('AppShell', 'Console/Command');
  3. App::uses('Folder', 'Utility');
  4. class WhitespaceShell extends AppShell {
  5. public $autoCorrectAll = false;
  6. // each report: [0] => found, [1] => corrected
  7. public $report = ['leading' => [0, 0], 'trailing' => [0, 0]];
  8. /**
  9. * Whitespaces before or after
  10. *
  11. * @return void
  12. */
  13. public function find() {
  14. if (!empty($this->args[0])) {
  15. $folder = realpath($this->args[0]);
  16. } elseif ($this->params['plugin']) {
  17. $folder = CakePlugin::path(Inflector::classify($this->params['plugin']));
  18. } else {
  19. $folder = APP;
  20. }
  21. $App = new Folder($folder);
  22. $this->out("Checking *.php in " . $folder);
  23. $files = $App->findRecursive('.*\.php');
  24. $this->out('Found ' . count($files) . ' files.');
  25. $action = $this->in('Continue? [y]/[n]', ['y', 'n'], 'n');
  26. if ($action !== 'y') {
  27. return $this->error('Aborted');
  28. }
  29. $folders = [];
  30. foreach ($files as $file) {
  31. $error = '';
  32. $action = '';
  33. $this->out('Processing ' . $file, 1, Shell::VERBOSE);
  34. $c = file_get_contents($file);
  35. if (preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c)) {
  36. $error = 'leading';
  37. }
  38. if (preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', $c)) {
  39. $error = 'trailing';
  40. }
  41. if (empty($error)) {
  42. continue;
  43. }
  44. $this->report[$error][0]++;
  45. $this->out('');
  46. $this->out('contains ' . $error . ' whitespaces: ' . $this->shortPath($file));
  47. if (!$this->autoCorrectAll) {
  48. $dirname = dirname($file);
  49. if (in_array($dirname, $folders)) {
  50. $action = 'y';
  51. }
  52. while (empty($action)) {
  53. //TODO: [r]!
  54. $action = $this->in('Remove? [y]/[n], [a] for all in this folder, [r] for all below, [*] for all files(!), [q] to quit', ['y', 'n', 'r', 'a', 'q', '*'], 'q');
  55. }
  56. } else {
  57. $action = 'y';
  58. }
  59. if ($action === '*') {
  60. $action = 'y';
  61. $this->autoCorrectAll = true;
  62. } elseif ($action === 'a') {
  63. $action = 'y';
  64. $folders[] = $dirname;
  65. $this->out('All: ' . $dirname);
  66. }
  67. if ($action === 'q') {
  68. return $this->error('Abort... Done');
  69. }
  70. if ($action === 'y') {
  71. if ($error === 'leading') {
  72. $res = preg_replace('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', '<?php', $c);
  73. } else { //trailing
  74. $res = preg_replace('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', '?>', $c);
  75. }
  76. file_put_contents($file, $res);
  77. $this->report[$error][1]++;
  78. $this->out('fixed ' . $error . ' whitespaces: ' . $this->shortPath($file));
  79. }
  80. }
  81. // Report.
  82. $this->out('--------');
  83. $this->out('found ' . $this->report['leading'][0] . ' leading, ' . $this->report['trailing'][0] . ' trailing ws');
  84. $this->out('fixed ' . $this->report['leading'][1] . ' leading, ' . $this->report['trailing'][1] . ' trailing ws');
  85. }
  86. /**
  87. * Whitespaces at the end of the file
  88. *
  89. * @return void
  90. */
  91. public function eof() {
  92. if (!empty($this->args[0])) {
  93. $folder = realpath($this->args[0]);
  94. } else {
  95. $folder = APP;
  96. }
  97. $App = new Folder($folder);
  98. $this->out("Checking *.php in " . $folder);
  99. $files = $App->findRecursive('.*\.php');
  100. $this->out('Found ' . count($files) . ' files.');
  101. $action = $this->in('Continue? [y]/[n]', ['y', 'n'], 'n');
  102. if ($action !== 'y') {
  103. return $this->error('Aborted');
  104. }
  105. foreach ($files as $file) {
  106. $this->out('Processing ' . $file, 1, Shell::VERBOSE);
  107. $content = $store = file_get_contents($file);
  108. $newline = PHP_EOL;
  109. $x = substr_count($content, "\r\n");
  110. if ($x > 0) {
  111. $newline = "\r\n";
  112. } else {
  113. $newline = "\n";
  114. }
  115. // add one new line at the end
  116. $content = trim($content) . $newline;
  117. if ($content !== $store) {
  118. file_put_contents($file, $content);
  119. }
  120. }
  121. $this->out('Done');
  122. }
  123. /**
  124. * WhitespaceShell::getOptionParser()
  125. *
  126. * @return ConsoleOptionParser
  127. */
  128. public function getOptionParser() {
  129. $subcommandParser = [
  130. 'options' => [
  131. 'ext' => [
  132. 'short' => 'e',
  133. 'help' => 'Specify extensions [php|txt|...]',
  134. 'default' => '',
  135. ],
  136. 'dry-run' => [
  137. 'short' => 'd',
  138. 'help' => 'Dry run the clear command, no files will actually be deleted. Should be combined with verbose!',
  139. 'boolean' => true
  140. ],
  141. 'plugin' => [
  142. 'short' => 'p',
  143. 'help' => 'Plugin',
  144. 'default' => '',
  145. ],
  146. ]
  147. ];
  148. return parent::getOptionParser()
  149. ->description('The Whitespace Shell removes uncessary/wrong whitespaces.
  150. Either provide a path as first argument, use -p PluginName or run it as it is for the complete APP dir.')
  151. ->addSubcommand('find', [
  152. 'help' => 'Detect any leading/trailing whitespaces',
  153. 'parser' => $subcommandParser
  154. ])
  155. ->addSubcommand('eof', [
  156. 'help' => 'Fix whitespaces at the end of PHP files (a single newline as per coding standards)',
  157. 'parser' => $subcommandParser
  158. ]);
  159. }
  160. }