Sei interessato ai nostri servizi di consulenza?

1 Clicca nella sezione contatti
2 Compila il form
3 Ti ricontattiamo

Se hai bisogno urgente del nostro intervento puoi contattarci al numero 370 148 9430

RENOR & Partners

I nostri orari
Lun-Ven 9:00AM - 18:PM

From Zero to Packagist: Publishing a PHP Library on Composer

by Simone Renzi / May 12, 2025
Post Image

This post is also available in: Italiano (Italian)

Publishing a Composer package means making your code installable with a simple CLI command.

composer require vendor/package

and keeping updates, versions, and dependencies under control.
To follow the guide, we need:

  • Git version 2.0 or higher installed and configured
  • A GitHub account and a token with repo permission
  • Composer version 2.5 or higher installed $PATH
  • A free account on Packagist.org (registered via GitHub using OAuth)

Referring back to the article where we explored how to develop a descriptive statistics library, the repository structure was as follows:

descriptive-statistics-php/
├─ composer.json
├─ composer.lock
├─ phpunit.xml
├─ examples/
│  └─ geometric_mean_demo.php
│  └─ harmonic_mean_demo.php
│  └─ iqr_demo.php
│  └─ mad_demo.php
│  └─ mean_demo.php
│  └─ median_demo.php
│  └─ min_max_demo.php
│  └─ mode_demo.php
│  └─ percentile_demo.php
│  └─ range_demo.php
│  └─ standard_deviation_demo.php
│  └─ trimmed_mean_demo.php
│  └─ variance_demo.php
├─ src/
│  └─ DescriptiveStats.php
├─ tests/
│  └─ DescriptiveStatsTest.php
└─ vendor/
    └─ autoload.php

In the terminal, inside the project folder, we type the command:

composer init

Composer will ask us for some information, starting with the package name, which is expressed as vendor/package-name. In my case, since my account is named “thesimon82”, the package name will be thesimon82/descriptive-statistics. We confirm by pressing Enter.

Now we are asked for the description—
for this project, we’ll enter: Lightweight PHP class for descriptive statistics.

Next is the author field: if we’ve already configured it previously, Composer will show a default author; otherwise, we enter our name and surname manually.

Minimum stability: if it’s a stable release, we enter “stable”.

For Package Type, we enter: “library”.

In license: MIT

We are then asked whether we want to add, one by one, the production packages (require) that the library needs in order to work. In this case, the class is stand-alone (it only uses native PHP functions), so we don’t have any dependencies other than the minimum PHP version—therefore, we can safely answer “no”.


If you’re thinking about PHPUnit for testing the class methods, remember that PHPUnit is only used during development and testing, so it should be placed in require-dev, not in require.


This way, anyone installing the package in a production environment won’t have PHPUnit among their runtime dependencies.

The next prompt, in fact, asks whether we intend to define our development dependencies interactively (require-dev). Here, we type “yes”, and Composer will then prompt us to enter the name of the package.


We now type:


phpunit/phpunit


Composer will return a list of matching packages—select phpunit/phpunit by entering the corresponding number.


Then it will ask which version to include.
We simply press Enter to install the latest available version.

It will now ask if we want to install any additional dependency packages, but since we don’t have any others, we simply press Enter.
It will now ask if we want to add a PSR-4 autoload mapping for the classes, suggesting to map the namespace Thesimon82\DescriptiveStatistics.
However, in our project we used Renor\\Statistics, so we type n to skip the proposal.

Finally, we obtain a summary of the entered data:

{
    "name": "thesimon82/descriptive-statistics",
    "description": "Lightweight PHP class for descriptive statistics",
    "type": "library",
    "require-dev": {
        "phpunit/phpunit": "^12.1"
    },
    "license": "MIT",
    "authors": [
        {
            "name": "Simone Renzi",
            "email": "info@simonerenzi.com"
        }
    ],
    "minimum-stability": "stable",
    "require": {},
    "autoload": {
        "psr-4": {
            "Renor\\Statistics\\": "src/"
        }
    }
}

Do you confirm generation [yes]? 

Here we review the data, and if everything is correct, we confirm with “yes” and proceed to install the dependencies.

At the end, we type the command:

composer dump-autoload to regenerate the class autoloader.

It’s time to make the commit using the following series of commands:

git add .
git commit -m "feat: first stable release"
git tag v1.0.0
git push origin main v1.0.0

Now we move to GitHub, and we should see our repository with the latest commit—
in my case: thesimon82 feat: first stable release.

We copy the URL from the address bar at the top and head over to Packagist.

Click on “Submit” at the top, then paste the GitHub link you copied into the text field. Press the Check button.
At this point, Packagist will analyze the composer.json file and immediately display the package details.
Clicking the “Submit” button at the bottom will create the package.

We’re done—now it’s time to test if everything works correctly.

Create a new folder named “Stats Test”, then open the terminal and navigate into that folder using:
Here we type:

composer require thesimon82/descriptive-statistics

Now import this folder into your development environment and create a new file in the root directory named: test.php.

<?php
require_once 'vendor/autoload.php';
use Renor\Statistics\DescriptiveStats;

$stats = new DescriptiveStats([1, 2, 3, 4, 5,6.22]);
echo $stats->mean();

As we can see, we’re including the Composer-generated autoloader, which makes all classes mapped via PSR-4 available automatically—without the need for additional require statements.

We define the namespace used in the DescriptiveStats class and instantiate a new object with a numeric dataset.
You’ll recall that the class constructor automatically filters out non-numeric values, reindexes the array keys, and throws an exception if the resulting dataset is empty.


Then, the method mean()is called, which computes the mean and displays it on the screen.

If you now run the command: php test.php from the terminal, and if everything was done correctly, you should see the result:
3.5366666666667,
which is precisely the approximate mean of the values we passed to the class constructor.

Conclusions

We’ve seen how to transform a simple PHP class into a Composer package distributed on Packagist.
Now the library can be installed with a single command, benefits from semantic versioning, continuous integration, and automation that updates the Packagist listing automatically with every new tag.

From this point on, the lifecycle is straightforward:
you implement a feature, run the tests, increment the version, create the tag, and push it to GitHub.
Packagist and Composer will take care of the rest!

Simone Renzi
Seguimi

Scegli un'area

CONTATTACI

Ti risponderemo entro 24 ore

TORNA SU