Skip to content

Commit dd2ca62

Browse files
committed
feature #4275 Issue #4274: Let lowercase_constants directive to be configurable. (drupol)
This PR was squashed before being merged into the 2.16-dev branch (closes #4275). Discussion ---------- Issue #4274: Let lowercase_constants directive to be configurable. Fix #4274 Depends on #4362 This issue is to have the `lowercase_constants` directive configurable. (Issue #3026) The configurable option would let you choose in between 'upper' or 'lower' case syntax. Here's an example on how it would be: ```yaml lowercase_constants: case: upper ``` or ```yaml lowercase_constants: case: lower ``` The default would be `lower` to be backward compatible. Why this request ? CMS's like Drupal 8 are using uppercase constants (TRUE, FALSE, NULL) and I would like to have a proper tool that follows the Drupal 8 "standard" in our custom code at European Commission. #### The PHP version you are using (`$ php -v`): => PHP 7.2.14 #### PHP CS Fixer version you are using (`$ php-cs-fixer -V`): => PHP CS Fixer 2.14.1-DEV Sunrise by Fabien Potencier and Dariusz Ruminski #### The command you use to run PHP CS Fixer: => Via grumphp #### The configuration file you are using, if any: ```yaml array_syntax: syntax: short blank_line_after_opening_tag: true blank_line_before_statement: statements: - break - continue - declare - return - throw - try include: true # Drupalism: class_attributes_separation: false declare_equal_normalize: space: single declare_strict_types: true is_null: true lowercase_static_reference: true # Drupalism: lowercase_constants (unable to configure it in php-cs-fixer. Provide a patch?) native_function_invocation: false # Drupalism: no_blank_lines_after_class_opening: false no_closing_tag: true no_extra_blank_lines: tokens: - extra # Drupalism: - curly_brace_block - parenthesis_brace_block - square_brace_block - return - throw - use - use_trait no_trailing_whitespace_in_comment: true no_unneeded_final_method: true single_blank_line_at_eof: true single_line_after_imports: true strict_comparison: true strict_param: true trailing_comma_in_multiline_array: true yoda_style: equal: false identical: false less_and_greater: false always_move_variable: false ``` #### If applicable, please provide minimum samples of PHP code (as plain text, not screenshots): * before running PHP CS Fixer (no changes): ```php if ($var === null) { // Do something } ``` * with unexpected changes applied when running PHP CS Fixer: ```php if ($var === null) { // Do something } ``` * with the changes you expected instead: ```php if ($var === NULL) { // Do something } ``` Commits ------- bfefff8 Issue #4274: Let lowercase_constants directive to be configurable.
2 parents 15030c1 + bfefff8 commit dd2ca62

File tree

6 files changed

+357
-60
lines changed

6 files changed

+357
-60
lines changed

README.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,16 @@ Choose from the list of available rules:
421421
- ``spacing`` (``'none'``, ``'one'``): spacing to apply around concatenation operator;
422422
defaults to ``'none'``
423423

424+
* **constant_case** [@PSR2, @Symfony, @PhpCsFixer]
425+
426+
The PHP constants ``true``, ``false``, and ``null`` MUST be written using the
427+
correct casing.
428+
429+
Configuration options:
430+
431+
- ``case`` (``'lower'``, ``'upper'``): whether to use the ``upper`` or ``lower`` case
432+
syntax; defaults to ``'lower'``
433+
424434
* **date_time_immutable**
425435

426436
Class ``DateTimeImmutable`` should be used instead of ``DateTime``.
@@ -821,9 +831,10 @@ Choose from the list of available rules:
821831

822832
Cast should be written in lower case.
823833

824-
* **lowercase_constants** [@PSR2, @Symfony, @PhpCsFixer]
834+
* **lowercase_constants**
825835

826836
The PHP constants ``true``, ``false``, and ``null`` MUST be in lower case.
837+
DEPRECATED: use ``constant_case`` instead.
827838

828839
* **lowercase_keywords** [@PSR2, @Symfony, @PhpCsFixer]
829840

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
/*
4+
* This file is part of PHP CS Fixer.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
8+
*
9+
* This source file is subject to the MIT license that is bundled
10+
* with this source code in the file LICENSE.
11+
*/
12+
13+
namespace PhpCsFixer\Fixer\Casing;
14+
15+
use PhpCsFixer\AbstractFixer;
16+
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
17+
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
18+
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
19+
use PhpCsFixer\FixerDefinition\CodeSample;
20+
use PhpCsFixer\FixerDefinition\FixerDefinition;
21+
use PhpCsFixer\Tokenizer\CT;
22+
use PhpCsFixer\Tokenizer\Token;
23+
use PhpCsFixer\Tokenizer\Tokens;
24+
25+
/**
26+
* Fixer for constants case.
27+
*
28+
* @author Pol Dellaiera <pol.dellaiera@protonmail.com>
29+
*/
30+
final class ConstantCaseFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
31+
{
32+
/**
33+
* Hold the function that will be used to convert the constants.
34+
*
35+
* @var callable
36+
*/
37+
private $fixFunction;
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
public function configure(array $configuration = null)
43+
{
44+
parent::configure($configuration);
45+
46+
if ('lower' === $this->configuration['case']) {
47+
$this->fixFunction = static function ($token) {
48+
return strtolower($token);
49+
};
50+
}
51+
52+
if ('upper' === $this->configuration['case']) {
53+
$this->fixFunction = static function ($token) {
54+
return strtoupper($token);
55+
};
56+
}
57+
}
58+
59+
/**
60+
* {@inheritdoc}
61+
*/
62+
public function getDefinition()
63+
{
64+
return new FixerDefinition(
65+
'The PHP constants `true`, `false`, and `null` MUST be written using the correct casing.',
66+
[new CodeSample("<?php\n\$a = FALSE;\n\$b = True;\n\$c = nuLL;\n")]
67+
);
68+
}
69+
70+
/**
71+
* {@inheritdoc}
72+
*/
73+
public function isCandidate(Tokens $tokens)
74+
{
75+
return $tokens->isTokenKindFound(T_STRING);
76+
}
77+
78+
/**
79+
* {@inheritdoc}
80+
*/
81+
protected function createConfigurationDefinition()
82+
{
83+
return new FixerConfigurationResolver([
84+
(new FixerOptionBuilder('case', 'Whether to use the `upper` or `lower` case syntax.'))
85+
->setAllowedValues(['upper', 'lower'])
86+
->setDefault('lower')
87+
->getOption(),
88+
]);
89+
}
90+
91+
/**
92+
* {@inheritdoc}
93+
*/
94+
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
95+
{
96+
$fixFunction = $this->fixFunction;
97+
98+
foreach ($tokens as $index => $token) {
99+
if (!$token->isNativeConstant()) {
100+
continue;
101+
}
102+
103+
if (
104+
$this->isNeighbourAccepted($tokens, $tokens->getPrevMeaningfulToken($index)) &&
105+
$this->isNeighbourAccepted($tokens, $tokens->getNextMeaningfulToken($index))
106+
) {
107+
$tokens[$index] = new Token([$token->getId(), $fixFunction($token->getContent())]);
108+
}
109+
}
110+
}
111+
112+
/**
113+
* @param Tokens $tokens
114+
* @param int $index
115+
*
116+
* @return bool
117+
*/
118+
private function isNeighbourAccepted(Tokens $tokens, $index)
119+
{
120+
static $forbiddenTokens = [
121+
T_AS,
122+
T_CLASS,
123+
T_CONST,
124+
T_EXTENDS,
125+
T_IMPLEMENTS,
126+
T_INSTANCEOF,
127+
T_INSTEADOF,
128+
T_INTERFACE,
129+
T_NEW,
130+
T_NS_SEPARATOR,
131+
T_OBJECT_OPERATOR,
132+
T_PAAMAYIM_NEKUDOTAYIM,
133+
T_TRAIT,
134+
T_USE,
135+
CT::T_USE_TRAIT,
136+
CT::T_USE_LAMBDA,
137+
];
138+
139+
$token = $tokens[$index];
140+
141+
if ($token->equalsAny(['{', '}'])) {
142+
return false;
143+
}
144+
145+
return !$token->isGivenKind($forbiddenTokens);
146+
}
147+
}

src/Fixer/Casing/LowercaseConstantsFixer.php

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212

1313
namespace PhpCsFixer\Fixer\Casing;
1414

15-
use PhpCsFixer\AbstractFixer;
15+
use PhpCsFixer\AbstractProxyFixer;
16+
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
1617
use PhpCsFixer\FixerDefinition\CodeSample;
1718
use PhpCsFixer\FixerDefinition\FixerDefinition;
18-
use PhpCsFixer\Tokenizer\CT;
19-
use PhpCsFixer\Tokenizer\Token;
20-
use PhpCsFixer\Tokenizer\Tokens;
2119

2220
/**
2321
* Fixer for rules defined in PSR2 ¶2.5.
2422
*
2523
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
24+
*
25+
* @deprecated proxy to ConstantCaseFixer
2626
*/
27-
final class LowercaseConstantsFixer extends AbstractFixer
27+
final class LowercaseConstantsFixer extends AbstractProxyFixer implements DeprecatedFixerInterface
2828
{
2929
/**
3030
* {@inheritdoc}
@@ -38,65 +38,23 @@ public function getDefinition()
3838
}
3939

4040
/**
41-
* {@inheritdoc}
41+
* Returns names of fixers to use instead, if any.
42+
*
43+
* @return string[]
4244
*/
43-
public function isCandidate(Tokens $tokens)
45+
public function getSuccessorsNames()
4446
{
45-
return $tokens->isTokenKindFound(T_STRING);
47+
return array_keys($this->proxyFixers);
4648
}
4749

4850
/**
4951
* {@inheritdoc}
5052
*/
51-
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
52-
{
53-
foreach ($tokens as $index => $token) {
54-
if (!$token->isNativeConstant()) {
55-
continue;
56-
}
57-
58-
if (
59-
$this->isNeighbourAccepted($tokens, $tokens->getPrevMeaningfulToken($index)) &&
60-
$this->isNeighbourAccepted($tokens, $tokens->getNextMeaningfulToken($index))
61-
) {
62-
$tokens[$index] = new Token([$token->getId(), strtolower($token->getContent())]);
63-
}
64-
}
65-
}
66-
67-
/**
68-
* @param Tokens $tokens
69-
* @param int $index
70-
*
71-
* @return bool
72-
*/
73-
private function isNeighbourAccepted(Tokens $tokens, $index)
53+
protected function createProxyFixers()
7454
{
75-
static $forbiddenTokens = [
76-
T_AS,
77-
T_CLASS,
78-
T_CONST,
79-
T_EXTENDS,
80-
T_IMPLEMENTS,
81-
T_INSTANCEOF,
82-
T_INSTEADOF,
83-
T_INTERFACE,
84-
T_NEW,
85-
T_NS_SEPARATOR,
86-
T_OBJECT_OPERATOR,
87-
T_PAAMAYIM_NEKUDOTAYIM,
88-
T_TRAIT,
89-
T_USE,
90-
CT::T_USE_TRAIT,
91-
CT::T_USE_LAMBDA,
92-
];
93-
94-
$token = $tokens[$index];
95-
96-
if ($token->equalsAny(['{', '}'])) {
97-
return false;
98-
}
55+
$fixer = new ConstantCaseFixer();
56+
$fixer->configure(['case' => 'lower']);
9957

100-
return !$token->isGivenKind($forbiddenTokens);
58+
return [$fixer];
10159
}
10260
}

src/RuleSet.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ final class RuleSet implements RuleSetInterface
3535
'blank_line_after_namespace' => true,
3636
'braces' => true,
3737
'class_definition' => true,
38+
'constant_case' => true,
3839
'elseif' => true,
3940
'function_declaration' => true,
4041
'indentation_type' => true,
4142
'line_ending' => true,
42-
'lowercase_constants' => true,
4343
'lowercase_keywords' => true,
4444
'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
4545
'no_break_comment' => true,

0 commit comments

Comments
 (0)