php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

Creating a PHP Library With Packagist

Posted by on February 2, 2023

One of the most powerful parts of writing web applications in the modern era is how we can quickly create an application that does a lot with just a little bit of our code by using open-source packages. Have you ever wanted to contribute a package to the community for others to use? Maybe you want to modify an existing package for your personal use.

If you have, you’re in luck because, in this article, we’ll discuss how we can create a package and list it on Packagist so anyone can use it in their project.

Getting Started

First of all, we need to pick a package name. I know, I know, naming things is hard but this is important because we can not change our package name once we’ve picked it so it’s important to get it right.

Package names can contain any alpha-numeric characters plus underscore(_), period(.), and hyphen (-). It consists of a vendor name and a project name and must be globally unique. For some projects with a unique name, like PHPUnit, the vendor name and the project name are the same.

Once the name has been determined we need to create a working directory (if we haven’t already) and cd into it. Then we’re going to install Composer into our newly created directory (unless we have it installed globally). We talked about this in our article on Composer, and we prefer to have a copy of Composer in each project we’re working on.

For this example, we’ll create a library of Value Objects others can easily use in their software.

mkdir ScottsValueObjects
cd ScottsValueObjects
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

Next, we’re going to initialize our package. This is done by running php composer.phar init. Composer will then walk us through a series of questions that will help it build a composer.json for us. Accept the defaults when possible and provide a helpful description of the package.

My composer.json looks like the following.

{
"name": "scottkeckwarren/scottsvalueobjects",
"description": "Value objects for everyday use.",
"type": "library",
"require-dev": {
    "phpunit/phpunit": "^9"
},
"license": "MIT",
"autoload": {
    "psr-4": {
        "ScottsValueObjects\\": "src/"
    }
},
"authors": [
    {
        "name": "Scott Keck-Warren",
        "email": "warren5236@gmail.com"
    }
],
"require": {}
}

At this point, we have a basic setup for our package of value objects. Now we just need to create the classes. We’re going to start with the following in “src/Name/FirstName.php”.

<?php namespace ScottsValueObjects\Name; class FirstName { protected string $value; public function __construct(string $value) { $this->value = $value;
}

public function length(): int
{
    return strlen($this->value);
}
}

Now we have a very basic library created but we need to get it out onto the Internet so others can use it. To do this we first need to push our source code to a supported public repository. As of this writing/recording, we can use GitHub, BitBucket, GitLab, or Gitea. We’re going to use GitHub to create a new repository with a name that’s similar to what our package is named in our composer.json.

Now we need to create our initial commit.

This is done by first calling git init to initialize the local directory for Git usage. Then we need to commit the “composer.json” and“composer.lock” files along with the “src” directory.

git add composer.json composer.lock src
git commit -m "Initial Commit"
[master (root-commit) 930d4fe] Initial Commit
 3 files changed, 1787 insertions(+)
 create mode 100644 composer.json
 create mode 100644 composer.lock
 create mode 100644 src/Name/FirstName.php

Then we’ll set the origin to our new repository and push the change. I grabbed these commands from the instructions GitHub gives us after we create our repository.

git remote add origin git@github.com:warren5236/ScottsValueObjects.git
git branch -M main
git push -u origin main

Now we need to list our package so others can find it.

We’ll discuss that more after this word from our sponsors.

Introducing Packagist

Now we need to introduce the next piece to this system, and that’s https://Packagist.org. Packagist.org is the default Composer package repository. That means that it allows you to enter a package name like phpunit/phpunit into Composer, and Composer will check with Packagist to see if that’s a valid package name and which public repository it should be using. Packagist allows us to abstract away the complexity of if our package is being hosted on GitHub or BitBucket, provides a list of versions of the package, and stats about our package.

To add your package to Packagist, the first thing you’ll need is an account. Accounts are free.

After you’ve been registered and have signed in, click the “Submit” link in the top navigation. This will bring you to a form that asks for the URL of your public package. Enter the URL to the project’s GitHub main page and click “Check”. The site will make sure there’s a valid composer.json at the URL you provided and make sure you’re happy with the name. Then click “Submit”. At this point, Packagist will index our project, and eventually, the screen will refresh to show the information about our project.

Congratulation you now have a library that anyone in the community can use. Now comes the process of maintaining the project.

Versions

Now that you’re a package maintainer, we need to get some versions of our package out for others to use. Composer uses Semantic Versioning (SemVer) to determine how to determine if it can safely upgrade to new versions of packages as they are released. We should be using SemVer to label our releases. We have an article on SemVer you should read before you do too much to your package.

I’m going to say that my example package is both feature incomplete and likely to change, so I’m going to use the 0 major release number to indicate this. This will be version 0.1.0 of our package.

Now to indicate this, we’re going to create a Git tag. A Git tag allows us to create what is essentially a label for a specific commit of the source code repository. Tags can’t be changed, so we can always reference the tag and get a consistent source tree. To create a tag we just call git tag . In this case, it will be git tag 0.1.0.

git tag 0.1.0

We can also call git tag to see all the tags.

% git tag
0.1.0

And use git checkout with the name of the tag to switch to that tagged version.

Now annoyingly a tag is kept locally unless we explicitly push with the --tag switch.

% git push --tag 
Total 0 (delta 0), reused 0 (delta 0)
To github.com:warren5236/ScottsValueObjects.git
 * [new tag]         0.1.0 -> 0.1.0

After we do so, we can wait for Packagist to reindex our project, which it does automatically every five minutes, or we can press the “Update” button to force a reindex.

After this update has been completed, we’ll see our new version listed in the versions on the sidebar.

Using In Another Project

Using our package in another project now is simple. We just need to use the require command.

php composer.phar require scottkeckwarren/scottsvalueobjects ^0

You might have noticed the ^0 at the end of this string. This indicates that Composer should stay in the “0” major branch as major branches should remain stable and not have breaking changes.

Platform Packages

Another important part of Composer is the ability to specify the version of system-level packages like PHP and PHP extensions that are supported and required. Composer calls these platform packages. They’re not like standard packages we might install and are instead virtual packages that we’re saying must exist for our package to be installed.

We can see these by running php composer.phar show --platform.

For our example, let’s say we’re requiring PHP 8.1 and mbstring because we need 8.1’s Enum support and some of the multi-byte string functions, respectively. To do this, we’ll add these to the “require” array of our “composer.json”.

"require": {
  "php": ">=8.1",
  "ext-mbstring": ">=8.1"
},

Now if we attempt to run an install, require, or upgrade and we don’t have these packages installed, we’ll get an error.

php composer.phar update 
Loading composer repositories with package information
Info from https://repo.packagist.org: #StandWithUkraine
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
- Root composer.json requires php >=8.1 but your php version (8.0.6) does not satisfy that requirement.

This is super helpful for consumers of your package because they won’t accidentally end up with a version of your package that doesn’t run in their environment.

What You Need to Know

  • Creating your own open-source package is surprisingly easy
  • Composer uses Packigist to determine how to find it
  • We use tags to determine versions

 

php[architect] is an industry-leading publication focused on the PHP language. We are for developers, written by developers. Publishing great PHP content since 2002 and dedicated to providing educational material to the PHP programming community.

Subscribe today at https://phpa.me/signup
Twitter: @phparch
Mastodon: phparch.social@editor
Facebook: https://phpa.me/facebook
LinkedIn: https://phpa.me/linkedin

This video is sponsored by Honeybadger


Tags:
 

Leave a comment

Use the form below to leave a comment: