How can PHP CodeSniffer check files that have no extension?
Asked Answered
E

4

6

In my team we are using codesniffer to enforce a coding style for a Symfony application, and just realized that the files without extension are not getting checked, even if we explicitly use the file as an argument. This is by design as discussed on this github issue.

That means that files like bin/console are not getting checked, even though they are valid PHP files, and the --extensions argument doesn't accept and empty argument.

Is there a way to make CodeSniffer check these files as well?

Encephalomyelitis answered 21/11, 2017 at 14:9 Comment(0)
T
1

From the docs

The ignoring of file extensions for specific files is a feature of PHP_CodeSniffer and is the only way to check files without an extension. If you check an entire directory of files, all files without extensions will be ignored, so you must check each of these file separately

https://github.com/squizlabs/PHP_CodeSniffer/wiki/Advanced-Usage

So you will need to specify the file

The syntax is

phpcs --file-list=path/to/file/containing/others

Where others is a file with a file per line structure.

This check all files listed in the others file

Eg

File1 File2 Console

To check the 3 files

Traditor answered 21/11, 2017 at 14:25 Comment(3)
If that's the case, there must be a bug because it looks like that the command php vendor/bin/phpcs ./bin/console command behaves like I gave it an empty folder as an argument instead of a file. Will open a ticket just in case.Encephalomyelitis
Looks like there is an explicit check just to prevent that github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Filters/…Encephalomyelitis
using --file-list option doesn't work for files without extension, at list in v3.3.2Charlyncharm
R
1

According to Issue #1754 PHP_CodeSniffer does not test PHP files with no extensions, it isn't supported out of the box. You need to write your own filter and pass it as argument.

Here's a little example, which you should tweak for your own needs. In particular, this filter is designed to find shell scripts written in PHP. It opens all extensionless files (fast for small projects, can be troublesome for large ones) looking for a specific she-bang pattern (builtin MIME detection functions weren't working for my use case). You could, for instance, add a white list of locations to look for scripts.

phpcs.xml

<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="phpcs.xsd">
    <arg name="filter" value="lib/CustomPHP_CodeSniffer/Filters/ExtensionlessPhpScriptsFilter.php"/>
    <rule ref="PSR12"></rule>
    <file>.</file>
</ruleset>

lib/CustomPHP_CodeSniffer/Filters/ExtensionlessPhpScriptsFilter.php

<?php

namespace Lib\CustomPHP_CodeSniffer\Filters;

use PHP_CodeSniffer\Filters\Filter;
use PHP_CodeSniffer\Util\Common;

class ExtensionlessPhpScriptsFilter extends Filter
{
    private const SCRIPT_SHEBANG = '@^#!/opt/php[\d.]+/bin/php@';

    public function accept(): bool
    {
        $filePath = Common::realpath($this->current());
        $extension = pathinfo($filePath, PATHINFO_EXTENSION);
        if (is_dir($filePath) || $extension !== '') {
            return parent::accept();
        }

        if ($this->shouldIgnorePath($filePath)) {
            return false;
        }

        return $this->isPhpShellScript($filePath);
    }

    private function isPhpShellScript(string $filePath): bool
    {
        if (is_dir($filePath)) {
            return false;
        }

        $fp = fopen($filePath, 'rb');
        if (!$fp) {
            throw new \RuntimeException("Could not open file to determine type: $filePath");
        }
        $line = fgets($fp, 80);
        fclose($fp);
        if ($line === false) {
            throw new \RuntimeException("Could not read file to determine type: $filePath");
        }

        return (bool)preg_match(self::SCRIPT_SHEBANG, $line);
    }
}
Redblooded answered 24/11, 2022 at 17:26 Comment(0)
S
0

I think you can specify * as wildcard extension.

If that does not work it states in the docs:

If you have asked PHP_CodeSniffer to check a specific file rather than an entire directory, the extension of the specified file will be ignored. The file will be checked even if it has an invalid extension or no extension at all. In the following example, the main.inc file will be checked by PHP_CodeSniffer even though the --extensions command line argument specifies that only .php files should be checked. Extension ignored when checking specific file

$ phpcs --extensions=php /path/to/code/main.inc

So it should work if you explicitly check a single file that has no extension.

Further it states:

The ignoring of file extensions for specific files is a feature of PHP_CodeSniffer and is the only way to check files without an extension. If you check an entire directory of files, all files without extensions will be ignored, so you must check each of these file separately.

See https://pear.php.net/manual/en/package.php.php-codesniffer.advanced-usage.php

Stronghold answered 21/11, 2017 at 14:22 Comment(1)
While you "think" I know in config <file>.</file><arg name="extensions" value="*"/> does NOT work. **In fact it stops working even for .php files if you switch from .php to *Musso
S
-2

I don't know if CS can do this, but I think files like bin/console are mostly up to ten lines, so there is no need to check them with codesniffer :) They should just require vendor and run some Application class from source. Also they are all same in all projects.

Just for reference, our bin/console.

#!/usr/bin/env php
<?php declare(strict_types = 1);

/** @var \Nette\DI\Container $container */
$container = require __DIR__ . '/../src/bootstrap.php';

/** @var \Symfony\Component\Console\Application $console */
$console = $container->getByType(Symfony\Component\Console\Application::class);

$services = $container->findByType(Symfony\Component\Console\Command\Command::class);

foreach ($services as $service) {
    $console->add(
        $container->getService($service)
    );
}

exit($console->run());

There is no reason to check this.

Scurry answered 21/11, 2017 at 14:27 Comment(9)
Although I agree with you, it does not solve the OPs specific problem :-)Stronghold
That's true, hence i mentioned it in first sentence. :) But I also think it's important to talk about where we should use the tools and where it is make real profit :)Deel
"are mostly up to ten lines"? Because you just pulled one up that is short? PHP is awesome for shell scripting, much better than bash. And not all scripts need to be structured in external files. I have 0common.php where I keep a few functions I use in multiple scripts, but I have lots of script files with more than 10 lines and I for sure want them checked without having to give them the .php extension.Musso
@Musso no, you just don't get that right... you talk about scripts which are do something... they are surelly longer than 10 lines... but this question talks about bin/console which is ENTRYPOINT of CLI application, and that is as i can say with my 10 years experience exactly SAME for even projects which i created more than 5 years ago.Deel
Is bin/console just a common entrypoint people use? I do not want to write a single application. I just moved from bash to php and just want to have a bunch of scripts in a folder ...Musso
@Musso yeah, it is, is tool for organize THAT SCRIPTS you said. symfony.com/doc/current/components/console.html PHP files in normal should have .php extensions, the only files which this is acceptible was some entrypoints like bin/console.Deel
I also said in answer, that it's not direct answer to question, but it's basically about that, this is not problem at all.Deel
We also check every file with most strict tools which is posible, but in case of bin/console. Its just doesn't make sense. :]Deel
By peoples in community? The base problem of your arguments is that you don't know that file which was listed in question as you said... So there can't be understanding of my described point of view...Deel

© 2022 - 2024 — McMap. All rights reserved.