Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot add custom ruleset via .phpcs.xml.dist (try to add phpcompatibility/php-compatibility) #2776

Open
c33s opened this issue Dec 18, 2019 · 9 comments · May be fixed by #2780
Open

Cannot add custom ruleset via .phpcs.xml.dist (try to add phpcompatibility/php-compatibility) #2776

c33s opened this issue Dec 18, 2019 · 9 comments · May be fixed by #2780

Comments

@c33s
Copy link

c33s commented Dec 18, 2019

related to #2764

  • php 7.3
  • PHP_CodeSniffer version 3.4.0 (phar)
  • win7x64

i try to get phpcompatibility/php-compatibility ruleset up and running by defining the installed_paths in .phpcs.xml.dist.

project root and its relevant directories:

.robo // ci folder
    composer.json // ci composer file
    bin
        phpcs.phar
    vendor // ci vendor
        phpcompatibility
            php-compatibility
                phpunit-bootstrap.php
src // project source
vendor // project vendor
.phpcs.xml.dist
composer.json // project composer file

part of the content of .phpcs.xml.dist

...
    <file>./src</file>
    <config name="installed_paths" value="./.robo/vendor/phpcompatibility/php-compatibility" />
...

tried the following paths:

../vendor/phpcompatibility/php-compatibility
../vendor/phpcompatibility/php-compatibility/PHPCompatibility
vendor/phpcompatibility/php-compatibility
vendor/phpcompatibility/php-compatibility/PHPCompatibility
.robo/vendor/phpcompatibility/php-compatibility
.robo/vendor/phpcompatibility/php-compatibility/PHPCompatibility
./../vendor/phpcompatibility/php-compatibility
./../vendor/phpcompatibility/php-compatibility/PHPCompatibility
C:\project1\src\.robo\vendor\phpcompatibility\php-compatibility
C:\\project1\\src\\.robo\\vendor\\phpcompatibility\\php-compatibility
C:/project1/src/.robo/vendor/phpcompatibility/php-compatibility

none of them worked.

some sort of debug output would be awesome to see which is the base path from where i can relatively access the vendor for the php-compatibility ruleset. also it would be good to know where the the path should exactly point to.

running php .robo/bin/phpcs.phar correctly runs the style check with my defined rulesets (choosen from the pre installed sets) but running php .robo/bin/phpcs.phar -i doesn't show the php-compat ruleset.

i only get them show up if i run php .robo/bin/phpcs.phar --config-set installed_paths C:\project1\src\.robo\vendor\phpcompatibility\php-compatibility with an absolute path.
then i get a CodeSniffer.conf file in .robo/bin and i get the PHPCompatibility ruleset listed

php .robo/bin/phpcs.phar -i
The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz, Zend and PHPCompatibility

changing the content of CodeSniffer.conf to a relative path 'installed_paths' => '.\\.robo\\vendor\\phpcompatibility\\php-compatibility' also don't work.

any hints?

@c33s
Copy link
Author

c33s commented Dec 18, 2019

same result with phpcs.phar version 3.5.3

@cdayjr
Copy link

cdayjr commented Dec 19, 2019

installed_paths can be the following currently:

  • an absolute path, e.g. something that starts with /home/user/.composer/vendor/
  • a path relative to the current working directory, e.g. something that starts with vendor/
  • a path relative to the location of the PHP_CodeSniffer composer vendor directory. e.g. something that starts with ../../custom/custom-rules. These paths begin with a . and is handled by some string replace logic.

As far as I know there's currently no way to have these paths be relative to the xml file, that's what #2764 aim to solve.

@c33s
Copy link
Author

c33s commented Dec 23, 2019

@cdayjr
tried from ../vendor/phpcompatibility/php-compatibility to ../../../../../../../../../../vendor /phpcompatibility/php-compatibility cannot break out of the phar file.

even if i switch to the non phar version php .robo\vendor\squizlabs\php_codesniffer\bin\phpcs -i i cannot get the config installed_paths to be read (used an absolute path).

in Standards.php $configPaths is always null even if i defined it in my .phpcs.xml.dist

@cdayjr
Copy link

cdayjr commented Dec 23, 2019

Ah, I see now. I believe -i only checks your global phpcs configuration, not whatever is defined in your phpcs.xml files. I don't think there's currently a way to read back configuration settings from an xml file, if I run phpcs --config-show in the same directory as a phpcs.xml file I get the following:

Using config file:

I imagine you'd get something similar.

@c33s
Copy link
Author

c33s commented Dec 23, 2019

correction:
absolute path in xml works but it will not show up with phpcs -i

all three types of path specification work:

C:\project1\src\.robo\vendor\phpcompatibility\php-compatibility
C:\\project1\\src\\.robo\\vendor\\phpcompatibility\\php-compatibility
C:/project1/src/.robo/vendor/phpcompatibility/php-compatibility

@c33s
Copy link
Author

c33s commented Dec 25, 2019

@cdayjr oh thanks, i have seen you where faster :)

as far as i read the code, the problem is, that the current code only allows to load code from another phar file if using a phar file.

in Standards.php the following code is executed:

$installedPath = Common::realPath(__DIR__.$ds.'..'.$ds.'..'.$ds.$installedPath);

in a phar file __DIR__ results in phar://C:/project1/src/.robo/bin/phpcs.phar/src/Util. so the path passed to realpath if i add ../vendor/phpcompatibility/php-compatibility to my xml config is:

phar://C:/project1/src/.robo/bin/phpcs.phar/src/Util\..\..\../vendor/phpcompatibility/php-compatibility

in Common::realpath the function self::isPharFile($path) always results in true because of __DIR__ always result in a path starting with phar:// if called from within a phar file.

if i change Standards::getInstalledStandardPaths it is fixed for me:

    public static function getInstalledStandardPaths()
    {
        $ds = DIRECTORY_SEPARATOR;

        $installedPaths = [dirname(dirname(__DIR__)).$ds.'src'.$ds.'Standards'];
        $configPaths    = Config::getConfigData('installed_paths');
        if ($configPaths !== null) {
            $installedPaths = array_merge($installedPaths, explode(',', $configPaths));
        }

        $resolvedInstalledPaths = [];
        foreach ($installedPaths as $installedPath) {
            if (substr($installedPath, 0, 1) === '.') {
                $baseDir = dirname(\Phar::running(false)) ;
                $installedPath = Common::realPath($baseDir.$ds.$installedPath);
                if ($installedPath === false) {
                    continue;
                }
            }

            $resolvedInstalledPaths[] = $installedPath;
        }

        return $resolvedInstalledPaths;

    }//end getInstalledStandardPaths()

c33s added a commit to c33s/PHP_CodeSniffer that referenced this issue Dec 25, 2019
@c33s c33s linked a pull request Dec 25, 2019 that will close this issue
@bkdotcom
Copy link
Contributor

bkdotcom commented Feb 4, 2020

I'm using the codacy.com code quality reporting tool..
I have no idea what value to use for installed_paths

the only error I see is

KubernetesDockerRunner: Container for codacy/codacy-codesniffer:1.3.1 exited with non-zero code 1
Error executing the tool
java.lang.Exception: 
CodeSniffer exited with code 3
stdout: ERROR: Referenced sniff "SlevomatCodingStandard.Arrays" does not exist

I don't know the absolute path
I don't know the "working dir"..
composer install is not used

Edit:
Turns out Codacy doesn't run composer or install dependencies... but I think they're supposed to have the slevomat sniffs...
Anyhow, I'm now using composer's post-update-cmd callback to edit/update my phpcs.xml and change the paths from relative to absolute.. Ensuring they work in whatever installed environment

    /**
     * update phpcs.xml.dist
     * convert relative dirs to absolute
     *
     * called from post-update-cmd script
     *
     * @return void
     */
    public static function updatePhpcsXml()
    {
        $phpcsPath = __DIR__ . '/../../phpcs.xml.dist';
        $regex = '#(<config name="installed_paths" value=")([^"]+)#';
        $xml = \file_get_contents($phpcsPath);
        $xml = \preg_replace_callback($regex, function ($matches) {
            $baseDir = \realpath(__DIR__ . '/../..') . '/';
            $paths = \preg_split('/,\s*/', $matches[2]);
            foreach ($paths as $i => $path) {
                if (\strpos($path, 'vendor') === 0) {
                    $paths[$i] = $baseDir . $path;
                }
            }
            return $matches[1] . \join(', ', $paths);
        }, $xml);
        \file_put_contents($phpcsPath, $xml);
    }

@bkdotcom
Copy link
Contributor

currently

when installed_paths is defined in phpcs.xml, it overrides the configuration provided by the command phpcs --config-set installed_paths /path/to/one,/path/to/two, even if the path is invalid. As consequence, the plugin will not be found.

should phpcs.xml installed_paths have the option to supplement command line installed paths?

@ktomk
Copy link
Contributor

ktomk commented Jun 8, 2021

installed_paths can be the following currently:

  • an absolute path, e.g. something that starts with /home/user/.composer/vendor/

Can confirm.

  • a path relative to the current working directory, e.g. something that starts with vendor/

Can not confirm this, it seems this suggestion (also commented in #2764) is a little brittle or outdated, at least it does not work as here in the original report, given the path starts with a dot and then the next path segment also starts with a dot (but is not a single dot alone or a double-dot path segment, like in ".robo").

  • a path relative to the location of the PHP_CodeSniffer composer vendor directory. e.g. something that starts with ../../custom/custom-rules. These paths begin with a . and is handled by some string replace logic.

Can confirm, but not for the part it would be strange. it is basically just relative to the PHP_CodeSniffer installation directory.

The leading dot is the marker for being a relative path. That is perhaps non-standard (on the file-system a relative path is a path not starting with /), but then it needs to be portable to less posix compatible systems, support URI-schemes like phar:// etc..

To use phpcompatibility/php-compatibility with dependencies managed by composer (vendor folder) with a relative path like in the example from @c33s :

<config name="installed_paths" value="./.robo/vendor/phpcompatibility/php-compatibility" />

(relative path; it is mandatory it starts with a dot ".")

it needs to resolve on the file-system relative to the installation path of PHP_CodeSniffer by realpath() to work.

Given the vendor folder in the non-working example, the correct relative path is:

<config name="installed_paths" value="./../../phpcompatibility/php-compatibility" />

The two dir-up /.. segments step up squizlabs/php_codesniffer/ to reach the vendor/ folder.

The following is equivalent:

<config name="installed_paths" value="../../phpcompatibility/php-compatibility" />

(comment against PHP_CodeSniffer 3.6.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants