# New Tutorial: Using PHP Composer in the WordPress Ecosystem

This post was originally published in blog.php-prefixer.com (opens new window).

When building a WordPress website, developers like you build a plethora of themes and plugins. In doing so, you frequently copy-paste the same code over and over again. It could be just to create your versions of available libraries. Every time you manually duplicate code – you increase the development time and reduce the time you spent developing the business layer of your plugin.

That’s where you need a dependency management tool like Composer.

As pip is for Python and Bundler is for Ruby, Composer is for PHP – it’s a dependency manager!

Though Composer allows declaring, installing, and managing dependencies, its use in WordPress is limited. The development of plugins powered by Composer can result in conflicts between any installed libraries and pose issues when dealing with multiple plugins using the same dependency.

For instance, a site uses two plugins:

  • Plugin “Menu” uses the library "MyDates" in version 1.40
  • Plugin “Restaurant Menu” the library "MyDates" in version 2.0

Both plugins use the same dependency. The same namespace is defined in both versions but has entirely different functionalities.

In practice, you get PHP errors such as:

Fatal error: Cannot redeclare GuzzleHttp\uri_template() (previously declared in ...
...
Fatal error: Cannot declare interface Psr\Cache\CacheItemPoolInterface, because the name is already in use in ...
...

To solve these conflicts and ensure a seamless coding experience, we’ve developed a service called PHP-Prefixer.

From solving conflicts to prefixing Composer-installed libraries – it saves you from the nightmare of copy-pasting and testing the library source code with different namespaces.

PHP-Prefixer is cloud-based and accessible from anywhere. It lets you install any library while eliminating the complex configuration of hardware or software. Just declare what to prefix in the composer.json schema, and the PHP-Prefixer does it for you.

Cool, isn’t it?

These are the steps to produce a Hello Prefixed World plugin for WordPress using the PHP-Prefixer cloud. To follow this guide, we recommend first learning the basics of writing a WordPress plugin: Plugin Handbook (opens new window).

# What is a Prefixed Plugin?

In the WordPress world, plugins are packages of code that extend the core functionality of WordPress. WordPress plugins are made up of PHP code and can include other assets such as images, CSS, and JavaScript.

By making your plugin, you are extending WordPress. You are building additional functionality on top of what WordPress already offers. For example, you could write a plugin that displays links to your site's ten most recent posts.

To include Composer dependencies and avoid naming conflicts with other plugins, PHP-Prefixer provides the service that prefixes all PHP files with the custom prefix for the project.

In this guide, we will create the Hello Prefixed World Plugin, based on the Hello Dolly (opens new window) plugin. Hello Dolly, one of the first plugins is only 82 lines long. Hello Dolly shows lyrics from the famous song (opens new window) in the WordPress admin. Some CSS is used in the PHP file to control how the lyrics are styled.

To integrate a Composer dependency, we install the Laravel illuminate/support (opens new window) library, and we use it to show a formatted date before the Hello Dolly lyrics.

Essentially, this is how the modified Hello Dolly plugin looks after the prefixing process with the new PPP namespace:

function hello_dolly() {
	require_once __DIR__.'/vendor/autoload.php';

	$chosen = hello_prefixed_world_get_lyric();
	$lang   = '';
	if ( 'en_' !== substr( get_user_locale(), 0, 3 ) ) {
		$lang = ' lang="en"';
	}

    // The Carbon library reference is prefixed with PPP
	$now = \PPP\Carbon\Carbon::now();
	$formattedDate = $now->toDateTimeString();

	printf(
		'<p id="dolly"><span class="screen-reader-text">%s </span><span dir="ltr"%s>%s // %s</span></p>',
		__( 'Quote from Hello Dolly song, by Jerry Herman:', 'hello-dolly' ),
		$lang,
		$formattedDate,
		$chosen
	);
}

# Plugin Basics

# Getting Started

In this guide, we follow WordPress's Plugin Handbook / Plugin Basics (opens new window) steps to create the plugin structure.

# The Composer Schema of the WordPress Plugin

In this version of the Hello Dolly plugin, the plugin shows the date and hour before the lyrics. To do this, the plugin calls on the Carbon (opens new window) library, a dependency of the Laravel illuminate/support (opens new window) library.

The plugin has the following composer.json to declare the project, the illuminate/support library, and the prefixer service configuration:

{
    "name": "php-prefixer/hello-prefixed-world-for-wp",
    "description": "Hello Prefixed World plugin for WordPress. A plugin to showcase the PHP-Prefixer service. Install any library freely. PHP-Prefixer will manage your namespaces.",
    "require": {
        "illuminate/support": "^8.10"
    },
    "extra": {
        "php-prefixer": {
            "project-name": "Hello Prefixed World for WordPress",
            "namespaces-prefix":: "PPP",
            "global-scope-prefix": "PPP_",

            "exclude-paths": [
                "bin/",
                "doctrine/inflector/docs",
                "voku/portable-ascii/build"
            ]
        }
    }
}

In particular, this schema has the attribute excludePaths to help package the plugin for distribution. The excludePaths remove folders that should not be included in the target prefixed plugin (commands, unit tests, library documentation, etc.).

The prefixed project ZIP file will be installed on WordPress once it is processed and available for download.

# Create the PHP-Prefixer Project for the WordPress Plugin

We support several ways of uploading and downloading the project's source code. Follow the steps below to set up a simple downloadable project that doesn’t require the integration of version control and source code management.​

In this brief tutorial, we upload and download the plugin ZIPs manually.

# Creating Projects

  1. In the Dashboard, click "Projects."
  2. Click on "Create Project."
  3. Type in a name for your project.
  4. Select "Source Code Integration" from the dropdown menu.
  5. For this first guide, select the "File" integration to upload and download the project files manually.
  6. Click "Create Project."

Create Project

# Create the Build to Prefix the WordPress Plugin

PHP-Prefixer works based on Composer (opens new window) as the dependency management tool. It allows you to declare the libraries your project depends on and manages (install/update) them for you. For more information about Composer, please visit https://getcomposer.org/ (opens new window).

# Prefixing the "Hello Prefixed World Plugin" Project

In this guide, we will prefix the project "Hello Prefixed World Plugin." We prefix it with our prefix PPP.

# The "Hello Prefixed World Plugin" Project

The modified version of the Hello Dolly plugin shows a formatted date before the lyrics hello.php:

<?php

/**
 * @package Hello_Prefixed_World_For_Wp
 * @version 1.0.0
 */

....

// This just echoes the chosen line, we'll position it later.
function hello_dolly() {
	require_once __DIR__.'/vendor/autoload.php';

	$chosen = hello_prefixed_world_get_lyric();
	$lang   = '';
	if ( 'en_' !== substr( get_user_locale(), 0, 3 ) ) {
		$lang = ' lang="en"';
	}

    // The modified version of the Hello Dolly plugin shows a formatted date before the lyrics
	$now = \Carbon\Carbon::now();
	$formattedDate = $now->toDateTimeString();

	printf(
		'<p id="dolly"><span class="screen-reader-text">%s </span><span dir="ltr"%s>%s // %s</span></p>',
		__( 'Quote from Hello Dolly song, by Jerry Herman:', 'hello-dolly' ),
		$lang,
		$formattedDate,
		$chosen
	);
}

...

add_action( 'admin_head', 'dolly_css' );

# The PHP-Prefixer Configuration

In the composer.json schema, in the extra configuration, the PHP-Prefixer configuration is as follows:

    "extra": {
        "php-prefixer": {
            "project-name": "Hello Prefixed World for WordPress",
            "namespaces-prefix":: "PPP",
            "global-scope-prefix": "PPP_",

            "exclude-paths": [
                "bin/",
                "doctrine/inflector/docs",
                "voku/portable-ascii/build"
            ]
        }
    }

The configuration declares the project name and the prefix PPP used for namespaces and global objects (functions, etc.).

In particular, this schema has the attribute excludePaths to help package the plugin for distribution. The excludePaths remove folders that should not be included in the target prefixed plugin (commands, unit tests, library documentation, etc.).

# Verify Composer.json

To review the project:

  1. Download the source code from the repository.
  2. Go to the project directory.
  3. Run the following command to verify the schema and lock the project's dependencies that would be distributed:
~$ composer update --no-dev
...
Writing lock file
Generating autoload files
...

Then, compress the files in a new ZIP file or download the project's ZIP file from GitHub.

# Create a Build

You can now set up a new build and prefix the project in the "Dashboard/ Projects" area. Follow the steps below to set up a build and prefix the project ZIP file.

  1. Go to "Project."
  2. Click on "Create Build."
  3. Select the file to be prefixed.
  4. Upload and create the Build; click on "Create Build."

Create a Build

# Validating the Build

Once the Build is created, the PHP-Prefixer service automatically validates the project and continues with the next steps.

Validating the Build

# Prefixing the Build

Once the Build is validated, the PHP-Prefixer service prefixes the files and notifies when processing is completed.

Prefixing the Build

# Download and Review the Prefixed Codebase

When the build is ready, check the processing results in the Build Details:

Build Details

Download the Output File and uncompress it to review the final results.

# Testing the Prefixed WordPress Plugin

Install it on your WordPress site to confirm it works in the same way as the original plugin.

Testing the prefixed WordPress plugin

In the next chapter, we will point out the main results of processing.

# Review the Prefixed PHP Project

The results of the prefixing process of the project can be found in this repository: https://github.com/PHP-Prefixer/hello-wp-world_prefixed (opens new window)

# The New Composer.json of the Prefixed WordPress Plugin

The PHP-Prefixed process has produced a new composer.json schema and contains the original files, and files that are re-organized according to the applied PPP prefix.

{
    "name": "php-prefixer/hello-prefixed-world-for-wp",
    "description": "Hello Prefixed World plugin for WordPress. A plugin to showcase the PHP-Prefixer service. Install any library freely. PHP-Prefixer will manage your namespaces.",
    "autoload": {
        "classmap": [
            "vendor_prefixed/nesbot/carbon/src/Carbon/Carbon.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonConverterInterface.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonImmutable.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonInterface.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonInterval.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonPeriod.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/CarbonTimeZone.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/Cli/Invoker.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/Doctrine/CarbonDoctrineType.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php",
            "vendor_prefixed/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php",
...
            "vendor_prefixed/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php",
            "vendor_prefixed/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php",
            "vendor_prefixed/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php",
            "vendor_prefixed/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php",
            "vendor_prefixed/doctrine/inflector/lib/Doctrine/Inflector/Language.php",
...
            "vendor_prefixed/illuminate/contracts/Auth/Access/Gate.php",
            "vendor_prefixed/illuminate/contracts/Auth/Authenticatable.php",
            "vendor_prefixed/illuminate/contracts/Auth/CanResetPassword.php",
            "vendor_prefixed/illuminate/contracts/Auth/Factory.php",
            "vendor_prefixed/illuminate/contracts/Auth/Guard.php",
...
            "vendor_prefixed/psr/container/src/ContainerExceptionInterface.php",
            "vendor_prefixed/psr/container/src/ContainerInterface.php",
            "vendor_prefixed/psr/container/src/NotFoundExceptionInterface.php",
            "vendor_prefixed/psr/simple-cache/src/CacheException.php",
            "vendor_prefixed/psr/simple-cache/src/CacheInterface.php",
            "vendor_prefixed/psr/simple-cache/src/InvalidArgumentException.php",
            "vendor_prefixed/symfony/polyfill-php80/Resources/stubs/Stringable.php",
            "vendor_prefixed/symfony/translation/Catalogue/AbstractOperation.php",
            "vendor_prefixed/symfony/translation/Catalogue/MergeOperation.php",
            "vendor_prefixed/symfony/translation/Catalogue/OperationInterface.php",
            "vendor_prefixed/symfony/translation/Catalogue/TargetOperation.php",
            "vendor_prefixed/symfony/translation/Command/XliffLintCommand.php",
...
            "vendor_prefixed/symfony/polyfill-php80/Php80.php",
            "vendor_prefixed/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php",
            "vendor_prefixed/symfony/polyfill-php80/Resources/stubs/ValueError.php",
            "vendor_prefixed/voku/portable-ascii/src/voku/helper/ASCII.php"
        ],
        "files": [
            "vendor_prefixed/symfony/polyfill-mbstring/bootstrap.php",
            "vendor_prefixed/symfony/polyfill-php80/bootstrap.php",
            "vendor_prefixed/illuminate/collections/helpers.php",
            "vendor_prefixed/illuminate/support/helpers.php"
        ]
    }
}

The files of the dependencies are stored in the vendor_prefixed folder. composer.json declares them in the classmap and files attributes to generate the autoloader.

No conflicts will be found if other plugins were installed using the Carbon, Doctrine, Illuminate or Symfony namespaces.

# The Prefixed Plugin

The project file hello.php has been processed and the prefix PPP added to the Carbon dependency call:

// The modified version of the Hello Dolly plugin shows a formatted date before the lyrics
$now = \PPP\Carbon\Carbon::now();
$formattedDate = $now->toDateTimeString();

# The Prefixed Dependencies

The original libraries installed in the vendor folder have been prefixed and updated. These are a few examples:

# The Prefixed Doctrine Inflector

<?php /* This file has been prefixed by <PHP-Prefixer> for "Hello Prefixed World for WordPress" */

declare(strict_types=1);

namespace PPP\Doctrine\Inflector;

use RuntimeException;
use function chr;
use function function_exists;
use function lcfirst;
use function mb_strtolower;
use function ord;
use function preg_match;
use function preg_replace;
use function sprintf;
use function str_replace;
use function strlen;
use function strtolower;
use function strtr;
use function trim;
use function ucwords;

class Inflector
{

# The Prefixed Laravel Support Helpers

/* This file has been prefixed by <PHP-Prefixer> for "Hello Prefixed World for WordPress" */

use PPP\Illuminate\Contracts\Support\DeferringDisplayableValue;
use PPP\Illuminate\Contracts\Support\Htmlable;
use PPP\Illuminate\Support\Arr;
use PPP\Illuminate\Support\Env;
use PPP\Illuminate\Support\HigherOrderTapProxy;
use PPP\Illuminate\Support\Optional;

if (! function_exists('PPP_append_config')) {
    /**
     * Assign high numeric IDs to a config item to force appending.
     *
     * @param  array  $array
     * @return array
     */
    function PPP_append_config(array $array)
    {
        $start = 9999;

        foreach ($array as $key => $value) {
            if (is_numeric($key)) {
                $start++;

                $array[$start] = Arr::pull($array, $key);
            }
        }

        return $array;
    }
}
...
if (! function_exists('PPP_env')) {
    /**
     * Gets the value of an environment variable.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    function PPP_env($key, $default = null)
    {
        return Env::get($key, $default);
    }
}
...

# The Prefixed Carbon Library

<?php
/* This file has been prefixed by <PHP-Prefixer> for "Hello Prefixed World for WordPress" */

/**
 * This file is part of the Carbon package.
 *
...
 */
namespace PPP\Carbon;

use PPP\Carbon\Traits\Date;
use DateTime;
use DateTimeInterface;
use DateTimeZone;

/**
 * A simple API extension for DateTime.
 *
...
 */
class Carbon extends DateTime implements CarbonInterface
{
    use Date;

    /**
     * Returns true if the current class/instance is mutable.
     *
     * @return bool
     */
    public static function isMutable()
    {
        return true;
    }
}

# The Polyfills

The polyfill libraries are excluded from processing by the prefixer.

// vendor_prefixed/symfony/polyfill-php80/Resources/stubs/Stringable.php
interface Stringable
{
    /**
     * @return string
     */
    public function __toString();
}

# Conclusion

In this guide, we presented a simple WordPress plugin Hello Prefixed World plugin for WordPress.

The plugin is based on the Hello Dolly (opens new window) plugin. To integrate a Composer dependency, we install the Laravel illuminate/support (opens new window) library and use it to show a formatted date before the Hello Dolly lyrics.

In the guide, we introduced the original project, the prefixed project and the different prefixing cases. These have been successfully processed to produce a complete functional plugin, ready to be distributed and installed.

The source code of the project can be found in this repository: https://github.com/PHP-Prefixer/hello-wp-world (opens new window)

The results of the prefixing process can be found in this repository: https://github.com/PHP-Prefixer/hello-wp-world_prefixed (opens new window)