WordPress / WordPress-Coding-Standards

PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions
MIT License
2.56k stars 487 forks source link

Use a PHPCS bootstrap file to auto-detect text-domain and minimum supported wp version #995

Open jrfnl opened 7 years ago

jrfnl commented 7 years ago

The I18n sniff needs a text_domain property to be set in a custom ruleset to trigger the text-domain checks. The DeprecatedFunctions, DeprecatedClasses and the upcoming DeprecatedParameters (#826) sniffs need a minimum_supported_version property.

When the sniffs are used on plugins and themes, this information is typically already available in the theme/plugin headers in style.css/ readme.txt or the main plugin file and - at least for the minimum_supported_version a pain to keep in sync.

Since PHPCS 2.5.0 there appears to be a - rarely used - option to bootstrap a file to be run between when the sniffs are loaded and when they are actually run. See:

I would like to suggest we explore the possibility of auto-detecting the above mentioned properties using a bootstrap file.

Some thoughts:

I suggest this to be added to the Extra ruleset as these sniffs are typically contained therein.

Opinions ?

/cc @grappler This principle may also be useful for the theme sniffs.

grappler commented 7 years ago

I have started playing around and it works. This is the bit of code needed to fetch the text domain.

One issue that I came across is if only PHP is defined in extensions then the style.css file will not be loaded in the list of files. The same problem would be if TXT files would not be included.

<?php
$my_files = $phpcs->getFilesToProcess($values['files'], $values['local']);

foreach ( $my_files as $file ) {
    if ( false === strpos( $file, 'style.css' ) ) {
        continue;
    }
    $contents = file_get_contents($file);
    if (preg_match('/^[ \t\/*#@]*' . preg_quote('Text Domain', '/') . ':(.*)$/mi', $contents, $match)
    && $match[1]) {
        PHP_CodeSniffer::setConfigData( 'text_domain', cleanHeaderComment($match[1]) );
    }
}

function cleanHeaderComment($content)
{
    return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $content));
}
jrfnl commented 7 years ago

@grappler Interesting. I would expect not to have access to the $phpcs object from within the bootstrap file. Where did you place/run this code snippet in the flow of things ?

grappler commented 7 years ago

Where did you place/run this code snippet in the flow of things ?

This code would be placed in bootstrap.php which is loaded via phpcs --bootstrap=bootstrap.php.

The file is included in PHP_CodeSniffer_CLI::process() in line 995.

jrfnl commented 7 years ago

@grappler Nice! I was actually thinking of not using the getFilesToProcess(), but of just glob()-ing the root directory of the project being sniffed. That would get round the --extensions=php issue.

Also: style.css would only work for themes and we'd need to cater for plugins too.

grappler commented 7 years ago

I was actually thinking of not using the getFilesToProcess(), but of just glob()-ing the root directory of the project being sniffed.

The reason I wanted to use getFilesToProcess() is that I felt it was more stable. I am a bit worried that $values['files'][0] is not always going to be the root directory.

Also: style.css would only work for themes and we'd need to cater for plugins too.

This was just a proof of concept so we can always extend it to support plugins too.

westonruter commented 7 years ago

One caution here that comes to mind is that some IDEs will take the file being edited and place it in /tmp at a random path name and then run it through phpcs. So this could throw a wrench into this being able to be used by many users. I think this issue came up before when attempting to autodetect the text domain.

JDGrimes commented 7 years ago

Yeah, PhpStorm does that. But perhaps the values could be cached in some way, and the cached versions could be used in that case.

oojacoboo commented 7 years ago

Just found this when trying to work through a custom exclude-pattern bootstrap for each of our projects that use a shared phpcs.xml config. On the latest version, at least, I think you're going to want the following:

\PHP_CodeSniffer\Config::setConfigData();