浏览代码

Add whitespace shell

euromark 11 年之前
父节点
当前提交
0a844e74b8
共有 2 个文件被更改,包括 280 次插入0 次删除
  1. 198 0
      Console/Command/WhitespaceShell.php
  2. 82 0
      Test/TestCase/Console/Command/WhitespaceShellTest.php

+ 198 - 0
Console/Command/WhitespaceShell.php

@@ -0,0 +1,198 @@
+<?php
+namespace Tools\Console\Command;
+
+use Cake\Console\Shell;
+use Cake\Utility\Folder;
+use Cake\Utility\Inflector;
+use Cake\Core\Plugin;
+
+/**
+ * Shell to remove superfluous whitespace.
+ *
+ * @author Mark Scherer
+ * @license MIT
+ */
+class WhitespaceShell extends Shell {
+
+	/**
+	 * Each report: [0] => found, [1] => corrected
+	 *
+	 * @var array
+	 */
+	public $report = array(
+		'leading' => array(0, 0),
+		'trailing' => array(0, 0)
+	);
+
+	/**
+	 * Whitespaces before or after <?php and ?>.
+	 * The latter should be removed from PHP files by the way.
+	 *
+	 * @return void
+	 */
+	public function clean() {
+		if (!empty($this->args[0])) {
+			$folder = realpath($this->args[0]);
+		} elseif ($this->params['plugin']) {
+			$folder = Plugin::path(Inflector::classify($this->params['plugin']));
+		} else {
+			$folder = APP;
+		}
+		$App = new Folder($folder);
+		$this->out("Checking *.php in " . $folder);
+
+		$files = $App->findRecursive('.*\.php');
+		$this->out('Found ' . count($files) . ' files.');
+
+		$action = $this->in(__('Continue? [y]/[n]'), array('y', 'n'), 'n');
+		if ($action !== 'y') {
+			return $this->error('Aborted');
+		}
+
+		$folders = array();
+
+		foreach ($files as $file) {
+			$errors = array();
+			$action = '';
+			$this->out('Processing ' . $file, 1, Shell::VERBOSE);
+
+			$c = file_get_contents($file);
+			if (preg_match('/^[\n\r|\n|\r|\s]+\<\?php/', $c)) {
+				$errors[] = 'leading';
+			}
+			if (preg_match('/\?\>[\n\r|\n|\r|\s]+$/', $c)) {
+				$errors[] = 'trailing';
+			}
+
+			if (empty($errors)) {
+				continue;
+			}
+			foreach ($errors as $e) {
+				$this->report[$e][0]++;
+			}
+			$this->out('');
+			$this->out('contains ' . implode(' and ' , $errors) . ' whitespaces: ' . $this->shortPath($file));
+
+			$dirname = dirname($file);
+			if (in_array($dirname, $folders)) {
+				$action = 'y';
+			}
+
+			while (empty($action)) {
+				$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');
+			}
+
+			if ($action === '*') {
+				$action = 'y';
+
+			} elseif ($action === 'a') {
+				$action = 'y';
+				$folders[] = $dirname;
+				$this->out('All: ' . $dirname);
+			}
+
+			if ($action === 'q') {
+				return $this->error('Abort... Done');
+			}
+
+			if ($action === 'y') {
+				if (in_array('leading', $errors)) {
+					$c = preg_replace('/^\s+\<\?php/', '<?php', $c);
+				}
+				if (in_array('trailing', $errors)) {
+					$c = preg_replace('/\?\>\s+$/', '?>', $c);
+				}
+
+				file_put_contents($file, $c);
+				foreach ($errors as $e) {
+					$this->report[$e][1]++;
+				}
+				$this->out('fixed ' . implode(' and ' , $errors) . ' whitespaces: ' . $this->shortPath($file));
+			}
+		}
+
+		// Report.
+		$this->out('--------');
+		$this->out('found ' . $this->report['leading'][0] . ' leading, ' . $this->report['trailing'][0] . ' trailing ws');
+		$this->out('fixed ' . $this->report['leading'][1] . ' leading, ' . $this->report['trailing'][1] . ' trailing ws');
+	}
+
+	/**
+	 * Whitespaces at the end of the file
+	 *
+	 * @return void
+	 */
+	public function eof() {
+		if (!empty($this->args[0])) {
+			$folder = realpath($this->args[0]);
+		} else {
+			$folder = APP;
+		}
+		$App = new Folder($folder);
+		$this->out("Checking *.php in " . $folder);
+
+		$files = $App->findRecursive('.*\.php');
+
+		$this->out('Found ' . count($files) . ' files.');
+
+		$action = $this->in(__('Continue? [y]/[n]'), array('y', 'n'), 'n');
+		if ($action !== 'y') {
+			return $this->error('Aborted');
+		}
+
+		foreach ($files as $file) {
+			$this->out('Processing ' . $file, 1, Shell::VERBOSE);
+			$content = $store = file_get_contents($file);
+
+			$newline = PHP_EOL;
+			$x = substr_count($content, "\r\n");
+			if ($x > 0) {
+				$newline = "\r\n";
+			} else {
+				$newline = "\n";
+			}
+
+			// add one new line at the end
+			$content = trim($content) . $newline;
+			if ($content !== $store) {
+				file_put_contents($file, $content);
+			}
+		}
+		$this->out('Done');
+	}
+
+	public function getOptionParser() {
+		$subcommandParser = array(
+			'options' => array(
+				'ext' => array(
+					'short' => 'e',
+					'help' => __d('cake_console', 'Specify extensions [php|txt|...]'),
+					'default' => '',
+				),
+				'dry-run' => array(
+					'short' => 'd',
+					'help' => __d('cake_console', 'Dry run the clear command, no files will actually be deleted. Should be combined with verbose!'),
+					'boolean' => true
+				),
+				'plugin' => array(
+					'short' => 'p',
+					'help' => __d('cake_console', 'Plugin'),
+					'default' => '',
+				),
+			)
+		);
+
+		return parent::getOptionParser()
+			->description(__d('cake_console', 'The Whitespace Shell removes uncessary/wrong whitespaces.
+Either provide a path as first argument, use -p PluginName or run it as it is for the complete APP dir.'))
+			->addSubcommand('clean', array(
+				'help' => __d('cake_console', 'Detect and remove any leading/trailing whitespaces'),
+				'parser' => $subcommandParser
+			))
+			->addSubcommand('eof', array(
+				'help' => __d('cake_console', 'Fix whitespace issues at the end of PHP files (a single newline as per coding standards)'),
+				'parser' => $subcommandParser
+			));
+	}
+
+}

+ 82 - 0
Test/TestCase/Console/Command/WhitespaceShellTest.php

@@ -0,0 +1,82 @@
+<?php
+namespace Tools\Test\TestCase\Console\Command;
+
+use Tools\Console\Command\WhitespaceShell;
+use Cake\Console\ConsoleIo;
+use Cake\Console\ConsoleOutput;
+use Cake\Console\Shell;
+use Cake\Core\Plugin;
+use Cake\TestSuite\TestCase;
+
+/**
+ * Class TestCompletionStringOutput
+ *
+ */
+class TestWhitespaceOutput extends ConsoleOutput {
+
+	public $output = '';
+
+	protected function _write($message) {
+		$this->output .= $message;
+	}
+
+}
+
+/**
+ */
+class WhitespaceShellTest extends TestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ */
+	public function setUp() {
+		parent::setUp();
+
+		$this->out = new TestWhitespaceOutput();
+		$io = new ConsoleIo($this->out);
+
+		$this->Shell = $this->getMock(
+			'Tools\Console\Command\WhitespaceShell',
+			['in', 'err', '_stop'],
+			[$io]
+		);
+	}
+
+/**
+ * tearDown
+ *
+ * @return void
+ */
+	public function tearDown() {
+		parent::tearDown();
+		unset($this->Shell);
+	}
+
+/**
+ * test that the startup method supresses the shell header
+ *
+ * @return void
+ */
+	public function testClean() {
+		$this->Shell->expects($this->any())->method('in')
+			->will($this->returnValue('y'));
+
+		$content = PHP_EOL . ' <?php echo $foo;' . PHP_EOL . '?> ' . PHP_EOL . PHP_EOL;
+		file_put_contents(TMP . 'Foo.php', $content);
+		$this->Shell->runCommand(['clean', TMP]);
+		$output = $this->out->output;
+
+		$this->assertTextContains('Found 1 files.', $output);
+		$this->assertTextContains('found 1 leading, 1 trailing ws', $output);
+		$this->assertTextContains('fixed 1 leading, 1 trailing ws', $output);
+
+		$output = file_get_contents(TMP . 'Foo.php');
+		$expected = '<?php echo $foo;' . PHP_EOL . '?>';
+
+		unlink(TMP . 'Foo.php');
+		$this->assertEquals($expected, $output);
+	}
+
+}