In the last post Brief History of Tools Watching and Changing Your PHP Code we saw there are over dozen tools in PHP that can modify code. So there is no surprise coding standard tools are "upgrading" code from PHP 5.6 to PHP 7.2 without knowing types and that AST is moving false
to !
.
Should coding standard upgrade your code? Should AST make your code cleaner? Should AST take of coding standard changes? Which is born for it?
PHP CS Fixer can upgrade to few features of new PHP using just token_get_all()
:
<p><a href="https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/03e13fb91c775a151dc57ae51e80ba3f2abe7da6/src/RuleSet.php#L209-L240"><code>RuleSet.php</code></a></p>
Rector can solve rather low-level changes in code quality level:
-if (! $this->isTrue($condition) === false) {
+if ($this->isTrue($condition)) {
-count(func_get_args()) === 1);
+func_num_args() === 1
It's the most favorite level in Rector. Why?
-foreach ($this->oldToNewFunctions as $oldFunction => $newFunction) {
- if ($currentFunction === $oldFunction) {
- return $newFunction;
- }
-}
-
-return null;
+return $this->oldToNewFunctions[$currentFunction] ?? null;
Huge thanks to Gabriel Caruso, who brought this idea to Rector and helped me to shift my view to the one I'll show you below.
If there would be no AST, this all could be handled by token_get_all
(like PHP_CodeSniffer and PHP CS Fixer/), but such implementation needs to be lot longer to achieve similar quality, since you have to check every previous and next tokens for any unexpected values.
"I really don't like programming. I built this tool to program less so that I could just reuse code."
We're here at the moment:
That's very narrow and old-school, but the shift has already begun...
spacing and exact positions
-if ($condition )
-{
+if ($condition) {
sign changes
-$items = array(1, 2, 3;);
+$items = [1, 2, 3];
doc block changes
/**
-* @param int|string
+* @param int|string $id
*/
logic and structure changes
-if (! $this->isTrue($condition) === false) {
+if ($this->isTrue($condition)) {
code cleanup
-$value = $value;
context-aware names
-$formBuilder->add('name', new TextType);
+$formBuilder->add('name', TextType::class);
Let's take this case of useless variable:
function () {
- $a = true;
- return $a;
+ return true;
};
My first thought was: "Why is it assigned, is there some magic behind this? I need to explore more." Well, there isn't - it's a trap. Both for the programmer and for PHP to interpret it.
So this change will not only make your code more readable but also faster. A nice side effect, right?
Let's briefly compare how tokens and AST approach this:
Tokens | AST |
---|---|
PHP_CodeSniffer | Rector |
UselessVariableSniff
|
SimplifyUselessVariableRector
|
329 lines | 120 lines |
2 helper services | 1 helper service |
Note that it would be very difficult to write both versions shorter and keep reliability high and I believe kukulich is very good at implementing Sniffs effectively. It's a matter of used technology, not implementation skill.
To sum up, the AST version takes only 36,47 % of code what token version.
Also, AST implementation also solved this case without no extra work:
function test() {
$a = 1;
$b = 1;
- $c = [
+ return [
$b-- => $a++,
--$b => ++$a,
];
- return $c;
}
I still imagine how PHP would look like today if we had AST in 2012 when Fabien started PHP CS Fixer.
Would you be interested in such AST rules for coding standard?
What rules would you add if it would be easier to create them with AST?
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!