How to Build a Composer Package for PHP or Laravel

Shaon Majumder
7 min readOct 1, 2023

--

In the ever-expanding world of web development, creating reusable and shareable packages is a fundamental practice. These packages not only streamline your own development process but also contribute to the open-source community by providing solutions to common problems.

If you’ve ever wondered how to create your own PHP or Laravel package and share it with the world, you’re in the right place. This tutorial will take you through the essential steps of building and publishing your Composer package.

Composer, the PHP package manager, has become an indispensable tool for developers. It simplifies dependency management, making it easier to incorporate external libraries and packages into your projects. By building your own Composer package, you’ll join the ranks of developers who have contributed to this powerful ecosystem.

In this tutorial, we will guide you through the process of creating a Composer package for PHP or Laravel, covering key aspects such as directory structure, service providers, facades, and configuration files. By the end, you’ll have a clear understanding of how to package your code and share it with the world, enriching the developer community.

Let’s dive into the world of package development and unleash the potential of your PHP and Laravel projects.

(Prerequisites, Understand the directory structure and how Laravel boots a package)

Step 1: Create a New Directory for Your Package

Create a new directory for your package. You can name it based on your package’s functionality or any naming convention you prefer. For this example, we’ll use the name examplename.

mkdir examplename
cd examplename

Step 2: Define the Directory Structure

Your directory structure should follow the PSR-4 standard, which is commonly used in PHP package development. Here’s the recommended structure:

Your directory structure should look like this and should follow the PSR-4 standard. -

examplename
├── composer.json
├── LICENSE
├── README.md
└── src
├── config
│ └── examplename.php
├── ExampleName.php
├── Facades
│ └── ExampleNameFacade.php
└── ServiceProvider
└── ExampleNameServiceProvider.php
  • composer.json: The main configuration file for your package.
  • LICENSE: Your package's license file (e.g., MIT, Apache 2.0).
  • README.md: A README file explaining your package's usage and installation.
  • src: The source directory where your package's code resides.
  • config: The directory for your package's configuration files.
  • ExampleName.php: The main class file for your package.
  • Facades: The directory for your package's facades (if needed).
  • ServiceProvider: The directory for your package's service provider.

Step 3: Initialize a Git Repository

Initialize a Git repository in your package directory to track changes.

git init

Step 4: Create Package Files

Now, let’s create the essential package files.

  • composer.json: This file contains metadata about your package, its dependencies, and autoloading information. Here’s a basic example:
{
"name": "your-vendor/your-package",
"description": "Description of Your Package",
"keywords": ["your-keyword-1", "your-keyword-2"],
"homepage": "https://your-package-url.com",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "your@email.com"
}
],
"require": {
"php": "^7.2",
"illuminate/support": "^8.0|^9.0|^10.0" // Adjust based on your Laravel version
},
"autoload": {
"psr-4": {
"YourNamespace\\": "src/"
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
  • LICENSE: Add the text of your chosen license (e.g., MIT or Apache 2.0) to this file.
  • README.md: Create a README file explaining how to install and use your package, including examples.

Now, your package directory is set up with the necessary files and structure. You’re ready to move on to creating your package’s code, service provider, facades, and configuration files.

Your service provider `ExampleNameServiceProvider.php` should look like this -

namespace ExampleName\ServiceProvider;

use Illuminate\Support\ServiceProvider;

class ExampleNameServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('example-name', function () {
return new \ExampleName\ExampleName(); // Replace with your actual instantiation logic.
});
}

public function boot()
{
// Publish the configuration file during package boot
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/examplename.php' => config_path('examplename.php'),
], 'config');
}
}
}

Explanation :

  1. Namespace: The namespace ExampleName\ServiceProvider; line at the beginning of the file specifies the namespace for this PHP class. Namespaces are used to organize code and prevent naming conflicts with other classes or libraries.
  2. Extending ServiceProvider: This class ExampleNameServiceProvider extends ServiceProvider. In Laravel, service providers are a fundamental part of the service container and are used for various tasks such as binding services, registering configurations, and more.
  3. Register Method: The register method is called when the service provider is registered. In this method, we are binding the name 'example-name' to a closure function. This binding essentially tells Laravel that when someone asks for 'example-name', it should create and return a new instance of \ExampleName\ExampleName(). You should replace \ExampleName\ExampleName() with the actual logic for creating an instance of your package.
  4. Boot Method: The boot method is called after all other service providers have been registered. In this method, we are checking if the application is running in the console (i.e., through the command line). If it is, we publish a configuration file. This configuration file is typically used to configure settings for your package. The publishes method specifies the source and destination of the configuration file. __DIR__.'/../config/examplename.php' specifies the source file location. config_path('examplename.php') specifies the destination file location within the Laravel application's config directory.
  5. The second argument 'config' is called a tag. It lets you specify which group of publishable resources you want to publish. In this case, it's the configuration file.
  6. So, when a user of your package runs an Artisan command like php artisan vendor:publish --tag=config, it will copy your package's configuration file to the Laravel config directory, allowing users to customize the package's behavior.

Remember to replace ExampleName, example-name, and the instantiation logic with your actual package names and code. These help notes should give beginners an overview of what this Laravel service provider does in the context of a package.

Your Facade `ExampleNameFacade.php` should look like this -

<?php

namespace ExampleName\Facades;

use Illuminate\Support\Facades\Facade;

class ExampleNameFacade extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
// This method specifies how Laravel should resolve this facade.
// The string 'example-name' should match the key you used to bind the service in the service provider.
return 'example-name';
}
}

Explanation :

  1. Facade: Think of this as a magical remote control for your package. It lets you access all the cool features of your package without worrying about what’s going on behind the scenes.
  2. getFacadeAccessor Method: This method is like the instruction manual for your remote control. When you use the remote control (facade) like ExampleNameFacade::someMethod(), Laravel looks up these instructions. The 'example-name' should match the name you used when setting up your package. It's how Laravel knows which package features to activate.

Remember to replace ExampleName and 'example-name' with your actual package and service names.

Your ExampleMap.php should look like this -

<?php

namespace ExampleName;

use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;

class ExampleName {

const YOUR_CONSTANT = 45855;
protected $class_var;

function __construct() {
$this->class_var_key = Config::get('examplemap.class_var_key') ?? env('CLASS_VAR_KEY', '');
}


public static function methodFunc1($address){
// Implementation here...
}

public static function methodFunc2($latlongstring){
// Implementation here...
}

// Rest of the methods...
}

Explanation -

  1. Constants: Use constants (e.g., YOUR_CONSTANT) for values that should not change during the package's execution.
  2. Class Variables: Class variables (e.g., $class_var_key) are used to store data within the class. Initialize them in the constructor if needed.
  3. Constructor: The constructor method is called when an instance of your class is created. You can use it for setup and configuration.
  4. Static Methods: Public static methods (e.g., methodFunc1 and methodFunc2) can be called without creating an instance of the class. Use them for various functionalities.
  5. Comments: Add comments (like the ones provided) to explain what each method does. This helps other developers understand your code.
  6. Configuration: You can fetch configuration values (like $class_var_key) from Laravel's configuration files or environment variables.

Remember to replace the placeholders (YOUR_CONSTANT, methodFunc1, methodFunc2, etc.) with meaningful names and implement the actual functionality for your package.

Your ‘config/examplename.php’ file should look like this -

<?php

return [

/*
|--------------------------------------------------------------------------
| ExampleName Configuration
|--------------------------------------------------------------------------
|
| This file contains various configuration options for the ExampleName package.
| You can modify these settings to customize the behavior of the package.
|
*/

'class_var_key' => env('EXAMPLENAME_CLASS_VAR_KEY', 'default_value'),

// Add more configuration options here as needed for your package.

];

Explanation :

  1. Customization: You can customize the behavior of the ExampleName package by modifying the values in this configuration file.
  2. class_var_key: This option is an example of how you can configure a variable within the ExampleName class. You can set its value in your application's .env file by adding EXAMPLENAME_CLASS_VAR_KEY=your_custom_value. If not set, it will default to 'default_value'.
  3. Adding More Options: If your package has additional settings or options that users can configure, you can add them to this file following the same pattern. Make sure to provide clear descriptions for each option.
  4. Keep Secrets Safe: Avoid storing sensitive information like API keys directly in this file. Instead, use Laravel’s .env file to store sensitive data and reference them in your configuration.
  5. Documentation: Don’t forget to document these configuration options in your package’s documentation, explaining what each option does and how users can use them effectively.

You are encouraged to review and modify these configuration options according to their project’s requirements to tailor the ExampleName package to their needs. ‘examplename.php’ should be placed by your configuration filename.

Second Step, set up configuration files for the package and push to the GitHub repository.

In the ‘composer.json’ file, add the version field -

"version" : "0.0.0.1"

Your composer.json should look like this -

{
"name": "your-vendor/your-package",
"description": "Description of Your Package",
"keywords": [
"your-keyword-1",
"your-keyword-2"
],
"homepage": "https://your-package-url.com",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "your@email.com"
}
],
"version": "0.0.0.1", // Replace with your actual version

"require": {
"illuminate/support": "^8.0|^9.0|^10.0",
"illuminate/config": "^8.0|^9.0|^10.0",
"ext-json": "*",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"autoload": {
"psr-4": {
"YourNamespace\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"YourNamespace\\ServiceProvider\\YourServiceProvider"
],
"aliases": {
"YourFacade": "YourNamespace\\Facade\\YourFacade"
}
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

From your master branch of the git repository release with a tag -

git tag 0.0.0.1
git push origin 0.0.0.1

Final Step, Go to create and verify your account first at https://packagist.org. Then go to https://packagist.org/packages/submit and give your GitHub repository link, press check, then submit.

If you are successful, you will see your package like me.

Hope the tutorial was helpful. Feel free to ask any questions smazoomder@gmail.com. Have a good day.

--

--

Shaon Majumder
Shaon Majumder

Written by Shaon Majumder

Software Engineer | Author | Data Scientist

No responses yet