Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Skeleton Php Laravel Package

nunomaduro/skeleton-php

A clean starting point for building PHP packages, from Nuno Maduro. It bundles a sensible structure, testing setup, and tooling so you can skip boilerplate and focus on writing your package's actual code.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a repository template, not a runtime dependency. There is no composer require step in an application — you start a new package from it. That distinction drives everything else: "using" it day-to-day means you own the code and the tooling, and upstream improvements will never arrive via composer update.

  1. Create your project. Click Use this template on GitHub, or clone and re-init history:
    git clone https://github.com/nunomaduro/skeleton-php my-package
    cd my-package && rm -rf .git && git init
    
    (composer create-project nunomaduro/skeleton-php my-package also works if you prefer that flow.)
  2. Run one aggressive find-and-replace before writing any code. Replace the vendor namespace, package slug, and class name everywhere: NunoMaduro\SkeletonPhp, skeleton-php, SkeletonPhp, nunomaduro. Touch composer.json (name, description, autoload, authors), src/, tests/, phpunit.xml.dist, phpstan.neon, the CS-fixer config, the README badges, and the workflow files.
  3. Install and verify the baseline is green before you add anything:
    composer install
    composer test      # or: vendor/bin/phpunit
    
  4. Replace the LICENSE. It ships MIT with Nuno's copyright line — swap in your name/company.

Where to look first: composer.json is the real entry point — its scripts block tells you exactly how the author expects you to test, lint and analyse. Then read src/ (one example class demonstrating namespace + style conventions) and tests/ (shows the mirror-structure convention). Finally skim .github/workflows/ so you know what will run on your first PR.

First use case: a small, framework-agnostic utility package — a parser, a value-object library, an API client — where you want tests, static analysis and CI green from commit one instead of bolting them on later. If you need a Laravel-aware package, this base still works but you'll add a service provider and Testbench (see below).

Implementation Patterns

Treat composer scripts as your workflow API. Rather than remembering tool flags, standardize on the shipped aliases and add a single aggregator:

"scripts": {
    "test": "phpunit",
    "analyse": "phpstan analyse",
    "lint": "php-cs-fixer fix",
    "check": ["@lint --dry-run", "@analyse", "@test"]
}

Then composer check locally, and the same command in CI — one definition of "passing". Add a Makefile or a pre-push git hook that calls it if you want a hard gate.

Mirror src/ in tests/. Keep the skeleton's convention: src/Contracts/Foo.php → tests/Contracts/FooTest.php, same namespace with Tests suffix. It makes "where is the test for this?" a non-question as the package grows.

Choose your integration story up front, because it changes composer.json and tests/ substantially:

  • Framework-agnostic (the default assumption). Keep src/ free of Laravel/Symfony imports. Put optional integrations behind suggest in composer.json and, if needed, a Bridge/ or Integrations/ namespace that is only autoloaded when the dependency exists.
  • Laravel package. Add require-dev: orchestra/testbench, extend Orchestra\Testbench\TestCase instead of plain PHPUnit's, register your provider there, and add extra.laravel.providers for auto-discovery. Keep the provider thin — it should only bind, not contain logic.
  • Standalone library with contracts. Create src/Contracts/ and src/Concerns/ early. Retrofitting interfaces after consumers exist is the most common source of breaking changes in small packages.

Add a CI matrix early. The shipped workflow typically runs one PHP version. Widen it to PHP versions × dependency versions:

strategy:
  fail-fast: false
  matrix:
    php: ['8.1', '8.2', '8.3']
    dependencies: [lowest, highest]

composer update --prefer-lowest is the single most effective way to catch a composer.json constraint that lies about what your code actually supports — and it's cheapest to fix on day one.

Keep dist files lean. The skeleton's .gitattributes already export-ignores tests and CI; verify it covers anything new you add (fixtures, docs, .github/). Consumers installing via --prefer-dist then get only src/.

Release with plain semver tags. No release tooling is shipped, and none is needed. Tag v1.0.0, register on Packagist, enable the GitHub webhook. Because you have zero dependents at the start, this is the cheapest moment to get your public API right.

Gotchas and Tips

  • The rename is never complete on the first pass. After templating, run grep -rniE "nunomaduro|skeleton" . --exclude-dir=vendor --exclude-dir=.git and check README badges, workflow name: fields, phpunit.xml.dist testsuite names, and the CS-fixer header_comment/name if present. A stale NunoMaduro\SkeletonPhp in a test namespace produces a confusing "class not found" rather than an obvious error.
  • Composer package names must be lowercase (vendor/name) and are matched by Packagist literally. PHP namespaces are case-sensitive on Linux CI but often not on macOS — a case mismatch that passes locally will fail in Actions.
  • Class-not-found after moving a file is almost always autoload. Run composer dump-autoload (add -o for optimized production autoloads). If you rely on classmap generation, remember new files need a re-dump.
  • phpunit.xml.dist vs phpunit.xml. The .dist file is committed; a local phpunit.xml should be gitignored and used for machine-specific overrides (coverage drivers, env vars). Don't put your local paths in the dist file.
  • Coverage silently reports 0% in CI because no driver is installed. Set XDEBUG_MODE=coverage in the workflow or install pcov in setup-php. Locally, that same env var is the usual fix.
  • PHPStan at max on the skeleton doesn't mean max on your code. As soon as you add real-world code — especially magic methods (__call, __get) or dynamic properties — you'll hit false positives. Fix them with @method/@property docblocks or a small stub file; resist dropping the level or growing a baseline, both of which quietly stop paying for themselves.
  • Don't promote a dev dependency to require. Adding laravel/framework or symfony/* as a hard runtime dependency collapses your installable audience. Use suggest, conflict, and optional bridges instead.
  • Bump the pinned Actions. Template repos age: actions/checkout@v2 and an old shivammathur/setup-php may already be deprecated and will warn or fail. Also set coverage: none on the non-coverage matrix jobs to cut CI time roughly in half.
  • CI-fails-locally-passes is a version problem, not a logic problem. Check the PHP matrix entry and run composer update --prefer-lowest locally; nine times out of ten it's a minimum-constraint or a polyfill you didn't know you were relying on.
  • Template drift is permanent. Nothing merges upstream fixes into your copy. If you care about keeping pace, add a comment in CONTRIBUTING.md noting the commit you forked from, and periodically diff against upstream main. Conversely: once you've diverged, delete the upstream badge and the "skeleton" wording from the README so it doesn't read like an abandoned fork.
  • Zero dependents is expected, not a red flag. Nothing depends on this because it isn't a library — it produces libraries. Judge it by whether your package's first release ships with green tests, static analysis and CI, which is precisely the thing it saves you a day of setup on.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console