A PHP library that can be used manually as well as a CLI script that you can just run on your file

Overview

Run phpcs on files and only report new warnings/errors compared to the previous version.

This is both a PHP library that can be used manually as well as a CLI script that you can just run on your files.

What is this for?

Let's say that you need to add a feature to a large legacy file which has many phpcs errors. If you try to run phpcs on that file, there is so much noise it's impossible to notice any errors which you may have added yourself.

Using this script you can get phpcs output which applies only to the changes you have made and ignores the unchanged errors.

Installation

composer global require sirbrillig/phpcs-changed

CLI Usage

👩‍💻 👩‍💻 👩‍💻

To make this work, you need to be able to provide data about the previous version of your code. phpcs-changed can get this data itself if you use svn or git, or you can provide it manually.

Here's an example using phpcs-changed with the --svn option:

phpcs-changed --svn file.php

If you wanted to use svn and phpcs manually, this produces the same output:

svn diff file.php > file.php.diff
svn cat file.php | phpcs --report=json -q > file.php.orig.phpcs
cat file.php | phpcs --report=json -q > file.php.phpcs
phpcs-changed --diff file.php.diff --phpcs-unmodified file.php.orig.phpcs --phpcs-modified file.php.phpcs

Both will output something like:

FILE: file.php
-----------------------------------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------
 76 | WARNING | Variable $foobar is undefined.
-----------------------------------------------------------------------------------------------

Or, with --report json:

{
  "totals": {
    "errors": 0,
    "warnings": 1,
    "fixable": 0
  },
  "files": {
    "file.php": {
      "errors": 0,
      "warnings": 1,
      "messages": [
        {
          "line": 76,
          "message": "Variable $foobar is undefined.",
          "source": "VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable",
          "severity": 5,
          "fixable": false,
          "type": "WARNING",
          "column": 8
        }
      ]
    }
  }
}

If the file was versioned by git, we can do the same with the --git option:

phpcs-changed --git --git-unstaged file.php

When using --git, you should also specify --git-staged, --git-unstaged, or --git-base.

--git-staged compares the currently staged changes (as the modified version of the files) to the current HEAD (as the unmodified version of the files). This is the default.

--git-unstaged compares the current (unstaged) working copy changes (as the modified version of the files) to the either the currently staged changes, or if there are none, the current HEAD (as the unmodified version of the files).

--git-base, followed by a git object, compares the current HEAD (as the modified version of the files) to the specified git object (as the unmodified version of the file) which can be a branch name, a commit, or some other valid git object.

git checkout add-new-feature
phpcs-changed --git --git-base master file.php

CLI Options

More than one file can be specified after a version control option, including globs and directories. If any file is a directory, phpcs-changed will scan the directory for all files ending in .php and process them. For example: phpcs-changed --git src/lib test/**/*.php will operate on all the php files in the src/lib/ and test/ directories.

You can use --ignore to ignore any directory, file, or paths matching provided pattern(s). For example.: --ignore=bin/*,vendor/* would ignore any files in bin directory, as well as in vendor.

You can use --report to customize the output type. full (the default) is human-readable and json prints a JSON object as shown above. These match the phpcs reporters of the same names.

You can use --standard to specify a specific phpcs standard to run. This matches the phpcs option of the same name.

You can also use the -s option to Always show sniff codes after each error in the full reporter. This matches the phpcs option of the same name.

The --error-severity and --warning-severity options can be used for instructing the phpcs command on what error and warning severity to report. Those values are being passed through to phpcs itself. Consult phpcs documentation for severity settings.

The --cache option will enable caching of phpcs output and can significantly improve performance for slow phpcs standards or when running with high frequency. There are actually two caches: one for the phpcs scan of the unmodified version of the file and one for the phpcs scan of the modified version. The unmodified version phpcs output cache is invalidated when the version control revision changes or when the phpcs standard changes. The modified version phpcs output cache is invalidated when the file hash changes or when the phpcs standard changes.

The --no-cache option will disable the cache if it's been enabled. (This may also be useful in the future if caching is made the default.)

The --clear-cache option will clear the cache before running. This works with or without caching enabled.

The --always-exit-zero option will make sure the run will always exit with 0 return code, no matter if there are lint issues or not. When not set, 1 is returned in case there are some lint issues, 0 if no lint issues were found. The flag makes the phpcs-changed working with other scripts which could detect 1 as failure in the script run (eg.: arcanist).

The --no-verify-git-file option will prevent checking to see if a file is tracked by git during the git workflow. This can save a little time if you can guarantee this otherwise.

The --no-cache-git-root option will prevent caching the check used by the git workflow to determine the git root within a single execution. This is probably only useful for automated tests.

The --arc-lint option can be used when the phpcs-changed is run via arcanist, as it skips some checks, which are performed by arcanist itself. It leads to better performance when used with arcanist. (Equivalent to --no-verify-git-file --always-exit-zero.)

PHP Library

🐘 🐘 🐘

getNewPhpcsMessagesFromFiles

This library exposes a function PhpcsMessages\getNewPhpcsMessagesFromFiles() which takes three arguments:

  • A file path containing the full unified diff of a single file.
  • A file path containing the messages resulting from running phpcs on the file before your recent changes.
  • A file path containing the messages resulting from running phpcs on the file after your recent changes.

It will return an instance of PhpcsMessages which is a filtered list of the third argument above where every line that was present in the second argument has been removed.

PhpcsMessages represents the output of running phpcs.

To read the phpcs JSON output from an instance of PhpcsMessages, you can use the toPhpcsJson() method. For example:

use function PhpcsChanged\getNewPhpcsMessagesFromFiles;

$changedMessages = getNewPhpcsMessagesFromFiles(
	$unifiedDiffFileName,
	$oldFilePhpcsOutputFileName,
	$newFilePhpcsOutputFileName
);

echo $changedMessages->toPhpcsJson();

This will output something like:

{
  "totals": {
    "errors": 0,
    "warnings": 1,
    "fixable": 0
  },
  "files": {
    "file.php": {
      "errors": 0,
      "warnings": 1,
      "messages": [
        {
          "line": 20,
          "type": "WARNING",
          "severity": 5,
          "fixable": false,
          "column": 5,
          "source": "ImportDetection.Imports.RequireImports.Import",
          "message": "Found unused symbol Foobar."
        }
      ]
    }
  }
}

getNewPhpcsMessages

If the previous function is not sufficient, this library exposes a lower-level function PhpcsMessages\getNewPhpcsMessages() which takes three arguments:

  • (string) The full unified diff of a single file.
  • (PhpcsMessages) The messages resulting from running phpcs on the file before your recent changes.
  • (PhpcsMessages) The messages resulting from running phpcs on the file after your recent changes.

It will return an instance of PhpcsMessages which is a filtered list of the third argument above where every line that was present in the second argument has been removed.

You can create an instance of PhpcsMessages from real phpcs JSON output by using PhpcsMessages::fromPhpcsJson(). The following example produces the same output as the previous one:

use function PhpcsChanged\getNewPhpcsMessages;
use function PhpcsChanged\getNewPhpcsMessagesFromFiles;
use PhpcsChanged\PhpcsMessages;

$changedMessages = getNewPhpcsMessagesFromFiles(
     $unifiedDiffFileName,
     $oldFilePhpcsOutputFileName,
     $newFilePhpcsOutputFileName
);

echo $changedMessages->toPhpcsJson();

Multiple files

You can combine the results of getNewPhpcsMessages or getNewPhpcsMessagesFromFiles by using PhpcsChanged\PhpcsMessages::merge() which takes an array of PhpcsMessages instances and merges them into one instance. For example:

use function PhpcsChanged\getNewPhpcsMessages;
use function PhpcsChanged\getNewPhpcsMessagesFromFiles;
use PhpcsChanged\PhpcsMessages;

$changedMessagesA = getNewPhpcsMessages(
     $unifiedDiffA,
     PhpcsMessages::fromPhpcsJson($oldFilePhpcsOutputA),
     PhpcsMessages::fromPhpcsJson($newFilePhpcsOutputA)
$changedMessagesB = getNewPhpcsMessagesFromFiles(
     $unifiedDiffFileNameB,
     $oldFilePhpcsOutputFileNameB,
     $newFilePhpcsOutputFileNameB
);

$changedMessages = PhpcsMessages::merge([$changedMessagesA, $changedMessagesB]);

echo $changedMessages->toPhpcsJson();

Running Tests

Run the following commands in this directory to run the built-in test suite:

composer install
composer test

You can also run linting and static analysis:

composer lint
composer phpstan

Inspiration

This was inspired by the amazing work in https://github.com/Automattic/phpcs-diff

Comments
  • Add caching of phpcs results

    Add caching of phpcs results

    Running phpcs is probably the largest overhead of using phpcs-changed, since it needs to be run twice for each file. In addition, just getting the contents of some of those files can be expensive, notably old versions of svn files. In this PR we cache each phpcs result for later use. Each cache entry is removed if the svn revision changes (for svn mode), or if the file hash of any file changes. We keep caches for different phpcs standards so that phpcs-changed can be run on the same file for different standards multiple times and still benefit from the cache.

    This PR adds caching to svn mode and git mode. To activate the cache, you must use the --cache CLI option, and it can be explicitly disabled using --no-cache. The cache can be completely cleared by using the --clear-cache option.

    opened by sirbrillig 5
  • Add git workflow

    Add git workflow

    This adds a --git CLI shortcut and a git workflow to automatically collect the three necessary pieces of data for a git-versioned file in the same way that the --svn shortcut does for svn-versioned files.

    • [x] Add tests
    • [x] Add workflow
    • [x] Add CLI command
    • [x] Add docs
    • [x] Handle untracked and unchanged files

    Fixes #3

    opened by sirbrillig 5
  • Use "modified" terms">

    Use "unmodified" > "modified" terms

    This updates the terminology used by the code (without changing any user-facing terms or APIs) to standardize terminology of the previous version of a file and the changed version of a file as "unmodified" and "modified", respectively.

    This will replace usage of the terms "new" (except to refer to newly added files or newly added phpcs messages for reporting), "old", "base" (except to refer to git base), "previous", "changed", and "orig" in most contexts.

    opened by sirbrillig 2
  • Always return 0 from reportMessagesAndExit when run with --arc-lint flag

    Always return 0 from reportMessagesAndExit when run with --arc-lint flag

    When using futures in arcanist linter, the external lint program, eg.: phpcs-changed in this case, should always return 0 status, even if there are lint errors, otherwise the external program's run is considered being unsuccessful.

    opened by david-binda 2
  • Only run orig file PHPCS linting when the new version has lint issues

    Only run orig file PHPCS linting when the new version has lint issues

    When there are no linting issues reported by PHPCS in the new file, there is no point in checking the orig verion, since we would not report any issues anyway.

    This should speed up the phpcs-changed for files w/o lint issues.

    props to @sirbrillig for the idea.

    opened by david-binda 2
  • Use different commands for new files based on git mode

    Use different commands for new files based on git mode

    When using the various git modes, the way to determine the contents of the "new" version of a file differs. Using git-unstaged, we can cat the working copy of the file; using git-staged, we must ask git for the staged version; using git-branch, we must ask git for the committed version.

    An oversight in https://github.com/sirbrillig/phpcs-changed/pull/18 meant that the first two modes used the current working copy of the file. This PR fixes that bug by using the staged version for git-staged.

    The third mode is a little more complicated. https://github.com/sirbrillig/phpcs-changed/pull/28 added the git-branch mode, and based on the description, it was intended to also use the current working copy of the file. However, in https://github.com/sirbrillig/phpcs-changed/pull/31 and https://github.com/sirbrillig/phpcs-changed/pull/34 I added documentation for that mode and incorrectly specified that it used the currently committed version of the code to compare to the HEAD of another branch. This PR changes the mode's behavior to match the documentation, using the current HEAD for git-branch. While I think this should be safe (the use-case given in the original PR was running the changes through CI, which is probably always operating on the committed file), I want to double check with @david-binda to make sure.

    Fixes https://github.com/sirbrillig/phpcs-changed/issues/37

    opened by sirbrillig 2
  • Allow more files extensions to be checked

    Allow more files extensions to be checked

    PHPCS which is internally used by the phpcs-changed allows more than .php files to be linted by default. This PR brings the default allowed extensions in phpcs-changed on par with phpcs.

    The logic is a copy-paste from the phpcs, as it would validate the extensions using the very same logic again, so it should ideally work the same in both places.

    Fixes #27

    opened by david-binda 2
  • Add support for inter branch diffs

    Add support for inter branch diffs

    When phpcs-changed is used in CI (eg.: Travis), there is no good way to determine the unified diff, since the working directory is clean.

    In order to accommodate the situation when a branch or PR is being checked, a support for inter branch diff is helpful.

    This PR adds a new param git-branch: which instructs the phpcs-changed to run a diff of the current working directory against specified branch (eg.: master).

    A usage in Travis would be, for instance, as follows:

    script:
      - if [[ "$SNIFF" == "1" ]] && [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then /tmp/phpcs-changed/bin/phpcs-changed --git --git-branch "$TRAVIS_BRANCH" --standard="WordPress-VIP-Go" .; fi
    

    For the needs of checking whether a file is a new one, the UnixShell::executeCommand method had to be changed to use exec rather than shell_exec, as exec also provides support for reading the exit code. In order to make the UnixShell::executeCommand backward compatible, I'm joining the $output with PHP_EOL + appending one to the very end.

    See #25

    opened by david-binda 2
  • svn: Uncaught Exception when running on newly added file

    svn: Uncaught Exception when running on newly added file

    Instructions to reproduce:

    touch foo.php
    svn add foo.php
    phpcs-changed --svn foo.php
    
    PHP Fatal error:  Uncaught Exception: Failed to decode phpcs JSON: 'ERROR: You must supply at least one file or directory to process.
    
    bug 
    opened by sirbrillig 2
  • Replace JS-style var interpolation with PHP-style

    Replace JS-style var interpolation with PHP-style

    This replaces the JS-style variable interpolation ${var} with PHP-style {$var} since the former is deprecated.

    Fixes https://github.com/sirbrillig/phpcs-changed/issues/62

    opened by sirbrillig 1
  • PHP 8.2 Depreciations

    PHP 8.2 Depreciations

    PHP Deprecated: Using ${var} in strings is deprecated, use {$var} instead in //phpcs-changed/PhpcsChanged/GitWorkflow.php on line 168 and other various instances

    opened by kraftbj 1
  • Add emacs report type for use with vim plugins

    Add emacs report type for use with vim plugins

    This adds the "emacs" report type so that it can be used with vim plugins such as "ALE".

    When using vim plugins that report code sniff errors, it is nice to be able to limit the errors reported to just the lines that have been changed in certain cases to reduce the noise on some older files. Many of these plugins allow alternate executables to be specified, so adding this report type to phpcs-changed would allow this.

    opened by delputnam 4
  • More options for providing a list of files to (not) check

    More options for providing a list of files to (not) check

    PHPCS has rather broad options to provide a list of files to check and/or to exclude:

      [--extensions=<extensions>] [--ignore=<patterns>]
      [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...
    

    I believe that it would be cool if we could make the phpcs-changed as much drop-in replacement for phpcs as possible, to make it easier to use it in places where phpcs is currently used.

    The --stdin-path is IMHO not really possible, but other params should be doable, for automatic mode.

    opened by david-binda 1
Releases(v2.10.0)
  • v2.10.0(Mar 9, 2022)

    Changelog

    • First pass at passing in error and warning severity to phpcs (#57)
    • Move the debug info on (not)using cache behind isCachingEnabled check (#50)
    • Use "unmodified" > "modified" terms (#55)
    • Do not fetch file diff if updated file has no messages (#54)
    • Only run orig file PHPCS linting when the new version has lint issues (#51)
    • Always return 0 from reportMessagesAndExit when run with --arc-lint flag (#52)
    Source code(tar.gz)
    Source code(zip)
  • v2.10.0-beta.1(Feb 15, 2022)

    Changelog

    • First pass at passing in error and warning severity to phpcs (#57)
    • Move the debug info on (not)using cache behind isCachingEnabled check (#50)
    • Use "unmodified" > "modified" terms (#55)
    • Do not fetch file diff if updated file has no messages (#54)
    • Only run orig file PHPCS linting when the new version has lint issues (#51)
    • Always return 0 from reportMessagesAndExit when run with --arc-lint flag (#52)
    Source code(tar.gz)
    Source code(zip)
  • v2.9.0(Feb 1, 2022)

  • v2.8.1(Mar 22, 2021)

  • v2.8.0(Mar 15, 2021)

  • v2.8.0-beta.2(Mar 11, 2021)

  • v2.8.0-beta.1(Mar 9, 2021)

  • v2.7.1(Feb 16, 2021)

  • v2.7.0(Jan 29, 2021)

  • v2.6.0(Jan 4, 2021)

  • v2.5.1(Jun 24, 2020)

  • v2.5.0(Jun 22, 2020)

    Changelog

    This release adds support for comparing git branches instead of uncommitted changes as well as scanning for non-.php files in the same way as phpcs. Big thanks to @david-binda for these improvements!

    • Fix filenames expansions (#24) <David Biňovec>
    • Add support for inter branch diffs (#28) <David Biňovec>
    • Allow more files extensions to be checked (#29) <David Biňovec>
    • Introduce TestShell to make new tests easier (#30) <David Biňovec>
    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Apr 16, 2020)

    Changelog

    The main update in this release is support for unstaged git changes.

    • Add tests for reporters (#23)
    • Improve error messaging (#22)
    • Update help text with new options (#21)
    • Add show sniff codes option (#20)
    • Improve git mode (#18)
    Source code(tar.gz)
    Source code(zip)
  • v2.4.0-beta-3(Apr 15, 2020)

  • v2.4.0-beta-2(Apr 15, 2020)

  • v2.4.0-beta-1(Apr 15, 2020)

    Changelog

    • Add tests for reporters (#23)
    • Improve error messaging (#22)
    • Update help text with new options (#21)
    • Add show sniff codes option (#20)
    • Improve git mode (#18)
    Source code(tar.gz)
    Source code(zip)
  • v2.3.1(Apr 14, 2020)

  • v2.3.0(Apr 14, 2020)

  • v2.3.0-beta-1(Apr 14, 2020)

  • v2.2.7(Nov 21, 2019)

  • 2.2.7-beta-2(Nov 14, 2019)

  • 2.2.7-beta-1(Oct 29, 2019)

  • v2.2.6(Aug 7, 2019)

    Changelog

    This release just makes sure that reporters have full control of what is printed, even in the case of files which have no changes. So using --report json --svn will still output JSON even on a file that has no changes. This can be very important for scripts that use the output of phpcs-changed.

    It also adds static analysis (with phpstan) to the CI, to help catch any subtle bugs.

    • Treat unchanged files as normal (#11)
    • Add phpstan (#12)
    Source code(tar.gz)
    Source code(zip)
  • v2.2.3(Aug 5, 2019)

  • v2.2.2(Aug 4, 2019)

  • v2.2.0(Jun 25, 2019)

  • v2.2.0-beta.1(Jun 12, 2019)

  • v2.1.0(Feb 28, 2019)

    Changelog

    This is mostly a refactor of some of the existing svn workflow to give it test coverage, but it does add a --version option to the CLI so now you can see what version of phpcs-changed is being run.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Jan 4, 2019)

  • v2.0(Nov 27, 2018)

    Changelog

    • Added new getNewPhpcsMessagesFromFiles() top-level function which provides a more clean abstraction over the library's internals.
    • Added --report option to allow json and phpcs "full" reporting (default is now full, to match phpcs).
    • json report now prints output even if there are no messages (full report does not).
    • Output now includes the filename, if available from the diff.
    Source code(tar.gz)
    Source code(zip)
Owner
Payton Swick
Vegan. Digital craftsman. Tea explorer. Avid learner of things. Writes code @automattic.
Payton Swick
WordPlate is a wrapper around WordPress. It makes developers life easier. It is just like building any other WordPress website with themes and plugins. Just with sprinkles on top.

WordPlate is simply a wrapper around WordPress. It makes developers life easier. It is just like building any other WordPress website with themes and plugins. Just with sprinkles on top.

WordPlate 1.7k Dec 24, 2022
PHP project for tracking Azure AD App Reg secrets about to expire, and (manually) tracking SSL certificates

CertExpiry Katy Nicholson https://katystech.blog/ Setup instructions etc available at: https://katystech.blog/2021/11/certexpiry/ PHP project for trac

Katy Nicholson 5 Oct 2, 2022
Run your WP site on github pages, php innovation award winner https://www.phpclasses.org/package/12091-PHP-Make-a-WordPress-site-run-on-GitHub-pages.html

Gitpress Run wordpress directly on github pages Gitpress won the innovation award for may 2021 Read more about this https://naveen17797.github.io/gitp

naveen 13 Nov 18, 2022
🧬 Nano is a zero-config, no skeleton, minimal Hyperf distribution that allows you to quickly build a Hyperf application with just a single PHP file.

Nano is a zero-config, no skeleton, minimal Hyperf distribution that allows you to quickly build a Hyperf application with just a single PHP file.

Hyperf 273 Jan 4, 2023
StickWithIt is an online food ordering website created using PHP. You can view and purchase various items as well as remove items from the cart.

StickWithIt (App Name) StickWithIt is an online food ordering website created using PHP. The database used here is MYSQL database. The tool used here

Jenil Gajjar 1 May 11, 2022
Run a script in real time inside your pocketmine server!

Geral PlayScript is a plugin that allows you to run PHP code directly from the server by command. How to use Move your PHP code (must have a .php exte

LADINO 3 Aug 9, 2022
Shows you the current diocese that you're in, as well as the bishop.

Use config.php to create variables $dbuser and $dbpass for the database connection. Run `php -f ./coa/newcheck.php` to update database information fr

Canon Law Ninjas 2 Dec 2, 2021
This library can be used, among other things, to retrieve the classes, interfaces, traits, enums, functions and constants declared in a file

marijnvanwezel/reflection-file Library that allows reflection of files. This library can be used, among other things, to retrieve the classes, interfa

Marijn van Wezel 5 Apr 17, 2022
This shell script and PHP file create a browseable HTML site from the Zig standard library source.

Browseable Zig standard library This shell script and PHP file create a browseable HTML site from the Zig standard library source. The idea is to inve

Dave Gauer 3 Mar 20, 2022
This Kirby V3 Plugin brings snippets and blueprints together in one place. It includes useful tools that completely changing the way you work with Kirby: Fast and well organized.

Kirby Components Overview Do you love to make awesome projects with Kirby CMS? Do you also find it difficult to switch between snippets and blueprints

Roman Gsponer 6 May 31, 2023
Composer script handling your ignored parameter file

Managing your ignored parameters with Composer This tool allows you to manage your ignored parameters when running a composer install or update. It wo

Incenteev 921 Nov 21, 2022
This is a well customized library for MYSQLi and PHP.

prepared This is a well customized library for MYSQLi and PHP. Helps you in writing your procedural PHP with just few lines of codes. This library hav

World Brain Technology 5 Jul 29, 2022
This script is used to load datas (Anteriorite) in Alfresco

This script is used to load datas (Anteriorite) in Alfresco

Bureau National de l'Etat Civil 0 Sep 28, 2022
You have just downloaded "Messenger-app" [A lightweight, minimalistic real-time chat application]

MESSENGER-APP You have just downloaded "Messenger-app" [A lightweight, minimalistic real-time chat application] Setup To get it working, follow these

Chr1st0ph3r SAB 1 Oct 29, 2021
Just the stats I want, maybe you want them too

SKCDEV Easy Digital Downloads Stats This plugin adds a new "SKCDEV EDD Stats" report tab to the EDD Reports tabs. Just the stats I want, maybe you wan

Scott Kingsley Clark 3 Sep 8, 2022
It is a simple blog application coded with PHP, HTML, CSS. You can develop, edit. You can see it as a skeleton. ⚡

PHP-BLOG-SYSTEM Simple blog system Features Adding Text Update Text Text Deletion User Login and register Bootstrap Design Profile Page How to use blo

Selçuk 2 Aug 21, 2022
Dispatcher is a Laravel artisan command scheduling tool used to schedule artisan commands within your project so you don't need to touch your crontab when deploying.

Dispatcher Dispatcher allows you to schedule your artisan commands within your Laravel project, eliminating the need to touch the crontab when deployi

Indatus 1.1k Dec 21, 2022