{"id":15472606,"url":"https://github.com/sirbrillig/phpcs-changed","last_synced_at":"2026-04-26T20:00:46.079Z","repository":{"id":39737835,"uuid":"157615577","full_name":"sirbrillig/phpcs-changed","owner":"sirbrillig","description":"🐘 Run phpcs on files and only report new warnings/errors compared to the previous version.","archived":false,"fork":false,"pushed_at":"2026-04-21T00:28:42.000Z","size":409,"stargazers_count":38,"open_issues_count":5,"forks_count":12,"subscribers_count":2,"default_branch":"trunk","last_synced_at":"2026-04-21T02:28:51.989Z","etag":null,"topics":["cli","linting","php","phpcs","svn"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sirbrillig.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-11-14T21:49:23.000Z","updated_at":"2026-04-20T23:25:28.000Z","dependencies_parsed_at":"2023-02-14T09:46:38.816Z","dependency_job_id":"98508bec-95bd-47ba-9ce7-8042c084ecd1","html_url":"https://github.com/sirbrillig/phpcs-changed","commit_stats":{"total_commits":156,"total_committers":6,"mean_commits":26.0,"dds":"0.11538461538461542","last_synced_commit":"ba0432bc86ffdc31a6946117be6c2419b7e3e16d"},"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"purl":"pkg:github/sirbrillig/phpcs-changed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirbrillig%2Fphpcs-changed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirbrillig%2Fphpcs-changed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirbrillig%2Fphpcs-changed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirbrillig%2Fphpcs-changed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sirbrillig","download_url":"https://codeload.github.com/sirbrillig/phpcs-changed/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirbrillig%2Fphpcs-changed/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32310804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T19:15:34.056Z","status":"ssl_error","status_checked_at":"2026-04-26T19:15:15.467Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","linting","php","phpcs","svn"],"created_at":"2024-10-02T02:40:23.246Z","updated_at":"2026-04-26T20:00:46.061Z","avatar_url":"https://github.com/sirbrillig.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Run [phpcs](https://github.com/squizlabs/PHP_CodeSniffer) on files and only report new warnings/errors compared to the previous version.\n\nThis is both a PHP library that can be used manually as well as a CLI script that you can just run on your files.\n\n## What is this for?\n\nLet'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.\n\nUsing this script you can get phpcs output which applies only to the changes you have made and ignores the unchanged errors.\n\n## Installation\n\n```\ncomposer global require sirbrillig/phpcs-changed\n```\n\n## CLI Usage\n\n👩‍💻👩‍💻👩‍💻\n\nTo 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.\n\nHere's an example using `phpcs-changed` with the `--svn` option:\n\n```\nphpcs-changed --svn file.php\n```\n\nIf you wanted to use svn and phpcs manually, this produces the same output:\n\n```\nsvn diff file.php \u003e file.php.diff\nsvn cat file.php | phpcs --report=json -q \u003e file.php.orig.phpcs\ncat file.php | phpcs --report=json -q \u003e file.php.phpcs\nphpcs-changed --diff file.php.diff --phpcs-unmodified file.php.orig.phpcs --phpcs-modified file.php.phpcs\n```\n\nBoth will output something like:\n\n```\nFILE: file.php\n-----------------------------------------------------------------------------------------------\nFOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE\n-----------------------------------------------------------------------------------------------\n 76 | WARNING | Variable $foobar is undefined.\n-----------------------------------------------------------------------------------------------\n```\n\nOr, with `--report json`:\n\n```json\n{\n  \"totals\": {\n    \"errors\": 0,\n    \"warnings\": 1,\n    \"fixable\": 0\n  },\n  \"files\": {\n    \"file.php\": {\n      \"errors\": 0,\n      \"warnings\": 1,\n      \"messages\": [\n        {\n          \"line\": 76,\n          \"message\": \"Variable $foobar is undefined.\",\n          \"source\": \"VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable\",\n          \"severity\": 5,\n          \"fixable\": false,\n          \"type\": \"WARNING\",\n          \"column\": 8\n        }\n      ]\n    }\n  }\n}\n```\n\nIf the file was versioned by git, we can do the same with the `--git` option:\n\n```\nphpcs-changed --git --git-unstaged file.php\n```\n\nWhen using `--git`, you should also specify `--git-staged`, `--git-unstaged`, or `--git-base`.\n\n`--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.\n\n`--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).\n\n`--git-base`, followed by a git object, compares the current HEAD (as the modified version of the files) to the specified [git object](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) (as the unmodified version of the file) which can be a branch name, a commit, or some other valid git object.\n\n```\ngit checkout add-new-feature\nphpcs-changed --git --git-base master file.php\n```\n\n### CLI Options\n\nMore 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.\n\nYou 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.\n\nYou can use `--report` to customize the output type. `full` (the default) is human-readable, `json` prints a JSON object as shown above, and 'xml' can be used by IDEs. These match the phpcs reporters of the same names. `junit` can also be used for [JUnit XML](https://github.com/testmoapp/junitxml) which can be helpful for test runners. `checkstyle` will output Checkstyle format.\n\nYou can use `--standard` to specify a specific phpcs standard to run. This matches the phpcs option of the same name.\n\nYou can use `--extensions` to specify a list of valid file extensions that phpcs should check. These should be separated by commas. This matches the phpcs option of the same name.\n\nYou 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.\n\nThe `--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.\n\nThe `--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.\n\nThe `--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.)\n\nThe `--clear-cache` option will clear the cache before running. This works with or without caching enabled.\n\nThe `--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). \n\nThe `--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.\n\nThe `--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.\n\nThe `--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`.)\n\nThe `--svn-path`, `--git-path`, `--cat-path`, and `--phpcs-path` options can be used to specify the paths to the executables of the same names. If these options are not set, the program will try to use the `SVN`, `GIT`, `CAT`, and `PHPCS` env variables. If those are also not set, the program will default to `svn`, `git`, `cat`, and `phpcs`, respectively, assuming that each command will be in the system's `PATH`.\n\nFor phpcs, if the path is not overridden, and a `phpcs` executable exists under the `vendor/bin` directory where this command is run, that executable will be used instead of relying on the PATH. You can disable this feature with the `--no-vendor-phpcs` option.\n\nThe `--debug` option will show every step taken by the script.\n\n## PHP Library\n\n🐘🐘🐘\n\n### getNewPhpcsMessagesFromFiles\n\nThis library exposes a function `PhpcsMessages\\getNewPhpcsMessagesFromFiles()` which takes three arguments:\n\n- A file path containing the full unified diff of a single file.\n- A file path containing the messages resulting from running phpcs on the file before your recent changes.\n- A file path containing the messages resulting from running phpcs on the file after your recent changes.\n\nIt 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.\n\n`PhpcsMessages` represents the output of running phpcs.\n\nTo read the phpcs JSON output from an instance of `PhpcsMessages`, you can use the `toPhpcsJson()` method. For example:\n\n```php\nuse function PhpcsChanged\\getNewPhpcsMessagesFromFiles;\n\n$changedMessages = getNewPhpcsMessagesFromFiles(\n\t$unifiedDiffFileName,\n\t$oldFilePhpcsOutputFileName,\n\t$newFilePhpcsOutputFileName\n);\n\necho $changedMessages-\u003etoPhpcsJson();\n```\n\nThis will output something like:\n\n```json\n{\n  \"totals\": {\n    \"errors\": 0,\n    \"warnings\": 1,\n    \"fixable\": 0\n  },\n  \"files\": {\n    \"file.php\": {\n      \"errors\": 0,\n      \"warnings\": 1,\n      \"messages\": [\n        {\n          \"line\": 20,\n          \"type\": \"WARNING\",\n          \"severity\": 5,\n          \"fixable\": false,\n          \"column\": 5,\n          \"source\": \"ImportDetection.Imports.RequireImports.Import\",\n          \"message\": \"Found unused symbol Foobar.\"\n        }\n      ]\n    }\n  }\n}\n```\n\n### getNewPhpcsMessages\n\nIf the previous function is not sufficient, this library exposes a lower-level function `PhpcsMessages\\getNewPhpcsMessages()` which takes three arguments:\n\n- (string) The full unified diff of a single file.\n- (PhpcsMessages) The messages resulting from running phpcs on the file before your recent changes.\n- (PhpcsMessages) The messages resulting from running phpcs on the file after your recent changes.\n\nIt 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.\n\nYou 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:\n\n```php\nuse function PhpcsChanged\\getNewPhpcsMessages;\nuse PhpcsChanged\\PhpcsMessages;\n\n$changedMessages = getNewPhpcsMessages(\n     $unifiedDiff,\n     PhpcsMessages::fromPhpcsJson($oldFilePhpcsOutput),\n     PhpcsMessages::fromPhpcsJson($newFilePhpcsOutput)\n);\n\necho $changedMessages-\u003etoPhpcsJson();\n```\n\n### Multiple files\n\nYou 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:\n\n```php\nuse function PhpcsChanged\\getNewPhpcsMessages;\nuse function PhpcsChanged\\getNewPhpcsMessagesFromFiles;\nuse PhpcsChanged\\PhpcsMessages;\n\n$changedMessagesA = getNewPhpcsMessages(\n     $unifiedDiffA,\n     PhpcsMessages::fromPhpcsJson($oldFilePhpcsOutputA),\n     PhpcsMessages::fromPhpcsJson($newFilePhpcsOutputA)\n);\n$changedMessagesB = getNewPhpcsMessagesFromFiles(\n     $unifiedDiffFileNameB,\n     $oldFilePhpcsOutputFileNameB,\n     $newFilePhpcsOutputFileNameB\n);\n\n$changedMessages = PhpcsMessages::merge([$changedMessagesA, $changedMessagesB]);\n\necho $changedMessages-\u003etoPhpcsJson();\n```\n\n## Running Tests\n\nRun the following commands in this directory to run the built-in test suite:\n\n```\ncomposer install\ncomposer test\n```\n\nYou can also run linting and static analysis:\n\n```\ncomposer lint\ncomposer static-analysis\n```\n\n## Debugging\n\nIf something isn't working the way you expect, use the `--debug` option. This will show a considerable amount of output. Pay particular attention to the CLI commands run by the script. You can run these commands manually to try to better understand the issue.\n\n## Inspiration\n\nThis was inspired by the amazing work in https://github.com/Automattic/phpcs-diff\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirbrillig%2Fphpcs-changed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsirbrillig%2Fphpcs-changed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirbrillig%2Fphpcs-changed/lists"}