Running PHPCBF Automatically with GitHub Actions
As developers, we each have a specific way that we like to format our code and we can have extremely strong feelings about our preferences. This is fine if we’re the sole developer working on a project but it can be a real problem when you’re working with a team of developers. In this situation, it’s guaranteed that each team member will have vastly different views on how code should be formatted. By picking a single coding style and sticking to it we can prevent all kinds of anger and annoyance.
Once you’ve picked a standard you should enforce the coding standard using a tool like PHP_CodeSniffer which should be set up to run automatically when we push our code to our source code repository. The downside to this is that this generally means we just get a report back telling us what the problems are and then we have to take time out of our busy day to fix our code. But what if we could have GitHub Actions automatically resolve as many problems as possible for us?
In this article, we’ll set up a process to do just that.
Setting Up Our Project
For our example today we’re going to create a project hosted on GitHub that has a GitHub Action setup to automatically validate our code against the PSR-12 coding standard. We’re going to use PHP_CodeSniffer to do this.
If you haven’t had a chance to use PHP_CodeSniffer, it’s a library that provides us with two command line tools to perform validation and beautification of our code. The first one we’re going to discuss is the PHP Code Sniffer (phpcs
) tool which allows us to validate our code against a coding standard and show us how we can “fix” our code to meet the standard.
To start I’m going to create a new directory and then use composer to install PHP_CodeSniffer.
% mkdir phpcbf-automatically && cd phpcbf-automatically
% composer require --dev squizlabs/PHP_CodeSniffer
Using version ^3.10 for squizlabs/php_codesniffer
./composer.json has been created
Running composer update squizlabs/php_codesniffer
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking squizlabs/php_codesniffer (3.10.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Downloading squizlabs/php_codesniffer (3.10.1)
- Installing squizlabs/php_codesniffer (3.10.1): Extracting archive
Generating autoload files
1 package you are using is looking for funding.
Use the `composer fund` command to find out more!
Then I’m going to create a file that’s formatted in a way that violates the PSR12 coding standard.
<?php //bad.php
function hello()
{
echo "hi";
}
Now when I run phpcs
on this file and specify the PSR12 coding standard we’ll see some errors:
“`
$ ./vendor/bin/phpcs –standard=psr12 bad.php
FILE: /Users/scottkeck-warren/tmp/phpcbf-automatically/bad.php
FOUND 5 ERRORS AFFECTING 5 LINES
1 | ERROR | [x] Header blocks must be separated by a single blank line
2 | ERROR | [x] Expected 1 space after FUNCTION keyword; 0 found
3 | ERROR | [x] Opening brace should be on the same line as the declaration
4 | ERROR | [x] Line indented incorrectly; expected at least 4 spaces, found 0
5 | ERROR | [x] Expected 1 newline at end of file; 0 found
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
Time: 141ms; Memory: 6MB
“`
The “[x]” specifies we can fix the violations automatically but we want to see these errors in GitHub so we’re not going to fix them right now.
Now we can set up our directory to be a git repository, add our files, and push them to a GitHub repository I’ve already created.
$ git init
Reinitialized existing Git repository in /Users/scottkeck-warren/tmp/phpcbf-automatically/.git/
$ echo "vendor" >> .gitignore
$ git add bad.php composer.json composer.lock .gitignore
$ git commit -a -m "Initial Commit"
[main (root-commit) b839ccc] Initial Commit
3 files changed, 109 insertions(+)
create mode 100644 bad.php
create mode 100644 composer.json
create mode 100644 composer.lock
$ git remote add origin git@github.com:ScottKeckWarren/phpcbf-automatically.git
$ git push -u origin main
Now we need to set up a GitHub Action to automatically run phpcs
when we push code to our repository.
To do this we’re going to create a file named “.github/workflows/phpcs.yml” and fill it with the following.
name: PHPCS
on: [push]
jobs:
phpcs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run PHPCS
run: ./vendor/bin/phpcs --standard=psr12 ./bad.php
This is a very basic GitHub Actions YAML file. I’m not going to go through all the steps but if you’re curious about each step you can learn more in our article “Automating Our Tests With GitHub Actions”.
Now we’ll commit this file and push it to GitHub.
git add .; git commit -a -m "Added PHPCS.yml"; git push
If we look at the “Actions” tab of our project we’ll see that our action ran but failed because our file fails as we kind of expected it to.
Fixing Our Code
PHP_CodeSniffer comes with a second command line tool called PHP Code Beautifier and Fixer (phpcbf
). We can run phpcbf
to fix any of the errors that we found using phpcs
that have the “[X]” in them which tends to be a lot of them.
We can run it locally by running ./vendor/bin/phpcbf --standard=psr12 bad.php
and then commit the results and our phpcs
check will pass. But I don’t want to have to manually run this. I want to continue to “automate all the things” so I’m going to instead use GitHub actions to automatically run phpcbf
and commit the results directly to our repository.
To do this we’ll create a new YAML named “.github/workflows/phpcbf.yml”.
name: Automatically Run PHPCBF
on: [push]
jobs:
phpcbf:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run PHPCBF
run: ./vendor/bin/phpcbf --standard=psr12 ./bad.php
continue-on-error: true
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v4
It’s extremely similar to the “phpcs.yml” we created earlier with a couple of important differences.
The first is that we have a section that sets the permissions to allow the actions performed in the file to be written to our repository.
permissions:
contents: write
The next is we have the step that runs phpcbf
and then we specify that GitHub Actions should always continue. This is because if phpcbf
fixes anything it will return an error code and the action will stop. We don’t want that because we want the action to continue so we can commit the results.
- name: Run PHPCBF
run: ./vendor/bin/phpcbf --standard=psr12 ./
continue-on-error:true
Finally, we’re going to use the git-auto-commit-action action to create a new commit.
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v4
We’ll add this file to our repository and commit the results.
git add .github/workflows/phpcbf.yml
git commit -m "Added `phpcbf` action"
git push
Now we can go back to our actions tab and see two actions are running now.
After our “Automatically Run PHPCBF” action finishes we can look at the commit history of our code and see that our code has been automatically fixed.
Now the annoying thing is that our phpcs
check that also ran is going to fail because it runs using the initial commit and not the newly created commit.
GitHub Actions also doesn’t automatically rerun our Actions after the automatic commit is created. I think this is to prevent an infinite loop but it is annoying. When this happens we need to pull down the changes create a new commit and then push back to GitHub so we get.
Only Running Some Times
My team has a rule that all changes that go into the main branch need to go through a pull request so the change can be validated. So we don’t want any code being changed in the main branch automatically.
To fix that we can specify that the action only runs on a pull request and not on every push.
name: Automatically Run PHPCBF
on: [pull_request]
We have other actions we only want to run when some files change. For example, we use GitHub Actions to compile the CSS and JS for our application but that only needs to happen when we make changes to specific directories.
We can use the path option to specify just this.
on:
pull_request:
paths:
- resources/js/**
- resources/css/**
Again GitHub Actions are super powerful
What You Need To Know
- Your team needs a coding standard
- Use PHP_CodeSniffer to enforce the standard
- Use GitHub Actions to apply the standard automatically
Leave a comment
Use the form below to leave a comment: