Why integrate Composer into a WordPress plugin?
Using Composer inside a WordPress plugin brings dependency management, modern autoloading (PSR-4), reproducible installs, and the ability to consume third-party libraries reliably. It enables you to keep plugin code modular and unit-testable, avoid reinventing utilities like HTTP clients or validation, and create a predictable build process. However, integration must be done carefully to avoid conflicts with other plugins and to make packaging for distribution seamless.
High-level approaches
- Vendor the dependencies in the plugin — include the vendor directory inside the plugin and require vendor/autoload.php from your main plugin file. This is the simplest and most common approach for releasing to WordPress.org because WP.org updates do not run Composer.
- Share dependencies at the site level — use a shared Composer-managed root for the entire site and place shared libraries in a central vendor. This can reduce duplicate copies but requires site-level Composer management and careful autoloader handling.
- Package-only dev and build step — run Composer during development, then build a release artifact that bundles an optimized vendor and optionally scopes/prefixes dependencies to avoid conflicts (using tools like PHP-Scoper).
Prerequisites
- PHP compatible with your dependencies (specify in composer.json with platform or require).
- Composer installed locally or available via composer.phar. See https://getcomposer.org/.
- Basic knowledge of WordPress plugin structure and PHP namespaces.
Step-by-step tutorial
1) Create plugin skeleton and enable namespacing
Decide on a namespace for your plugin — e.g., AcmeMyPlugin. Place PSR-4 classes under src/ and the plugin bootstrap/entry file in the plugin root (e.g., acme-my-plugin.php).
2) Create composer.json
At the plugin root create composer.json. Minimal example with PSR-4 and a third-party dependency (Guzzle):
{
name: acme/acme-my-plugin,
description: Acme My Plugin for WordPress,
type: wordpress-plugin,
require: {
php: >=7.4,
guzzlehttp/guzzle: ^7.0
},
autoload: {
psr-4: {
AcmeMyPlugin: src/
}
},
config: {
optimize-autoloader: true,
sort-packages: true
}
}
3) Add PHP class files (PSR-4)
Example file: src/Bootstrap.php
4) Main plugin file — require Composer autoloader
Create the plugin header and require vendor/autoload.php. Use an existence check so activation doesnt fatal if vendor is missing during development (but include vendor for production releases).
Composer vendor directory is missing. Run composer install
.
} )
}
// Bootstrap the plugin
plugin = new AcmeMyPluginBootstrap()
plugin->run()
5) Install dependencies locally
From the plugin root, run:
composer install
For production packaging use:
composer install --no-dev --prefer-dist --optimize-autoloader
6) Use third-party libraries
Example using Guzzle inside a service:
client = new Client([
timeout => 10,
])
}
public function get(string url) {
response = this->client->get(url)
return (string) response->getBody()
}
}
7) Optimize autoloader and classmap
For releases run:
composer dump-autoload -o
This generates an optimized classmap and reduces autoload overhead.
8) Exclude dev dependencies for release
When packaging for distribution, always run composer install --no-dev. WordPress.org will not run Composer during plugin updates, so your release must include the vendor directory.
Advanced topics and best practices
Avoiding dependency conflicts
- Namespaces: Always use unique namespaces for your plugin classes.
- Avoid global class definitions: Use namespaced classes to minimize collisions.
- Prefixed/Scoped dependencies: If your plugin depends on a popular library that other plugins also include, consider using PHP-Scoper to prefix the vendor namespaces during your build step to avoid runtime conflicts.
Example: PHP-Scoper basic workflow
Add as dev dependency and run a build that prefixes vendor classes.
composer require --dev humbug/php-scoper
# Run after composer install and after files are in place
vendor/bin/php-scoper add-prefix AcmeMyPluginPrefix --output-dir build
# The build dir will contain a prefixed version of your plugin that you can zip and release.
Note: php-scoper has many configuration options. Test thoroughly since scoping can break code that relies on string-based class names or global symbols.
Conditional autoloading for performance
If parts of your plugin are only needed in admin or on specific requests, you can delay requiring heavy libraries until they are needed:
if ( is_admin() ) {
require_once __DIR__ . /vendor/autoload.php
// initialize admin-only services
}
But be careful: any class referenced before the autoloader is required will cause fatal errors. A common pattern is to do a lightweight bootstrap that registers a function to require the autoloader when needed.
Composer scripts and build tasks
Use composer scripts to automate building a packaged artifact:
{
scripts: {
build: [
composer install --no-dev --prefer-dist --optimize-autoloader,
rm -rf build/,
cp -R . build/,
vendor/bin/php-scoper add-prefix AcmeMyPluginPrefix --output-dir build_scoped
]
}
}
WordPress-specific Composer packages
- composer/installers — helps with installing WordPress packages via Composer when building larger projects.
- wpackagist.org — access WordPress plugins/themes as Composer packages for site-level management (not typically for plugin internal use).
Packaging for WordPress.org
- Run composer install --no-dev --prefer-dist --optimize-autoloader.
- Ensure vendor/ is included in your plugin ZIP that you upload to the WordPress.org plugin repository.
- Do not rely on Composer being available on the target server bundle everything needed.
- Document plugin dependencies and recommended PHP version in the plugin header or README.
Common pitfalls and troubleshooting
- Missing vendor/autoload.php: If you forget to run composer install, the plugin will fail. Add an informative admin notice during development to catch this early.
- Class not found: Verify PSR-4 mapping, namespace declarations, and file paths. Run composer dump-autoload -o and check the generated autoload_classmap.php.
- Memory limits during composer install: Increase PHP memory_limit for Composer when installing on constrained environments (e.g., php -d memory_limit=-1 composer.phar install).
- Dependency collisions: Two plugins shipping different versions of the same library can cause fatal errors. Use prefixed builds, unique namespaces, or encourage site-wide package management.
- Plugin update process: If you rely on Composer during deployment (not recommended for WordPress.org), ensure the update process runs Composer to fetch dependencies.
Example plugin file tree
acme-my-plugin/ |
- acme-my-plugin.php (main plugin file)
- composer.json
- composer.lock
- vendor/ (bundled on release)
- src/
- Bootstrap.php
- HttpService.php
- ... other classes ...
- readme.txt or README.md
|
Security considerations
- Only require trusted Composer packages from reputable sources (Packagist). Review dependencies code where feasible.
- Keep third-party libraries updated. If deploying to many sites, plan update strategy and semantic versioning constraints.
- Avoid executing upstream Composer scripts during production installs prefer a local build process that bundles finalized code.
Summary checklist before release
- composer.json and composer.lock present in development
- Run composer install --no-dev --optimize-autoloader and verify vendor is included
- Test plugin on clean WordPress install to detect dependency conflicts
- Consider scoping with php-scoper if library collisions are likely
- Document supported PHP versions and any server requirements
Quick reference commands
- Install deps:
composer install
- Prod install:
composer install --no-dev --prefer-dist --optimize-autoloader
- Optimize autoloader:
composer dump-autoload -o
- Add dependency:
composer require vendor/package
- Install php-scoper (dev):
composer require --dev humbug/php-scoper
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂
|
¡Si te ha servido el artículo ayúdame compartiendolo en algún sitio! Pero si no te ha sido útil o tienes dudas déjame un comentario! 🙂
Related