WhitespaceShell.php 4.6 KB

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