Analyzing PHP project code with Psalm

2 Apr 2021

Hello! Today I would like to share Psalm code analysis configuration for PHP(Symfony) project. Then I'm going to demonstrate html report generation and integration with Github Actions.

Psalm is a static analysis tool for finding errors in PHP applications. There is live demo on its website.

Initially I configured Code Coverage Analysis for this blog. Then I also made it for one of my open source projects - placeholder-service. Idea behind is to make it more clear and share all configs.

Installation

Psalm installation described in documentation.

composer require --dev vimeo/psalm
./vendor/bin/psalm --init

After installation will be created psalm.xml file.

Usage

Run psalm for entire project:

./vendor/bin/psalm

Or for specific files:

./vendor/bin/psalm src/Controller

Save results in XML:

./vendor/bin/psalm --report=checkstyle.xml

Generate html report

There is a psalm-html-output library for creation html report. It depends on xsltproc package. Firstly install library:

composer require --dev roave/psalm-html-output

THen install system package xsltproc

apt install xsltproc

Html report will be generated with such command:

vendor/bin/psalm --output-format=xml | xsltproc vendor/roave/psalm-html-output/psalm-html-output.xsl - > psalm-report.html

We can add shortcuts to composer.json. Then we will only need to run composer psalm and composer psalm-report-html.

{
    ...
    "scripts": {
        ...
        "psalm": [
            "vendor/bin/psalm --report=checkstyle.xml"
        ],
        "psalm-report-html": [
            "vendor/bin/psalm --output-format=xml | xsltproc vendor/roave/psalm-html-output/psalm-html-output.xsl - > psalm-report.html"
        ],
        ...
    }
    ...
}

Integration with Github Actions

Github actions configuration and showing html reports in pull request is described in previous articles. We need to add generation html report and upload to Web server. We will create .github/workflows/psalm.yml workflow for that. For a private repository it's very important to protect web server with basic auth or other methods!

## —— Upload tests coverage report to remote server
- name: Extract branch name
  shell: bash
  run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
  id: extract_branch
- name: Print branch name
  shell: bash
  run: echo ${{ steps.extract_branch.outputs.branch }}
- name: Create directories for reports
  uses: appleboy/ssh-action@master
  env:
    BRANCH: ${{ steps.extract_branch.outputs.branch }}
    FILES_PATH: ${{ secrets.GA_FILES_PATH }}
  with:
    envs: BRANCH,FILES_PATH
    host: ${{ secrets.REMOTE_HOST }}
    username: ${{ secrets.REMOTE_USER }}
    key: ${{ secrets.SERVER_SSH_KEY }}
    passphrase: ''
    script: mkdir -p $FILES_PATH/$BRANCH
- name: Uploads reports to server
  uses: appleboy/scp-action@master
  with:
    host: ${{ secrets.REMOTE_HOST }}
    username: ${{ secrets.REMOTE_USER }}
    key: ${{ secrets.SERVER_SSH_KEY }}
    passphrase: ''
    rm: true
    source: "coverage"
    target: ${{ secrets.GA_FILES_PATH }}/${{ steps.extract_branch.outputs.branch }}

Then we will add new comment creation into .github/workflows/pull_request_comments.yml workflow.

name: Pull request comments
on:
  pull_request:
jobs:
  pull_request_comments:
    runs-on: ubuntu-20.04
    steps:
      - name: Extract branch name
        shell: bash
        run: echo "##[set-output name=branch;]$(echo ${GITHUB_HEAD_REF})"
        id: extract_branch
      - name: Print branch name
        shell: bash
        run: echo ${{ steps.extract_branch.outputs.branch }}
      - name: Add psalm code static analysis report comment
        uses: mshick/add-pr-comment@v1
        with:
          message: |
            Psalm static analysis report: [http://files.antonshell.me/github-actions/placeholder-service/${{ steps.extract_branch.outputs.branch }}/psalm/psalm-report.html](http://files.antonshell.me/github-actions/placeholder-service/${{ steps.extract_branch.outputs.branch }}/psalm/psalm-report.html)
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          repo-token-user-login: 'github-actions[bot]' # The user.login for temporary GitHub tokens
          allow-repeats: false # This is the default

Finally, we will have such report.

Demo project is available on github. There is also Pull Request with code coverage implementation. That's all for today. Thank you for your attention!