Skip to Content
Course content

169: Creating and Publishing a Composer Package

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you've probably used dozens of packages from Packagist. But there's a certain "click" moment in your growth as a PHP developer when you stop just consuming code and start distributing it. It forces you to think about API stability and versioning in a way that internal project code doesn't.

How do I actually structure my folders so Composer knows where the code is?

I see a lot of beginners struggle here because they try to overcomplicate the directory tree. Keep it lean. For a package, I always recommend a src/ folder for your actual logic and a tests/ folder for your PHPUnit suites.

Let's say we're building a package called CaseConverter that handles some niche string transformations for a CMS. Your structure should look like this:


case-converter/
├── src/
│   └── Converter.php
├── tests/
│   └── ConverterTest.php
└── composer.json

The magic happens in the composer.json file using the autoload key. You use PSR-4 to tell Composer: "Any class in the App\CaseConverter namespace lives inside the src/ directory."


{
    "name": "yourname/case-converter",
    "autoload": {
        "psr-4": {
            "YourName\\CaseConverter\\": "src/"
        }
    }
}


What actually goes into the composer.json for a public package?

When you're building a private app, your composer.json is basically a shopping list. When you're building a package, it's a manifesto. You need to be explicit about who you are and what your code requires to run.

The name is the most critical part. It must follow the vendor/project format. If your GitHub username is dev-jane, your package is dev-jane/case-converter. Also, don't skip the license field; if you don't specify one (like MIT), many corporate developers won't touch your code because their legal teams won't allow it.

Here is how I'd write the full file for our converter:


{
    "name": "dev-jane/case-converter",
    "description": "A lightweight utility to convert strings to business-specific casing.",
    "type": "library",
    "license": "MIT",
    "require": {
        "php": "^8.1"
    },
    "autoload": {
        "psr-4": {
            "DevJane\\CaseConverter\\": "src/"
        }
    }
}


How do I get my code onto Packagist so others can install it?

Packagist doesn't actually host your code; it's just a fancy index that points to your Git repository. I usually use GitHub because it's the industry standard, but GitLab or Bitbucket work too.

First, push your code to a public repository. Now, here is the part that trips people up: Composer versions are tied to Git tags. If you just push to main, people can install your dev-main branch, but that's unstable. To release version 1.0.0, you need to tag it in Git:

git tag -a v1.0.0 -m "First stable release"
git push origin v1.0.0

Once the code is pushed, you head over to Packagist.org, log in with GitHub, and paste your repo URL. From that point on, you can set up a GitHub Action or a webhook so that every time you push a new tag, Packagist updates automatically. It's a great feeling the first time you run composer require dev-jane/case-converter in a completely different project and see it actually work.




📋 Practical Task

Build and Local-Test a "SocialMediaHandleFormatter" Package

Instead of publishing to the web immediately, you're going to create a package and test it locally using a "path repository". This is how pros test packages before releasing them to the public.

  • The Package: Create a folder named social-formatter. Inside, create a src/Formatter.php class with a method formatHandle(string $username) that ensures a username starts with '@' and removes any illegal characters (like spaces or emojis).
  • The Configuration: Create a composer.json for this package. Give it the name local/social-formatter and set up the PSR-4 autoloading for the namespace Local\\SocialFormatter\\.
  • The Implementation: Create a second, separate folder called test-app. In this folder, run composer init.
  • The Local Link: In the test-app/composer.json, add a repositories section to tell Composer to look at your local folder instead of Packagist:
    "repositories": [
                {
                    "type": "path",
                    "url": "../social-formatter"
                }
            ]
  • The Test: Run composer require local/social-formatter inside test-app. Create a index.php file that instantiates your Formatter class and prints a cleaned-up handle to the screen.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.