Skip to content

Commit

Permalink
NewWithBracesFixer - add no braces support
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmajor committed Jan 3, 2022
1 parent efeab76 commit 85c0c4d
Show file tree
Hide file tree
Showing 4 changed files with 461 additions and 20 deletions.
12 changes: 12 additions & 0 deletions doc/list.rst
Expand Up @@ -1176,6 +1176,18 @@ List of Available Rules

All instances created with new keyword must be followed by braces.

Configuration options:

- | ``named_class``
| Whether named classes should be followed by parentheses.
| Allowed types: ``bool``
| Default value: ``true``
- | ``anonymous_class``
| Whether anonymous classes should be followed by parentheses.
| Allowed types: ``bool``
| Default value: ``true``

Part of rule sets `@PSR12 <./ruleSets/PSR12.rst>`_ `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_

`Source PhpCsFixer\\Fixer\\Operator\\NewWithBracesFixer <./../src/Fixer/Operator/NewWithBracesFixer.php>`_
Expand Down
65 changes: 60 additions & 5 deletions doc/rules/operator/new_with_braces.rst
Expand Up @@ -4,29 +4,84 @@ Rule ``new_with_braces``

All instances created with new keyword must be followed by braces.

Configuration
-------------

``named_class``
~~~~~~~~~~~~~~~

Whether named classes should be followed by parentheses.

Allowed types: ``bool``

Default value: ``true``

``anonymous_class``
~~~~~~~~~~~~~~~~~~~

Whether anonymous classes should be followed by parentheses.

Allowed types: ``bool``

Default value: ``true``

Examples
--------

Example #1
~~~~~~~~~~

*Default* configuration.

.. code-block:: diff
--- Original
+++ New
-<?php $x = new X;
+<?php $x = new X();
<?php
-$x = new X;
-$y = new class {};
+$x = new X();
+$y = new class() {};
Example #2
~~~~~~~~~~

With configuration: ``['anonymous_class' => false]``.

.. code-block:: diff
--- Original
+++ New
<?php
-$y = new class() {};
+$y = new class {};
Example #3
~~~~~~~~~~

With configuration: ``['named_class' => false]``.

.. code-block:: diff
--- Original
+++ New
<?php
-$x = new X();
+$x = new X;
Rule sets
---------

The rule is part of the following rule sets:

@PSR12
Using the `@PSR12 <./../../ruleSets/PSR12.rst>`_ rule set will enable the ``new_with_braces`` rule.
Using the `@PSR12 <./../../ruleSets/PSR12.rst>`_ rule set will enable the ``new_with_braces`` rule with the default config.

@PhpCsFixer
Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``new_with_braces`` rule.
Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``new_with_braces`` rule with the default config.

@Symfony
Using the `@Symfony <./../../ruleSets/Symfony.rst>`_ rule set will enable the ``new_with_braces`` rule.
Using the `@Symfony <./../../ruleSets/Symfony.rst>`_ rule set will enable the ``new_with_braces`` rule with the default config.
79 changes: 68 additions & 11 deletions src/Fixer/Operator/NewWithBracesFixer.php
Expand Up @@ -15,6 +15,10 @@
namespace PhpCsFixer\Fixer\Operator;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
Expand All @@ -25,7 +29,7 @@
/**
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
*/
final class NewWithBracesFixer extends AbstractFixer
final class NewWithBracesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
/**
* {@inheritdoc}
Expand All @@ -34,7 +38,17 @@ public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All instances created with new keyword must be followed by braces.',
[new CodeSample("<?php \$x = new X;\n")]
[
new CodeSample("<?php\n\n\$x = new X;\n\$y = new class {};\n"),
new CodeSample(
"<?php\n\n\$y = new class() {};\n",
['anonymous_class' => false]
),
new CodeSample(
"<?php\n\n\$x = new X();\n",
['named_class' => false]
),
]
);
}

Expand Down Expand Up @@ -124,8 +138,12 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void

// new anonymous class definition
if ($nextToken->isGivenKind(T_CLASS)) {
if (!$tokens[$tokens->getNextMeaningfulToken($nextIndex)]->equals('(')) {
$this->insertBracesAfter($tokens, $nextIndex);
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);

if ($this->configuration['anonymous_class']) {
$this->ensureBracesAt($tokens, $nextIndex);
} else {
$this->ensureNoBracesAt($tokens, $nextIndex);
}

continue;
Expand All @@ -140,20 +158,59 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
// new statement has a gap in it - advance to the next token
if ($nextToken->isWhitespace()) {
$nextIndex = $tokens->getNextNonWhitespace($nextIndex);
$nextToken = $tokens[$nextIndex];
}

// new statement with () - nothing to do
if ($nextToken->equals('(') || $nextToken->isObjectOperator()) {
continue;
if ($this->configuration['named_class']) {
$this->ensureBracesAt($tokens, $nextIndex);
} else {
$this->ensureNoBracesAt($tokens, $nextIndex);
}
}
}

/**
* {@inheritdoc}
*/
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('named_class', 'Whether named classes should be followed by parentheses.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('anonymous_class', 'Whether anonymous classes should be followed by parentheses.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}

$this->insertBracesAfter($tokens, $tokens->getPrevMeaningfulToken($nextIndex));
private function ensureBracesAt(Tokens $tokens, int $index): void
{
$token = $tokens[$index];

if (!$token->equals('(') && !$token->isObjectOperator()) {
$tokens->insertAt(
$tokens->getPrevMeaningfulToken($index) + 1,
[new Token('('), new Token(')')]
);
}
}

private function insertBracesAfter(Tokens $tokens, int $index): void
private function ensureNoBracesAt(Tokens $tokens, int $index): void
{
$tokens->insertAt(++$index, [new Token('('), new Token(')')]);
if (!$tokens[$index]->equals('(')) {
return;
}

$closingIndex = $tokens->getNextMeaningfulToken($index);

// constructor has arguments - braces can not be removed
if (null === $closingIndex || !$tokens[$closingIndex]->equals(')')) {
return;
}

$tokens->clearAt($closingIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}

0 comments on commit 85c0c4d

Please sign in to comment.