As you might know, packages or libraries came to make developers life’s easier where the package is a piece of reusable code that can be dropped into any application and be used without any tinkering to add functionality to that code.
So Today, we will introduce the right way to create and publish a PHP composer package step by step.
Pre-requirements:
- Composer.
- Project package code.
- Github Account.
- Packagist Account.
What is Composer:
- Composer is a dependency manager tool for PHP packages or libraries.
- It’s inspired by npm (Node.js) and bundler (Ruby).
- It uses its official repository packages (packagist.org) to know where the code of package is located which is often in Github (public repository).
Prepare the Package:
Let’s pretend that we have a project structure like this and we would like to make it as a package.
sfmok ~/my-package> tree
.
├── src
│ └── HelloWorld.php
└── tests
└── HelloWorldTest.php
I know that is a silly package idea but the way to share it it’s the same whatever the kind and/or size of the package.
So let’s begin, the first step is to keep composer generating the config package file composer.json, so move over to the project package path via terminal and run:
composer init
The command has a sprinkling of interfaction questions and we need to treat them carefully.
- Make sure that your package name follows this convention “<vendor>/<name>” where:
- <vendor> is often the author name that should be unique (e.g. the username on Github : sfmok)
- <name> is the name of the package itself e.g: hello-world.
- Package Type:
- library: This is the default. It will copy the files to
vendor
. - project: This denotes a project rather than a library. For example application shells like the Symfony standard edition, CMSs like the SilverStripe installer or full fledged applications distributed as packages. This can for example be used by IDEs to provide listings of projects to initialize when creating a new workspace.
- metapackage: An empty package that contains requirements and will trigger their installation, but contains no files and will not write anything to the filesystem. As such, it does not require a dist or source key to be installable.
- composer-plugin: A package of type
composer-plugin
may provide an installer for other packages that have a custom type. Read more in the dedicated article. - symfony-bundle: Used for Symfony framework bundles to let Flex automatically enable the bundle when it’s installed.
- library: This is the default. It will copy the files to
- Dependencies
require
andrequire-dev
: We tell Composer which packages our project depends on such as php for all envs and phpunit for dev env. - PSR-4 autoload mapping : The namespace of your package that will be mapped to your root package (
src
) . By default inspired by the package name.
Project package tree after confirming the generation:
sfmok ~/my-package> tree
.
├── composer.json
├── src
│ └── HelloWord.php
└── tests
└── HelloWordTest.php
As you can see the new composer.json file has been added to our project package.
Unfortunately, thecomposer init
command does not support all configuration parameters such as:
You can add them manually by updating composer.json file. In our example package, I’ve added PSR-4 autoload-dev namespace.
{
"name": "sfmok/hello-world",
"description": "A hello world example package.",
"type": "library",
"require": {
"php": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"license": "MIT",
"autoload": {
"psr-4": {
"Sfmok\\HelloWorld\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Sfmok\\HelloWorld\\Tests\\": "tests/"
}
},
"authors": [
{
"name": "Mokhtar Tlili",
"email": "tlili.mokhtar@gmail.com"
}
]
}
Just before jumping to the next step, there are a few things we must take care of them:
- Make sure your tests are green.
- Add a README file where you describe the package plus some details about how to use it.
- Add .gitignore file to skip tracking unwanted files and directories such as vendor folder for dependencies packages, IDE folder (often hidden) and so on …
- Add .gitattributes file (Optionally), it uses for many useful cases like ignoring exporting specific files or directories when composer archiving the package, it’s content are relative paths followed by an attribute e.g.:
/tests export-ignore
Publish Package:
To publish the package officially, do these two steps:
- Host the package in a public repository (on Github for example).
- Register the package on Packagist.
Host the package on Github:
Create a new repository on Github and use the package name as the name of the repository (in our case “hello-world”).
Once the repository is created, we need to versioning our package by initializing a local repository and then track and commit the whole project.
sfmok ~/my-package> git init --initial-branch=main
Initialized empty Git repository in /Users/sfmok/projects/my-package/.git/
sfmok ~/my-package (main)> git add .
sfmok ~/my-package (main)> git commit -m "first commit"
[main (root-commit) 25b914a] first commit
6 files changed, 68 insertions(+)
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 README.md
create mode 100644 composer.json
create mode 100644 src/HelloWorld.php
create mode 100644 tests/HelloWorldTest.php
Afterwards, we will publish the package to Github by pushing the main branch to our public Github repository.
sfmok ~/my-package (main)> git remote add origin https://github.com/sfmok/hello-world.git
sfmok ~/my-package (main)> git push -u origin main
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (10/10), 1.29 KiB | 661.00 KiB/s, done.
Total 10 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sfmok/hello-world.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Let’s take a look at our repo <https://github.com/sfmok/hello-world>
Cool isn’t it?
Last step on Github we need to create the first release version v1.0.0. To do that let’s go to the release section and create a new release:
Each release needs a tag so we must create a tag first.
Then fill out the release form and publish:
Well done. Our first release has been published on Github and is ready to be registered in Packagist.
Register the package on Packagist:
In order to register the package we need to log into Packagist (better to login via Github account).
Once you logged in we have to submit our package.
Once you clicked on the check button Packagist will do some checking to verify unique package name that’s why I noted early that the vendor part in package name should be unique.
If the checking passes well, you should get a button to submit the package.
After submitting, Packagist needs to crawl the package this operation might take a few minutes afterwards it will be published.
Congratulation! we’ve finally published our package officially and it’s ready to use in any PHP project.
Let’s give it a try by requiring the package in a PHP project composer require sfmok/hello-world
Awesome, It works and Composer detects our first release v1.0.0.
Done! Hope you enjoyed reading it.
Cheers!!
thank you so much sir this is very good artcile
Nice post, thanks for sharing 🙏