Skip to content

Commit fc1a475

Browse files
committed
Add .gitignore, CHANGELOG, and PHPCS configuration; update composer.json and plugin headers
1 parent 7066abb commit fc1a475

8 files changed

Lines changed: 222 additions & 75 deletions

File tree

.github/workflows/phpcs.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: PHPCS
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- main
8+
- trunk
9+
pull_request:
10+
11+
jobs:
12+
phpcs:
13+
name: WordPress Coding Standards
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup PHP
20+
uses: shivammathur/setup-php@v2
21+
with:
22+
php-version: '8.2'
23+
tools: composer
24+
25+
- name: Install Composer dependencies
26+
run: composer install --no-interaction --no-progress
27+
28+
- name: Run PHPCS
29+
run: vendor/bin/phpcs

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor/
2+
composer.lock

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented in this file. Releases correspond to [GitHub tags](https://github.com/BeAPI/wp-mu-loader/tags).
4+
5+
The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6+
7+
## [Unreleased]
8+
9+
## [1.0.4] - 2026-04-07
10+
11+
### Changed
12+
13+
- Update plugin header with BeAPI metadata ([#4](https://github.com/BeAPI/wp-mu-loader/pull/4)).
14+
- Add `CHANGELOG.md` (release history aligned with [GitHub tags](https://github.com/BeAPI/wp-mu-loader/tags)) and link it from the README.
15+
16+
## [1.0.3] - 2016-08-26
17+
18+
### Fixed
19+
20+
- Do not run the loader while WordPress is installing or upgrading (`WP_INSTALLING`) ([#3](https://github.com/BeAPI/wp-mu-loader/pull/3)).
21+
- Fix cache flushing for network admins on multisite.
22+
- Remove case-insensitive flag from plugin discovery for more predictable matching.
23+
24+
### Changed
25+
26+
- Do not wait for MU plugins to be loaded before requiring the bootstrap file for subdirectory plugins.
27+
- Author and package metadata updates.
28+
29+
### Other
30+
31+
- Extra loader improvements from community contributions.
32+
33+
## [1.0.2] - 2015-01-21
34+
35+
### Fixed
36+
37+
- Avoid error messages during installation when database tables are not yet available ([e1fcd9b](https://github.com/BeAPI/wp-mu-loader/commit/e1fcd9b); originally merged from rexblack on the upstream project).
38+
39+
## [1.0.1] - 2014-12-10
40+
41+
### Changed
42+
43+
- README expanded with more installation and usage information.
44+
45+
## [1.0] - 2014-02-06
46+
47+
### Added
48+
49+
- Initial release: load WordPress MU plugins that live in subdirectories of `wp-content/mu-plugins/`.
50+
51+
[Unreleased]: https://github.com/BeAPI/wp-mu-loader/compare/v1.0.4...HEAD
52+
[1.0.4]: https://github.com/BeAPI/wp-mu-loader/compare/v1.0.3...v1.0.4
53+
[1.0.3]: https://github.com/BeAPI/wp-mu-loader/compare/v1.0.2...v1.0.3
54+
[1.0.2]: https://github.com/BeAPI/wp-mu-loader/compare/v1.0.1...v1.0.2
55+
[1.0.1]: https://github.com/BeAPI/wp-mu-loader/compare/v1.0...v1.0.1
56+
[1.0]: https://github.com/BeAPI/wp-mu-loader/releases/tag/v1.0

README.md

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,77 @@
1-
# Wordpress Must-Use plugins loader
1+
# MU Plugins Subdirectory Loader
22

3-
Builds a list of all plugins in the `wp-content/mu-plugins` folder and include them.
3+
WordPress only executes **single PHP files** placed directly in `wp-content/mu-plugins/`. It does **not** load plugins that live in subfolders, unlike normal plugins in `wp-content/plugins/`.
44

5-
Uses the internal Wordpress function `get_plugins` for better compatibility.
6-
In theory, any plugin could be included this way.
5+
This small must-use (MU) package bridges that gap: it discovers MU plugins that live in **subdirectories**, loads their main bootstrap file, and lists them neatly under the loader entry in **Plugins → Must-Use** in the admin.
76

8-
Sadly, `get_mu_plugins` does not have any hooks.
7+
## What it does
98

10-
* Will clear cache when visiting the plugin page in /wp-admin/.
11-
* Will also clear cache if a previously detected mu-plugin was deleted.
9+
- Scans `mu-plugins` with WordPress’s own [`get_plugins()`](https://developer.wordpress.org/reference/functions/get_plugins/) so discovery stays aligned with core behaviour.
10+
- Skips loose PHP files at the root of `mu-plugins` and skips the `mu-loader` folder itself, then `require_once`s each detected plugin’s bootstrap file.
11+
- During installation (`WP_INSTALLING`), it does nothing so core install/upgrade is not disturbed.
12+
- In the admin, each loaded subdirectory plugin appears as an indented row under the main MU loader line for easier reading.
1213

13-
[Original idea](https://gist.github.com/lavoiesl/6302907)
14+
## Credits
1415

15-
[Blog post](http://blog.lavoie.sl/2013/08/wordpress-mu-plugins-subdirectory-loader.html)
16+
The project was originally created and published as **`wemakecustom/wp-mu-loader`** by **[WeMakeCustom](https://github.com/wemakecustom)**. BeAPI now maintains this fork as **`beapi/wp-mu-loader`**. Thanks to WeMakeCustom for the original work.
17+
18+
Further background:
19+
20+
- [Original idea (gist)](https://gist.github.com/lavoiesl/6302907) — Sébastien Lavoie
21+
- [Blog post explaining MU subdirectories](http://blog.lavoie.sl/2013/08/wordpress-mu-plugins-subdirectory-loader.html)
22+
23+
WordPress does not expose hooks on `get_mu_plugins()`, which is why a dedicated loader like this one is still useful.
1624

1725
## Installation
1826

1927
### Composer
2028

21-
Add this to your `composer.json`:
29+
Require the package (maintained under the BeAPI vendor name):
30+
2231
```json
2332
{
2433
"require": {
25-
"wemakecustom/wp-mu-loader": "*"
34+
"beapi/wp-mu-loader": "^1.0"
2635
}
2736
}
2837
```
2938

30-
### Manual
39+
Then run `composer install` so the package is installed under `mu-plugins/mu-loader/` (see `composer.json` `extra.installer-name` in this repo).
3140

32-
Extract/clone this plugin in `wp-content/mu-plugins/mu-loader/`
41+
### Manual
3342

34-
### IMPORTANT
43+
1. Clone or extract this repository into `wp-content/mu-plugins/mu-loader/`.
44+
2. **Copy or symlink** `mu-require.php` into `wp-content/mu-plugins/` so WordPress loads the loader.
45+
Only `mu-require.php` must sit at the root of `mu-plugins`; the rest stays in the `mu-loader` folder.
3546

36-
**Copy or symlink `mu-require.php` into `wp-content/mu-plugins/`**
47+
If `mu-loader/mu-loader.php` is missing (e.g. incomplete install), an admin notice suggests running `composer install`.
3748

38-
## Usage
49+
## Usage for your own MU plugin
3950

40-
Create a plugin with this in your `composer.json`:
51+
If you ship a must-use plugin that should install into a folder under `mu-plugins`, use Composer’s `wordpress-muplugin` type and optionally set `extra.installer-name`:
4152

4253
```json
4354
{
4455
"name": "my-vendor/my-plugin",
4556
"type": "wordpress-muplugin",
46-
"keywords": ["wordpress","plugins"],
47-
"license": "GPL-2.0",
57+
"keywords": ["wordpress", "plugins"],
58+
"license": "GPL-2.0-or-later",
4859
"require": {
49-
"composer/installers": "~1.0"
60+
"composer/installers": "^1.0 || ^2.0"
5061
},
51-
5262
"extra": {
5363
"installer-name": "my-plugin"
5464
}
5565
}
5666
```
5767

58-
The `extra.installer-name` is optional, it is to give a custom folder name in case your plugin is actually declared as `my-vendor/wp-mu-my-plugin` like this one.
68+
`extra.installer-name` is optional; use it when you want the directory name under `mu-plugins` to differ from the Composer package name (this repo uses `mu-loader` for `beapi/wp-mu-loader`).
5969

60-
`keywords` and `license` are also optional but strongly suggested.
70+
`keywords` and `license` are optional but recommended for published packages.
6171

62-
## Extra notes
72+
## Custom install paths
6373

64-
If like me, your wordpress installation is not at the root of your project, you may need to change the install path:
74+
If WordPress is not at the project root, point `composer/installers` to your real `wp-content` paths, for example:
6575

6676
```json
6777
{
@@ -74,3 +84,13 @@ If like me, your wordpress installation is not at the root of your project, you
7484
}
7585
}
7686
```
87+
88+
Adjust the `htdocs/...` segments to match your layout.
89+
90+
## Changelog
91+
92+
Release notes are in [CHANGELOG.md](CHANGELOG.md). Tagged versions are listed on [GitHub](https://github.com/BeAPI/wp-mu-loader/tags).
93+
94+
## License
95+
96+
GPL-2.0-or-later (see `composer.json` and plugin header in `mu-require.php`).

composer.json

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
{
22
"name": "beapi/wp-mu-loader",
3-
"type": "wordpress-muplugin",
43
"description": "Enables the loading of Wordpress plugins sitting in mu-plugins (as folders)",
5-
"keywords": ["wordpress","plugins"],
6-
"license": "GPL-2.0",
4+
"license": "GPL-2.0-or-later",
5+
"type": "wordpress-muplugin",
6+
"version": "1.0.4",
7+
"keywords": [
8+
"wordpress",
9+
"plugins"
10+
],
711
"authors": [
8-
{
9-
"name": "WeMakeCustom",
10-
"email": "info@wemakecustom.com",
11-
"homepage": "http://www.wemakecustom.com"
12-
},
1312
{
1413
"name": "BeAPI",
1514
"email": "humans@beapi.fr",
1615
"homepage": "http://www.beapi.fr"
1716
}
1817
],
1918
"require": {
20-
"composer/installers": "~1.0"
19+
"composer/installers": "^1.0 || ^2.0"
2120
},
22-
"suggest" : {
23-
"wemakecustom/wp-mu-composer": "Composer includer"
21+
"require-dev": {
22+
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
23+
"squizlabs/php_codesniffer": "^3.13",
24+
"wp-coding-standards/wpcs": "^3.3"
25+
},
26+
"config": {
27+
"allow-plugins": {
28+
"composer/installers": true,
29+
"dealerdirect/phpcodesniffer-composer-installer": true
30+
}
2431
},
2532
"extra": {
2633
"installer-name": "mu-loader"
34+
},
35+
"scripts": {
36+
"cbf": "phpcbf",
37+
"cs": "phpcs"
2738
}
2839
}

mu-loader.php

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
<?php
2+
/**
3+
* Loads must-use plugins that live in subdirectories of mu-plugins.
4+
*
5+
* @package BeAPI\WP_Mu_Loader
6+
*/
27

38
/**
49
* Get mu plugins directories plugins
@@ -7,19 +12,19 @@
712
*/
813
function mu_loader_plugins_files() {
914
if ( defined( 'WP_INSTALLING' ) && true === WP_INSTALLING ) {
10-
// Do nothing during installation
11-
return array();
15+
// Do nothing during installation.
16+
return array();
1217
}
13-
18+
1419
if ( ! function_exists( 'get_plugins' ) ) {
15-
// get_plugins is not included by default
20+
// get_plugins is not included by default.
1621
require ABSPATH . 'wp-admin/includes/plugin.php';
1722
}
1823

1924
$plugins = array();
20-
foreach ( get_plugins( '/../mu-plugins' ) as $plugin_file => $data ) {
21-
if ( dirname( $plugin_file ) != '.' && dirname( $plugin_file ) != 'mu-loader' ) {
22-
// skip files directly at root
25+
foreach ( get_plugins( '/../mu-plugins', false, false ) as $plugin_file => $data ) {
26+
if ( dirname( $plugin_file ) !== '.' && dirname( $plugin_file ) !== 'mu-loader' ) {
27+
// Skip files directly at root.
2328
$plugins[] = $plugin_file;
2429
}
2530
}
@@ -37,20 +42,24 @@ function mu_loader_plugins_files() {
3742
/**
3843
* Add rows for each subplugin under this plugin when listing mu-plugins in wp-admin
3944
*/
40-
add_action( 'admin_init', function () {
45+
add_action(
46+
'admin_init',
47+
function () {
48+
add_action(
49+
'after_plugin_row_mu-require.php',
50+
function () {
51+
$table = new WP_Plugins_List_Table();
4152

42-
add_action( 'after_plugin_row_mu-require.php', function () {
43-
$table = new WP_Plugins_List_Table;
53+
foreach ( mu_loader_plugins_files() as $plugin_file ) {
54+
$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . '/' . $plugin_file, false );
55+
if ( empty( $plugin_data['Name'] ) ) {
56+
$plugin_data['Name'] = $plugin_file;
57+
}
58+
$plugin_data['Name'] = '&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;' . $plugin_data['Name'];
4459

45-
foreach ( mu_loader_plugins_files() as $plugin_file ) {
46-
$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . '/' . $plugin_file, false );
47-
48-
if ( empty( $plugin_data['Name'] ) ) {
49-
$plugin_data['Name'] = $plugin_file;
60+
$table->single_row( array( $plugin_file, $plugin_data ) );
61+
}
5062
}
51-
$plugin_data['Name'] = "&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;" . $plugin_data['Name'];
52-
53-
$table->single_row( array( $plugin_file, $plugin_data ) );
54-
}
55-
} );
56-
} );
63+
);
64+
}
65+
);

mu-require.php

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
<?php
22
/**
3-
* Plugin Name: MU plugins subdirectory loader
4-
* Plugin URI: https://github.com/BeAPI/wp-mu-loader/
5-
* Description: Enables the loading of plugins sitting in mu-plugins (as folders)
6-
* Version: 1.0
7-
* Author: BeAPI, WeMakeCustom
8-
* Author URI: http://beapi.fr/
3+
* Plugin Name: MU Plugins Subdirectory Loader
4+
* Plugin URI: https://github.com/BeAPI/wp-mu-loader/
5+
* Description: Loads must-use plugins from subdirectories under mu-plugins, not only single PHP files at the root. Uses the core get_plugins() API for discovery.
6+
* Version: 1.0.4
7+
* Requires at least: 6.0
8+
* Requires PHP: 7.4
9+
* Author: BeAPI
10+
* Previous Author: WeMakeCustom (https://github.com/wemakecustom/wp-mu-loader)
11+
* Author URI: https://beapi.fr/
12+
* License: GPL-2.0-or-later
13+
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
914
*
10-
* Will clear cache when visiting the plugin page in /wp-admin/.
11-
* Will also clear cache if a previously detected mu-plugin was deleted.
15+
* @package BeAPI\WP_Mu_Loader
1216
*/
1317

1418
$wp_mu_loader = WPMU_PLUGIN_DIR . '/mu-loader/mu-loader.php';
1519

16-
if (is_file($wp_mu_loader)) {
17-
require_once $wp_mu_loader;
20+
if ( is_file( $wp_mu_loader ) ) {
21+
require_once $wp_mu_loader;
1822
} else {
19-
add_action('admin_notices', function() use ($wp_mu_loader) {
20-
?>
21-
<div class="error">
22-
<p><code><?php echo substr($wp_mu_loader, strlen(ABSPATH)) ?></code> is missing. Consider running <code>composer install</code>.</p>
23-
</div>
24-
<?php
25-
});
23+
add_action(
24+
'admin_notices',
25+
function () use ( $wp_mu_loader ) {
26+
echo '<div class="error"><p><code>' . esc_html( substr( $wp_mu_loader, strlen( ABSPATH ) ) ) . '</code> is missing. Consider running <code>composer install</code>.</p></div>';
27+
}
28+
);
2629
}
2730

28-
unset($wp_mu_loader);
31+
unset( $wp_mu_loader );

0 commit comments

Comments
 (0)