mirror of
https://github.com/elyby/php-code-style.git
synced 2025-05-31 14:12:05 +05:30
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
0d20175b0a | |||
4b480df97d | |||
50c7deed65 | |||
31661cd142 | |||
8ee5979288 | |||
2480f7ba46 | |||
824cf6c1f1 | |||
fa40a02dfd | |||
a7bef227df | |||
59e9994662 | |||
76bd14c167 | |||
076c2f1c6c | |||
04e3690d06 |
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@ -0,0 +1,35 @@
|
||||
language: php
|
||||
php:
|
||||
- '7.0'
|
||||
- '7.1'
|
||||
- '7.2'
|
||||
- nightly
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- $HOME/.composer
|
||||
|
||||
env:
|
||||
global:
|
||||
- DEFAULT_COMPOSER_FLAGS="--optimize-autoloader --no-interaction --no-progress"
|
||||
|
||||
before_script:
|
||||
- composer global show hirak/prestissimo -q || travis_retry composer global require $DEFAULT_COMPOSER_FLAGS hirak/prestissimo
|
||||
- composer install --no-interaction
|
||||
|
||||
stages:
|
||||
- Static Code Analysis
|
||||
- Test
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: Static Code Analysis
|
||||
php: 7.2
|
||||
script:
|
||||
- vendor/bin/php-cs-fixer fix -v --dry-run
|
||||
- stage: Test
|
||||
script:
|
||||
- vendor/bin/phpunit
|
||||
allow_failures:
|
||||
- php: nightly
|
28
CHANGELOG.md
Normal file
28
CHANGELOG.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0] - 2018-08-08
|
||||
### Added
|
||||
- Enh #4: `Ely\remove_class_name_method_usages` fixer.
|
||||
- `array_syntax` fixer.
|
||||
- This changelog file.
|
||||
- Travis CI building.
|
||||
|
||||
### Changed
|
||||
- `friendsofphp/php-cs-fixer` version bumped to `^2.12.2`.
|
||||
- `phpunit/phpunit` downgraded to `^6.5.1` to be compatible with PHP 7.0.
|
||||
|
||||
### Fixed
|
||||
- Bug #5: `Ely/line_break_after_statements` triggers error if statement doesn't have control brackets.
|
||||
|
||||
## 0.1.0 - 2018-04-17
|
||||
### Added
|
||||
- First release
|
||||
|
||||
[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/0.2.0...HEAD
|
||||
[0.2.0]: https://github.com/elyby/php-code-style/compare/0.1.0...0.2.0
|
35
README.md
35
README.md
@ -3,6 +3,11 @@
|
||||
Set of PHP-CS-Fixer rules used in development of Ely.by PHP projects. It's suited for PHP 7.1 and above.
|
||||
You can use it as a ready-made set of rules or [just some of them](#using-our-fixers).
|
||||
|
||||
[![Latest Version on Packagist][ico-version]][link-packagist]
|
||||
[![Total Downloads][ico-downloads]][link-downloads]
|
||||
[![Software License][ico-license]](LICENSE.md)
|
||||
[![Build Status][ico-build-status]][link-build-status]
|
||||
|
||||
## Installation
|
||||
|
||||
First of all install Ely.by PHP-CS-Fixer rules via composer with
|
||||
@ -12,7 +17,7 @@ First of all install Ely.by PHP-CS-Fixer rules via composer with
|
||||
composer require --dev friendsofphp/php-cs-fixer ely/php-code-style
|
||||
```
|
||||
|
||||
Then create file `.php-cs` with following contents:
|
||||
Then create file `.php_cs` with following contents:
|
||||
|
||||
```php
|
||||
<?php
|
||||
@ -189,7 +194,7 @@ Ensure that a class body contains one blank line after its definition and before
|
||||
**Configuration:**
|
||||
|
||||
* `apply_to_anonymous_classes` - should this fixer be applied to anonymous classes? If it is set to `false`, than
|
||||
anonymous classes will be fixed to don't have empty lines around body. **Default**: `false`.
|
||||
anonymous classes will be fixed to don't have empty lines around body. **Default**: `true`.
|
||||
|
||||
* `blank_lines_count` - adjusts an amount of the blank lines. **Default**: `1`.
|
||||
|
||||
@ -261,3 +266,29 @@ an anonymous class declaration in a case when said class constructor doesn't con
|
||||
|
||||
* `remove_for_anonymous_classes` - if its set to `true`, then braces for anonymous classes without constructor
|
||||
arguments will be removed. **Default**: `false`.
|
||||
|
||||
### `Ely/remove_class_name_method_usages` (Yii2)
|
||||
|
||||
Replaces Yii2 [`BaseObject::className()`](https://github.com/yiisoft/yii2/blob/e53fc0ded1/framework/base/BaseObject.php#L84)
|
||||
usages with native `::class` keyword, introduced in PHP 5.5.
|
||||
|
||||
```diff
|
||||
--- Original
|
||||
+++ New
|
||||
@@ @@
|
||||
<?php
|
||||
use common\models\User;
|
||||
|
||||
- $className = User::className();
|
||||
+ $className = User::class;
|
||||
```
|
||||
|
||||
[ico-version]: https://img.shields.io/packagist/v/ely/php-code-style.svg?style=flat-square
|
||||
[ico-license]: https://img.shields.io/badge/license-Apache-green.svg?style=flat-square
|
||||
[ico-downloads]: https://img.shields.io/packagist/dt/ely/php-code-style.svg?style=flat-square
|
||||
[ico-build-status]: https://img.shields.io/travis/elyby/php-code-style/master.svg?style=flat-square
|
||||
|
||||
[link-packagist]: https://packagist.org/packages/ely/php-code-style
|
||||
[link-contributors]: ../../contributors
|
||||
[link-downloads]: https://packagist.org/packages/ely/php-code-style/stats
|
||||
[link-build-status]: https://travis-ci.org/elyby/php-code-style
|
||||
|
@ -17,10 +17,10 @@
|
||||
"type": "library",
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.11"
|
||||
"friendsofphp/php-cs-fixer": "^2.12.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^6.5.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -30,9 +30,11 @@
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Ely\\CS\\Test\\": "tests/"
|
||||
},
|
||||
"files": [
|
||||
"vendor/friendsofphp/php-cs-fixer/tests/Test/Constraint/SameStringsConstraint.php"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
560
composer.lock
generated
560
composer.lock
generated
File diff suppressed because it is too large
Load Diff
123
src/Fixer/LanguageConstruct/RemoveClassNameMethodUsagesFixer.php
Normal file
123
src/Fixer/LanguageConstruct/RemoveClassNameMethodUsagesFixer.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\LanguageConstruct;
|
||||
|
||||
use Ely\CS\Fixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* Replaces Yii2 BaseObject::className() usages with native ::class keyword, introduced in PHP 5.5.
|
||||
*
|
||||
* @author ErickSkrauch <erickskrauch@ely.by>
|
||||
*/
|
||||
final class RemoveClassNameMethodUsagesFixer extends AbstractFixer {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getDefinition() {
|
||||
return new FixerDefinition(
|
||||
'Converts Yii2 `BaseObject::className()` method usage into `::class` keyword.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$className = Baz::className();
|
||||
'
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens) {
|
||||
return $tokens->isTokenKindFound(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens) {
|
||||
for ($index = $tokens->count() - 4; $index > 0; --$index) {
|
||||
$candidate = $this->getReplaceCandidate($tokens, $index);
|
||||
if ($candidate === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fixClassNameMethodUsage(
|
||||
$tokens,
|
||||
$index,
|
||||
$candidate[0], // brace open
|
||||
$candidate[1] // brace close
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tokens $tokens
|
||||
* @param int $index
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
private function getReplaceCandidate(Tokens $tokens, $index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_STRING)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
||||
if (!$tokens[$braceOpenIndex]->equals('(')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
|
||||
if (!$tokens[$braceCloseIndex]->equals(')')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$doubleColon = $tokens->getPrevMeaningfulToken($index);
|
||||
if (!$tokens[$doubleColon]->isGivenKind([T_DOUBLE_COLON])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = $tokens[$index]->getContent();
|
||||
if ($methodName !== 'className') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
$braceOpenIndex,
|
||||
$braceCloseIndex,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tokens $tokens
|
||||
* @param int $index
|
||||
* @param int $braceOpenIndex
|
||||
* @param int $braceCloseIndex
|
||||
*/
|
||||
private function fixClassNameMethodUsage(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceCloseIndex);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceOpenIndex);
|
||||
$tokens->clearAt($index);
|
||||
$tokens->insertAt($index, new Token([CT::T_CLASS_CONSTANT, 'class']));
|
||||
}
|
||||
|
||||
}
|
@ -86,6 +86,14 @@ class Foo
|
||||
return $tokens->isAnyTokenKindsFound(self::STATEMENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPriority() {
|
||||
// for the best result should be run after the BracesFixer
|
||||
return -26;
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens) {
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (!$token->isGivenKind(self::STATEMENTS)) {
|
||||
@ -126,17 +134,23 @@ class Foo
|
||||
|
||||
if ($nextToken->equals('(')) {
|
||||
$parenthesisEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
|
||||
$possibleStartBraceIndex = $tokens->getNextNonWhitespace($parenthesisEndIndex);
|
||||
$possibleBeginBraceIndex = $tokens->getNextNonWhitespace($parenthesisEndIndex);
|
||||
} else {
|
||||
$possibleStartBraceIndex = $nextIndex;
|
||||
$possibleBeginBraceIndex = $nextIndex;
|
||||
}
|
||||
|
||||
// `do {} while ();`
|
||||
if ($tokens[$index]->isGivenKind(T_WHILE) && $tokens[$possibleStartBraceIndex]->equals(';')) {
|
||||
return $possibleStartBraceIndex;
|
||||
if ($tokens[$index]->isGivenKind(T_WHILE) && $tokens[$possibleBeginBraceIndex]->equals(';')) {
|
||||
return $possibleBeginBraceIndex;
|
||||
}
|
||||
|
||||
$possibleBeginBrace = $tokens[$possibleBeginBraceIndex];
|
||||
if ($possibleBeginBrace->equals('{')) {
|
||||
$blockEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $possibleBeginBraceIndex);
|
||||
} else {
|
||||
$blockEnd = $tokens->getNextTokenOfKind($possibleBeginBraceIndex, [';']);
|
||||
}
|
||||
|
||||
$blockEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $possibleStartBraceIndex);
|
||||
$nextStatementIndex = $tokens->getNextMeaningfulToken($blockEnd);
|
||||
if ($nextStatementIndex === null) {
|
||||
return $blockEnd;
|
||||
|
@ -7,6 +7,9 @@ class Rules {
|
||||
|
||||
private static $rules = [
|
||||
'@PSR2' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'binary_operator_spaces' => true,
|
||||
'braces' => [
|
||||
'position_after_functions_and_oop_constructs' => 'same',
|
||||
@ -65,6 +68,7 @@ class Rules {
|
||||
'Ely/new_with_braces' => [
|
||||
'remove_for_anonymous_classes' => true,
|
||||
],
|
||||
'Ely/remove_class_name_method_usages' => true,
|
||||
];
|
||||
|
||||
public static function create(array $overwrittenRules = []): array {
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Test\Fixer\Operator;
|
||||
|
||||
use Ely\CS\Fixer\LanguageConstruct\RemoveClassNameMethodUsagesFixer;
|
||||
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
|
||||
|
||||
/**
|
||||
* @covers \Ely\CS\Fixer\LanguageConstruct\RemoveClassNameMethodUsagesFixer
|
||||
*/
|
||||
class RemoveClassNameMethodUsagesFixerTest extends AbstractFixerTestCase {
|
||||
|
||||
/**
|
||||
* @param string $expected
|
||||
* @param null|string $input
|
||||
*
|
||||
* @dataProvider provideFixCases
|
||||
*/
|
||||
public function testFix($expected, $input = null) {
|
||||
$this->doTest($expected, $input);
|
||||
}
|
||||
|
||||
public function provideFixCases() {
|
||||
return [
|
||||
[
|
||||
'<?php echo className();',
|
||||
],
|
||||
[
|
||||
'<?php
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$exceptionString = Baz::classname();
|
||||
',
|
||||
],
|
||||
[
|
||||
'<?php
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$className = Baz::class;
|
||||
',
|
||||
'<?php
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$className = Baz::className();
|
||||
',
|
||||
],
|
||||
[
|
||||
'<?php
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$exceptionString = "The class should be instance of " . Baz::class . " and nothing else";
|
||||
',
|
||||
'<?php
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$exceptionString = "The class should be instance of " . Baz::className() . " and nothing else";
|
||||
',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function createFixer() {
|
||||
return new RemoveClassNameMethodUsagesFixer();
|
||||
}
|
||||
|
||||
}
|
@ -355,10 +355,211 @@ class Foo
|
||||
'<?php
|
||||
$a = "prev statement";
|
||||
foreach ($coordinates as $coordinate) {
|
||||
[$x, $y] = explode(\',\', $coordinate);
|
||||
$points = explode(",", $coordinate);
|
||||
}
|
||||
',
|
||||
];
|
||||
// Issue 5
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->bar();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->bar();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->bar();
|
||||
else
|
||||
$this->baz();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->bar();
|
||||
else
|
||||
$this->baz();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
for ($i = 0; $i < 3; $i++)
|
||||
$this->bar();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
for ($i = 0; $i < 3; $i++)
|
||||
$this->bar();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
foreach (["foo", "bar"] as $str)
|
||||
$this->bar();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
foreach (["foo", "bar"] as $str)
|
||||
$this->bar();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
while ($i < 10)
|
||||
$this->bar();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
while ($i < 10)
|
||||
$this->bar();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
do
|
||||
$this->bar();
|
||||
while ($i < 10);
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function foo()
|
||||
{
|
||||
do
|
||||
$this->bar();
|
||||
while ($i < 10);
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function bar()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->foo();
|
||||
else if ("a" === "c")
|
||||
$this->bar();
|
||||
else if ("a" === "d")
|
||||
$this->baz();
|
||||
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function bar()
|
||||
{
|
||||
if ("a" === "b")
|
||||
$this->foo();
|
||||
else if ("a" === "c")
|
||||
$this->bar();
|
||||
else if ("a" === "d")
|
||||
$this->baz();
|
||||
$a = "next statement";
|
||||
}
|
||||
}',
|
||||
];
|
||||
$cases[] = [
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function bar()
|
||||
{
|
||||
foreach (["foo", "bar"] as $str)
|
||||
if ($str === "foo")
|
||||
$this->bar();
|
||||
|
||||
return 3;
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
public function bar()
|
||||
{
|
||||
foreach (["foo", "bar"] as $str)
|
||||
if ($str === "foo")
|
||||
$this->bar();
|
||||
return 3;
|
||||
}
|
||||
}',
|
||||
];
|
||||
|
||||
return $cases;
|
||||
}
|
||||
|
Reference in New Issue
Block a user