From 26d3fecc51ac3406fcb1d1a63239fe47171e5545 Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Thu, 30 Aug 2018 11:36:21 +0100 Subject: [PATCH] Start with PSR-1 (#1) * Start with PSR-2 * Set up Travis * Enable short open tags * Not working * Try lowest dependency versions * Try and run separate tasks * XDebug might not be installed * Not sure what happened there * And again * Check file names * Method case * Constant names * Namespaces * Namespace/use blank lines * Property visibility * Method visibility * Start trying to make sure code is actually valid * Invalid PHP * Braces * PHP casing * Back to PSR-1 * More details * Show PHP_CodeSniffer progress * Better name * Use sniff names * Lowest working version for short tags * Match sniff name * Missing one-per-file tests --- .gitattributes | 2 + .gitignore | 5 + .travis.yml | 42 ++++++++ .travis/php.ini | 1 + README.md | 33 +++++++ composer.json | 30 ++++++ phpcs.xml.dist | 21 ++++ phpunit.xml.dist | 23 +++++ src/Libero/ruleset.xml | 12 +++ tests/RulesetTests.php | 142 +++++++++++++++++++++++++++ tests/bootstrap.php | 6 ++ tests/cases/classes/case | 16 +++ tests/cases/classes/constant-name | 17 ++++ tests/cases/classes/file-name | 16 +++ tests/cases/classes/method-case | 19 ++++ tests/cases/classes/namespace | 14 +++ tests/cases/classes/one-per-file | 21 ++++ tests/cases/interfaces/case | 16 +++ tests/cases/interfaces/constant-name | 17 ++++ tests/cases/interfaces/file-name | 16 +++ tests/cases/interfaces/method-case | 17 ++++ tests/cases/interfaces/namespace | 14 +++ tests/cases/interfaces/one-per-file | 21 ++++ tests/cases/php/short-tag | 13 +++ tests/cases/php/side-effects | 14 +++ tests/cases/trait/case | 16 +++ tests/cases/trait/method-case | 19 ++++ tests/cases/trait/namespace | 14 +++ tests/cases/trait/one-per-file | 20 ++++ 29 files changed, 617 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 .travis/php.ini create mode 100644 composer.json create mode 100644 phpcs.xml.dist create mode 100644 phpunit.xml.dist create mode 100644 src/Libero/ruleset.xml create mode 100644 tests/RulesetTests.php create mode 100644 tests/bootstrap.php create mode 100644 tests/cases/classes/case create mode 100644 tests/cases/classes/constant-name create mode 100644 tests/cases/classes/file-name create mode 100644 tests/cases/classes/method-case create mode 100644 tests/cases/classes/namespace create mode 100644 tests/cases/classes/one-per-file create mode 100644 tests/cases/interfaces/case create mode 100644 tests/cases/interfaces/constant-name create mode 100644 tests/cases/interfaces/file-name create mode 100644 tests/cases/interfaces/method-case create mode 100644 tests/cases/interfaces/namespace create mode 100644 tests/cases/interfaces/one-per-file create mode 100644 tests/cases/php/short-tag create mode 100644 tests/cases/php/side-effects create mode 100644 tests/cases/trait/case create mode 100644 tests/cases/trait/method-case create mode 100644 tests/cases/trait/namespace create mode 100644 tests/cases/trait/one-per-file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6e65161 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/*.* export-ignore +/tests export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d21911 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/.phpcs-cache +/composer.lock +/phpcs.xml +/phpunit.xml +/vendor/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0d7b95c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +sudo: false + +language: php + +php: + - 7.2 + - nightly + +before_install: + - phpenv config-rm xdebug.ini || true + - phpenv config-add .travis/php.ini + +install: + - travis_retry composer install --classmap-authoritative --no-suggest --prefer-dist + +script: + - vendor/bin/phpunit + +jobs: + include: + + - stage: Test + env: DEPENDENCIES=low + php: 7.2 + install: + - travis_retry composer update --classmap-authoritative --no-suggest --prefer-dist --prefer-lowest --prefer-stable + + - stage: Code Quality + env: CODING_STANDARDS + script: + - vendor/bin/phpcs -p + +allow_failures: + - php: nightly + +cache: + directories: + - $HOME/.composer/cache/files + +branches: + only: + - master diff --git a/.travis/php.ini b/.travis/php.ini new file mode 100644 index 0000000..c9ce25c --- /dev/null +++ b/.travis/php.ini @@ -0,0 +1 @@ +short_open_tag = On diff --git a/README.md b/README.md index fca1257..bc7ee82 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ Libero PHP coding standard ========================== + +[![Build Status](https://travis-ci.com/libero/php-coding-standard.svg?branch=master)](https://travis-ci.com/libero/php-coding-standard) + +The Libero PHP coding standard is a set of [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) rules applied to all Libero PHP projects. It is based on [PSR-1](https://www.php-fig.org/psr/psr-1/). + +Getting started +--------------- + +Using [Composer](https://getcomposer.org/) you can install the coding standard into your project: + +``` +composer require --dev libero/coding-standard +``` + +You can then find violations of the standard by running: + +``` +vendor/bin/phpcs --standard=Libero /path/to/some/file/to/sniff.php +``` + +Or automatically correct (at least some of) these violations by running: + +``` +vendor/bin/phpcbf --standard=Libero /path/to/some/file/to/sniff.php +``` + +See the [PHP_CodeSniffer documentation](https://github.com/squizlabs/PHP_CodeSniffer/wiki) for more details. + +Getting help +------------ + +- Report a bug or request a feature on [GitHub](https://github.com/libero/libero/issues/new/choose). +- Ask a question on the [Libero Community Slack](https://libero-community.slack.com/). diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..97da514 --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "libero/coding-standard", + "description": "Libero PHP coding standard", + "type": "phpcodesniffer-standard", + "license": "MIT", + "autoload-dev": { + "psr-4": { + "tests\\Libero\\CodingStandard\\": "tests/" + } + }, + "require": { + "php": "^7.2", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "ext-tokenizer": "*", + "lstrojny/functional-php": "^1.8", + "phpunit/phpunit": "^7.3", + "symfony/finder": "^4.1" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..73de171 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,21 @@ + + + + + + + + + + + + + src/ + tests/ + + + */tests/* + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..edbd815 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + + + + + + + tests + + + + + + src + + + + diff --git a/src/Libero/ruleset.xml b/src/Libero/ruleset.xml new file mode 100644 index 0000000..5bc3697 --- /dev/null +++ b/src/Libero/ruleset.xml @@ -0,0 +1,12 @@ + + + + + The Libero coding standard. + + + + + + diff --git a/tests/RulesetTests.php b/tests/RulesetTests.php new file mode 100644 index 0000000..7999f20 --- /dev/null +++ b/tests/RulesetTests.php @@ -0,0 +1,142 @@ +config = new Config(['--standard=Libero']); + self::$codeSniffer->init(); + } + + /** + * @test + * @dataProvider cases + */ + public function it_finds_and_fixes_violations( + string $filename, + string $contents, + string $fixed, + array $messages, + ?string $description + ) : void { + $file = $this->createFile($filename, $contents); + $actual = flatten($this->getMessages($file)); + + sort($actual); + sort($messages); + + $this->assertSame($messages, $actual, $description); + $this->assertSame($fixed, $file->fixer->getContents()); + } + + public function cases() : iterable + { + $files = Finder::create()->files()->in(__DIR__.'/cases'); + + foreach ($files as $file) { + preg_match_all('~(?:---)?([A-Z]+)---\s+([\s\S]+?)\n---~', $file->getContents(), $matches); + + $parts = array_combine(array_map('strtolower', $matches[1]), $matches[2]); + + if (isset($parts['messages'])) { + $parts['messages'] = array_filter(explode("\n", $parts['messages'])); + } + + if (empty($parts['contents'])) { + throw new LogicException("Couldn't find contents in {$file->getRelativePathname()}"); + } elseif (empty($parts['fixed']) && empty($parts['messages'])) { + throw new LogicException("Expected one of fixed or messages in {$file->getRelativePathname()}"); + } + + try { + token_get_all($parts['contents'], TOKEN_PARSE); + } catch (ParseError $exception) { + $message = "Failed to parse content in {$file->getRelativePathname()}: {$exception->getMessage()}"; + throw new LogicException($message, 0, $exception); + } + + if (!empty($parts['fixed'])) { + try { + token_get_all($parts['fixed'], TOKEN_PARSE); + } catch (ParseError $exception) { + $message = "Failed to parse fixed in {$file->getRelativePathname()}: {$exception->getMessage()}"; + throw new LogicException($message, 0, $exception); + } + } + + yield $file->getRelativePathname() => [ + $parts['filename'] ?? 'test.php', + $parts['contents'], + $parts['fixed'] ?? $parts['contents'], + $parts['messages'] ?? [], + $parts['description'] ?? null, + ]; + } + } + + private function createFile(string $filename, string $content) : File + { + if (!ini_get('short_open_tag') && false === strpos($content, 'markTestSkipped('short_open_tag option is disabled'); + } + + $file = new DummyFile( + "phpcs_input_file:${filename}\n{$content}", + self::$codeSniffer->ruleset, + self::$codeSniffer->config + ); + + $file->process(); + + $file->fixer->fixFile(); + + $file = new DummyFile( + "phpcs_input_file:${filename}\n{$file->fixer->getContents()}", + self::$codeSniffer->ruleset, + self::$codeSniffer->config + ); + + $file->process(); + + return $file; + } + + private function getMessages(File $file) : iterable + { + foreach ([$file->getErrors(), $file->getWarnings()] as $messages) { + foreach ($messages as $line => $lineMessages) { + foreach ($lineMessages as $column => $columnMessages) { + foreach ($columnMessages as $data) { + yield "{$line}:{$column} {$data['source']}"; + } + } + } + } + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..007ba25 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,6 @@ +