How to audit PHP dependencies (practical guide)

A practical, repeatable guide to auditing PHP Composer dependencies for security, licensing and maintenance risk. Includes scripts and CI checks.

Steven Richardson
Steven Richardson
· 5 min read

Keeping your project's dependencies healthy is one of the highest-leverage tasks you can do as an engineer. Old, vulnerable, or abandoned packages are a common source of incidents and maintenance debt. This guide shows a short, repeatable workflow for auditing Composer-based PHP projects: automated checks, lightweight manual signals, example scripts, and how to integrate checks into CI.

Why dependency auditing matters#

Dependencies bring value but also risk: known vulnerabilities, incompatible updates, bad licenses, or abandoned packages. Regular audits help you:

  • Find known CVEs and upgrade safely.
  • Avoid licenses that may be problematic for distribution.
  • Detect low-maintenance or abandoned packages before they become a blocker.

Automated checks — your first line of defence#

  1. composer audit / security advisories

Modern Composer setups can surface advisories. If you're on Composer 2+ and have the platform, run:

composer audit

It lists advisories for installed packages. This is quick and should be part of your CI.

  1. GitHub Dependabot / Dependabot alerts

If your repository is on GitHub, enable Dependabot and vulnerability alerts. Dependabot opens PRs to update vulnerable packages and is low friction.

  1. Snyk or Roave Security Advisories

Snyk provides a comprehensive DB and policy controls; Roave's security-advisories package can be used for stricter CI blocking. Snyk is a paid service but has free tiers.

  1. composer outdated and activity checks

Find outdated packages and their latest versions:

composer outdated -D

Beyond the version number, inspect the project's repository: latest commit date, open issues, number of maintainers and test coverage are useful signals.

License checks#

If license compatibility matters for your product, run a license audit. There are community tools to extract license info; at minimum, ensure you record each package license and block any unexpected copyleft licenses.

A simple audit script (practical)#

Save this small script as tools/audit-deps.sh. It combines Composer checks and quick GitHub lookups.

#!/usr/bin/env bash
set -euo pipefail

# composer audit (if available)
if composer audit >/dev/null 2>&1; then
  echo "Running composer audit..."
  composer audit || true
else
  echo "composer audit not available"
fi

# list outdated packages
echo "\nOutdated packages (composer outdated):"
composer outdated --direct || true

# simple maintenance signal: last commit and stars (requires gh cli)
if command -v gh >/dev/null 2>&1; then
  echo "\nMaintainer signals for direct dependencies:"
  composer show --direct --format=json | jq -r '.installed[] | .name' | while read pkg; do
    repo=$(composer show -N $pkg 2>/dev/null || echo "")
    if [ -n "$repo" ]; then
      owner_repo=$repo
      echo "Package: $pkg"
      gh repo view $owner_repo --json stargazersCount,pushedAt --jq '. | "stars: \(.stargazersCount), last push: \(.pushedAt)"' || echo "  info not available"
    fi
  done
fi

Notes: the gh repo view step relies on composer show providing the repository field or on a mapping; you can adapt this to your project conventions.

CI integration (introduce fail/warn gates)#

  • Run composer audit in CI and fail if any high severity advisories exist.
  • Run composer outdated --direct and optionally add a weekly job to open PRs or create reminders.
  • Add a small GitHub Action that runs the audit script and posts a summary comment on the PR if packages changed.

Interpreting results & remediation#

  • For a vulnerability with a patch release: prefer upgrading to the patched version, run the test suite, and merge. If the upgrade is breaking, evaluate the impact and consider a backport or mitigation.
  • For unmaintained packages: plan replacement or fork. Consider the cost of maintenance vs replacement.
  • For license issues: consult legal / choose replacement.

Example: quick actionable checklist#

  1. Run composer audit and fix high/critical advisories.
  2. Run composer outdated --direct and review each package's changelog.
  3. Inspect any package that hasn't had a commit in >12 months — consider replacing.
  4. Add an automated GitHub Action to run the steps above on PRs and on a weekly schedule.

Conclusion#

Auditing dependencies is a short recurring task that pays off in reliability and security. Start by automating the basic checks and add focused manual review for packages that fail your automated signals.

To catch dependency issues even earlier, add a composer audit step to your Git pre-commit hook alongside code formatting — running Laravel Pint automatically with pre-commit hooks shows the hook pattern you can extend. For the broader picture of code quality tooling — Pint, Larastan, Rector, and testing — see The Complete Laravel Developer Toolchain for 2026.

FAQ#

Why is auditing dependencies important for production Laravel apps?

Dependencies bring both functionality and risk. Outdated packages can contain known vulnerabilities (CVEs) that attackers exploit, abandoned packages become unmaintained security liabilities, and problematic licenses can create legal issues. Regular audits catch these problems before they become incidents. A composer audit step in CI takes 20 seconds and can prevent a production breach.

What's the difference between Snyk, GitHub Dependabot, and composer audit?

composer audit is built into Composer 2+ and provides local security checks using public vulnerability databases. Dependabot is GitHub's native solution that opens PRs automatically when vulnerabilities are found, reducing friction for developers. Snyk is a commercial platform that offers more control, policy enforcement, and integrations with your broader DevOps workflow. For most Laravel projects, composer audit in CI plus Dependabot alerts covers the essentials.

How do I know if a package is truly unmaintained?

Check the package's repository for (1) the last commit date — anything older than 12 months with no activity is a red flag, (2) open issues that go unanswered, (3) the number of active maintainers, and (4) recent release cadence. A package with one maintainer who hasn't committed in a year is a dependency risk. The bash script in this article extracts these signals via GitHub metadata so you can flag them in your audit.

Should I use SemVer pinning or allow updates in my composer.json?

Pin major versions (^8.0) to avoid accidental breaking changes in CI, but stay on the latest minor and patch releases. Composer prefers the most compatible version that satisfies the constraint. Manually review major version bumps before upgrading, but let Dependabot handle minor and patch updates automatically. This balance catches security fixes quickly without breaking your build.

Steven Richardson
Steven Richardson

CTO at Digitonic. Writing about Laravel, architecture, and the craft of leading software teams from the west coast of Scotland.