# spatie/package-skeleton-php, a template whose real feature is the placeholder rewrite script

> spatie/package-skeleton-php is a template repository, not a dependency: you copy it, run a configure script that rewrites every placeholder in the tree, and publish the result as your own Composer package. It arrives with CI workflows, code style configuration and a licence file already in place, and with a set of maintainer promotional sections you have to remove before you publish.

**spatie/package-skeleton-php** — A skeleton repository for Spatie's PHP Packages

- Repository: https://github.com/spatie/package-skeleton-php
- Website: https://laravelpackage.training
- Stars: 555 · Forks: 142
- Language: PHP
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/spatie-package-skeleton-php

## A template repository, not a library you depend on

spatie/package-skeleton-php is a starting point rather than a package. The repository description calls it a skeleton repository for Spatie's PHP packages, and the README is written in the imperative, as a set of instructions to the person about to use it: press the Use template button at the top of the repo to create a new repository with the contents of the skeleton, run the configure script at the root of that new repository, and then build the package.

That distinction matters for how you evaluate it. You do not add this to your composer.json as a dependency, and there is nothing in it that runs in production. What you are looking at is a directory of files with a set of names that will not work as they are, plus a script that rewrites them. The whole value proposition is in how much of the surrounding scaffolding you get for free, and how much of it you have to delete.

The homepage is laravelpackage.training, and the README points there for a video course on creating packages, which tells you the intended audience: a PHP developer about to publish their own package and who wants the repository structure, the CI configuration, the licence file and the badge markup already in place.

The repository is not archived and the last push was on 2026-06-26. There are no GitHub releases, so there is no version to track: the template is consumed as a snapshot of the master branch at the moment you copy it, and every future change to the template reaches you only if you go and apply it by hand.

## The configure script and the placeholder system it rewrites

The mechanism at the centre of the template is a PHP script called configure.php that lives at the repository root. The README describes it as a script that will replace all placeholders throughout all the files, and the workflow is to run that script from the root of your new repository as step two, right after creating the repository from the template and before writing any code.

The placeholders are visible in the README itself, which is the best way to see what the script is designed to sweep. The title is a placeholder rather than a package name. The badge markup points at a placeholder vendor and package slug, and the Packagist badges and the GitHub Actions badge in the same block refer to placeholder paths. The installation section shows a composer require line with a placeholder vendor and package slug, and the usage example instantiates a placeholder class name and calls a placeholder method.

The consequence for the person using the template is that the README is not documentation of the finished package, it is documentation with holes in it, and the configure script is what fills those holes. Two practical points follow. First, the script is meant to run once, on a fresh copy, before you have written anything, because it rewrites files in place and is not documented as reversible. Second, the sweep covers all files, so anything you have already written that happens to contain a similar token could be rewritten too.

The script itself is listed in the top level entries of the repository alongside composer.json, src/, tests/ and the other project files, so it is a plain file in the tree rather than a hidden step.

## The file layout you inherit and what each piece is for

The top level entries describe the shape of a publishable PHP package, and reading them tells you what the template has already decided on your behalf.

There is src/, which is where the package code goes, and tests/, which is where the test suite goes. The test configuration is phpunit.xml.dist, and the README's Testing section tells you to run composer test. Style is handled by .php-cs-fixer.dist.php, and .editorconfig, .gitattributes and .gitignore handle formatting attributes and ignore rules. There is a CHANGELOG.md, which the README points to for what has changed recently, and LICENSE.md, which the README names as the licence file. A .github/ directory holds the workflows, and the README badge points at a workflow file named run-tests-pest.yml, so the template ships with a test workflow wired to Pest even though the phpunit configuration is also present.

Two things are conspicuously absent, and both are deliberate. There is no LICENSE.md content yet in the sense of a filled in licence, since the licence is stated in the README as The MIT License (MIT) with a pointer to the file. And there is no composer.json content in the README, because the file exists but its package name and description are among the things the configure script rewrites.

There is also no CHANGELOG content and no release process documented. The template gives you the file, not the practice.

## Turning the template into an installable package

After the configure step, the skeleton is a normal Composer package, and the README's installation and usage sections read as the finished documentation. Installing it through Composer is the standard require command with your vendor and package slug substituted:

```bash
```bash
composer require :vendor_slug/:package_slug
```
```

The usage example is deliberately minimal, a placeholder class instantiated and a placeholder method called:

```php
```php
$skeleton = new VendorName\Skeleton();
echo $skeleton->echoPhrase('Hello, VendorName!');
```
```

The skeleton is described as framework agnostic, which is the design goal of the template. It is not tied to Laravel, even though the maintainers point at Laravel Package Training for help, and even though the home of the course is laravelpackage.training.

The test command is:

```bash
```bash
composer test
```
```

What you should see at each step is straightforward. Pressing Use template gives you a repository that looks like the skeleton but does not work, because the class name in the usage example does not exist yet. Running the configure script from that directory replaces the placeholders so that the namespace and class name in the example match the package you are about to write. Running composer test then runs the suite in tests/ with the phpunit configuration, and it should pass on an untouched template, which is the check that the configure step did its job.

The order matters more than it looks. The README puts the configure step at number two, before the fun starts, and the script rewrites files throughout the tree.

## The promotional blocks the template ships with

The README contains content that is not about the package at all, and a user of the template needs to deal with it. There is a Support us section asking readers to support the maintainers, with a banner image and links to a paid products page, a paragraph about resources invested in open source packages, and a request to send a postcard from your hometown. There is a contact page link and a virtual postcard wall where received postcards are published.

The Credits section similarly lists a placeholder author link and a link to all contributors, and the Security Vulnerabilities section points at a relative path, ../../security/policy, which only resolves correctly when the package sits in the right place inside the organisation's GitHub structure. That path is a small but real signal: this template is designed for a package published under a GitHub organisation with a shared security policy, and a standalone package will have to fix that link.

There are also two HTML comment markers in the README, one opening and one closing with delete in the name, around the getting started block. Those mark the section as instructional content for the person setting up the package rather than documentation for the package's users, and they are the signal for which parts of the README are meant to be removed before publishing.

None of this is a defect, it is the maintainers' own template with their own links in it. But the work of turning the skeleton into your package includes deleting the advertising, and the number of places you have to remember to do that is the cost of this approach.

## Where the skeleton is the wrong tool

The first wrong case is a package that is not a library. The template is built for a package you publish to Packagist: it has a composer require line in the README, a namespace, a src/ directory, a class and a method, and a changelog. If what you want to build is an application, a site or a set of scripts, most of this scaffolding is decoration.

The second wrong case is a team that wants a Laravel specific package with the conventions that come with it. The README calls the skeleton framework agnostic, which is a deliberate choice, and the course it points to is Laravel focused. The trade is the other way around for the many packages that are Laravel extensions with service providers, facades and configuration publishing, none of which this template shows you.

The third wrong case is a repository that will live for a long time and needs to track upstream changes. There is no version, no release and no changelog discipline, and the template is copied once. When the maintainers improve the CI workflow or the php-cs-fixer configuration six months from now, there is no mechanism that brings that to you, and the merge is yours to do by hand.

The fourth wrong case is a package with unusual packaging needs. The template assumes a single namespace, one class entry point and a straightforward test suite. A package that ships multiple binaries, has a generated code step or needs a build matrix has to add that structure itself.

The fifth wrong case is simply not wanting a template. If you already know the conventions, the cost of the skeleton is the same as the cost of a composer init plus a handful of files you would write anyway, minus the benefit of the CI configuration that comes ready made.

## The skeleton against writing the package yourself

The realistic alternative is to create an empty Composer package and write the files. The difference in approach is about what you inherit rather than what you end up with.

Starting from nothing, you control the whole structure and nothing is imposed on you. You write composer.json with the name and description you want, you decide whether src/ is PSR-4 with the namespace you want, you choose PHPUnit or Pest, you add a CI workflow you understand, and you write the licence and changelog yourself. The cost is roughly an hour of setup decisions and the risk of missing a convention that a reviewer would have expected.

Starting from the skeleton, those decisions are already made and consistent. The namespace convention, the test runner invocation, the code style configuration, the git attributes, the ignore rules and the badge markup for Packagist and the test workflow are all there and already agree with each other. You also inherit the configure script, which is a genuinely useful piece of tooling that saves you from finding and replacing every namespace and reference by hand.

The cost of the skeleton is the deletion work. The promotional README sections, the placeholder-marked badges, the relative security policy path and the placeholder changelog all have to be reviewed before publishing, and the configure script is a one-shot transformation you cannot easily undo once you have written real code on top of it.

So the choice is between a small number of decisions you make once and a small number of deletions you make once. The skeleton wins when you are new to publishing a package, and when the defaults it encodes are the defaults you would have chosen.

## Conclusion

spatie/package-skeleton-php suits a PHP developer publishing their first package who wants the repository structure, the CI workflow and the style configuration already consistent, and who is willing to delete the promotional README sections and the placeholder badges before releasing. It does not suit a Laravel extension that needs service providers and configuration publishing, a long lived repository that wants to track upstream template improvements, or an application rather than a library. Before you publish, run the configure script at the root of a fresh copy before writing any code, confirm composer test passes on the untouched template, and review the README for the maintainer support sections and the relative security policy path, both of which need to change for a package outside that organisation.

## FAQ

### How do I set up a new package from spatie/package-skeleton-php?

Press the Use template button at the top of the repository to create a new repository with the skeleton contents, then run the configure script at its root to replace all placeholders throughout all the files before writing any code.

### What does the configure.php script do?

It is a script at the repository root that replaces all placeholders throughout all the files, covering the namespace, class names, package name and the badge and installation references in the README.

### How do I run the tests in the PHP package skeleton?

The README lists composer test as the command, and the repository ships a phpunit.xml.dist configuration alongside a tests/ directory.

### Is the spatie package skeleton tied to Laravel?

The README describes it as a skeleton for a framework agnostic package. The maintainers point to Laravel Package Training for help with creating packages, but the skeleton itself is not Laravel specific.

### What licence does the spatie package skeleton use?

The README states The MIT License (MIT) and points to the License File, LICENSE.md, which is listed among the top level repository entries.

## Sources

- [Issues](https://github.com/spatie/package-skeleton-php/issues)
- [License: MIT](https://github.com/spatie/package-skeleton-php/blob/main/LICENSE)
- [Project website](https://laravelpackage.training)
- [README](https://github.com/spatie/package-skeleton-php/blob/main/README.md)
- [spatie/package-skeleton-php on GitHub](https://github.com/spatie/package-skeleton-php)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/spatie-package-skeleton-php
