WordPress plugins kunnen worden uitgebreid met extra functionaliteit, zoals populaire plugins als WooCommerce en Gravity Forms laten zien. In het artikel “Een WordPress plugin maken om extensies te ondersteunen” leren we dat er twee manieren zijn om een WordPress plugin uitbreidbaar te maken:
- Door hooks (acties en filters) in te stellen waarmee uitbreidingsplugins hun eigen functionaliteit kunnen injecteren
- Door PHP klassen aan te bieden die uitbreidingsplugins kunnen erven (inheriten)
De eerste methode is meer gebaseerd op documentatie, waarin de beschikbare hooks en hun gebruik gedetailleerd worden beschreven. De tweede methode biedt daarentegen kant-en-klare code voor extensies, waardoor er minder uitgebreide documentatie nodig is. Dit is voordelig omdat het maken van documentatie naast code het beheer en de uitgave van de plugin kan bemoeilijken.
Het direct aanbieden van PHP klassen vervangt documentatie effectief door code. In plaats van te leren hoe een functie geïmplementeerd moet worden, levert de plugin de benodigde PHP code, wat de taak voor externe ontwikkelaars vereenvoudigt.
Laten we een aantal technieken verkennen om dit te bereiken, met als uiteindelijke doel het bevorderen van een ecosysteem van integraties rondom onze WordPress plugin.
PHP basisklassen definiëren in de WordPress plugin
De WordPress plugin zal PHP klassen bevatten die bedoeld zijn voor gebruik door uitbreidingsplugins. Deze PHP klassen worden wellicht niet gebruikt door de hoofdplugin zelf, maar worden specifiek geleverd voor gebruik door anderen.
Laten we eens kijken hoe dit is geïmplementeerd in de open-source Gato GraphQL plugin.
De klasse AbstractPlugin:
AbstractPlugin
staat voor een plugin, zowel voor de hoofd-Gato GraphQL plugin als voor zijn uitbreidingen:
abstract class AbstractPlugin implements PluginInterface
{
protected string $pluginBaseName;
protected string $pluginSlug;
protected string $pluginName;
public function __construct(
protected string $pluginFile,
protected string $pluginVersion,
?string $pluginName,
) {
$this->pluginBaseName = plugin_basename($pluginFile);
$this->pluginSlug = dirname($this->pluginBaseName);
$this->pluginName = $pluginName ?? $this->pluginBaseName;
}
public function getPluginName(): string
{
return $this->pluginName;
}
public function getPluginBaseName(): string
{
return $this->pluginBaseName;
}
public function getPluginSlug(): string
{
return $this->pluginSlug;
}
public function getPluginFile(): string
{
return $this->pluginFile;
}
public function getPluginVersion(): string
{
return $this->pluginVersion;
}
public function getPluginDir(): string
{
return dirname($this->pluginFile);
}
public function getPluginURL(): string
{
return plugin_dir_url($this->pluginFile);
}
// ...
}
AbstractMainPlugin klasse:
AbstractMainPlugin
breidt AbstractPlugin
uit om de hoofdplugin te representeren:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
public function __construct(
string $pluginFile,
string $pluginVersion,
?string $pluginName,
protected MainPluginInitializationConfigurationInterface $pluginInitializationConfiguration,
) {
parent::__construct(
$pluginFile,
$pluginVersion,
$pluginName,
);
}
// ...
}
AbstractExtension klasse:
Evenzo, AbstractExtension
AbstractPlugin
om een uitbreidingsplugin weer te geven:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
public function __construct(
string $pluginFile,
string $pluginVersion,
?string $pluginName,
protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration,
) {
parent::__construct(
$pluginFile,
$pluginVersion,
$pluginName,
);
}
// ...
}
Merk op dat AbstractExtension
is opgenomen in de hoofdplugin en functionaliteit biedt om een extensie te registreren en te initialiseren. Het wordt echter alleen gebruikt door extensies, niet door de hoofdplugin zelf.
De klasse AbstractPlugin
bevat gedeelde initialisatiecode die op verschillende momenten wordt gecalld. Deze methoden zijn gedefinieerd op het niveau van de ancestor, maar worden gecalld door de inheriting klassen volgens hun levenscycli.
De hoofdplugin en de uitbreidingen worden geïnitialiseerd door de methode setup
uit te voeren op de corresponderende klasse, gecalld vanuit het hoofdbestand van de WordPress plugin.
In Gato GraphQL wordt dit bijvoorbeeld gedaan in gatographql.php
:
$pluginFile = __FILE__;
$pluginVersion = '2.4.0';
$pluginName = __('Gato GraphQL', 'gatographql');
PluginApp::getMainPluginManager()->register(new Plugin(
$pluginFile,
$pluginVersion,
$pluginName
))->setup();
setup methode:
Op het niveau van de ancestor bevat setup
de gemeenschappelijke logica tussen de plugin en zijn uitbreidingen, zoals het uitschrijven van de plugin wanneer deze wordt gedeactiveerd. Deze methode is niet definitief; het kan overschreven worden door de inheriting klassen om hun functionaliteit toe te voegen:
abstract class AbstractPlugin implements PluginInterface
{
// ...
public function setup(): void
{
register_deactivation_hook(
$this->getPluginFile(),
$this->deactivate(...)
);
}
public function deactivate(): void
{
$this->removePluginVersion();
}
private function removePluginVersion(): void
{
$pluginVersions = get_option('gatographql-plugin-versions', []);
unset($pluginVersions[$this->pluginBaseName]);
update_option('gatographql-plugin-versions', $pluginVersions);
}
}
De setup methode van de hoofdplugin:
De methode setup
van de hoofdplugin initialiseert de levenscyclus van de applicatie. Het voert de functionaliteit van de hoofdplugin uit via methodes als initialize
, configureComponents
, configure
, en boot
, en activeert overeenkomstige action hooks voor uitbreidingen:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
public function setup(): void
{
parent::setup();
add_action('plugins_loaded', function (): void
{
// 1. Initialize main plugin
$this->initialize();
// 2. Initialize extensions
do_action('gatographql:initializeExtension');
// 3. Configure main plugin components
$this->configureComponents();
// 4. Configure extension components
do_action('gatographql:configureExtensionComponents');
// 5. Configure main plugin
$this->configure();
// 6. Configure extension
do_action('gatographql:configureExtension');
// 7. Boot main plugin
$this->boot();
// 8. Boot extension
do_action('gatographql:bootExtension');
}
// ...
}
// ...
}
Extension setup methode:
De klasse AbstractExtension
voert zijn logica uit op de overeenkomstige hooks:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
// ...
final public function setup(): void
{
parent::setup();
add_action('plugins_loaded', function (): void
{
// 2. Initialize extensions
add_action(
'gatographql:initializeExtension',
$this->initialize(...)
);
// 4. Configure extension components
add_action(
'gatographql:configureExtensionComponents',
$this->configureComponents(...)
);
// 6. Configure extension
add_action(
'gatographql:configureExtension',
$this->configure(...)
);
// 8. Boot extension
add_action(
'gatographql:bootExtension',
$this->boot(...)
);
}, 20);
}
}
Methoden initialize
, configureComponents
, configure
, en boot
zijn gemeenschappelijk voor zowel de hoofdplugin als de uitbreidingen en kunnen logica delen. Deze gedeelde logica wordt opgeslagen in de klasse AbstractPlugin
.
De methode configure
configureert bijvoorbeeld de plugin of uitbreidingen, door callPluginInitializationConfiguration
te callen, die verschillende implementaties heeft voor de hoofdplugin en uitbreidingen en gedefinieerd is als abstract en getModuleClassConfiguration
, die een standaard gedrag geeft maar overschreven kan worden als dat nodig is:
abstract class AbstractPlugin implements PluginInterface
{
// ...
public function configure(): void
{
$this->callPluginInitializationConfiguration();
$appLoader = App::getAppLoader();
$appLoader->addModuleClassConfiguration($this->getModuleClassConfiguration());
}
abstract protected function callPluginInitializationConfiguration(): void;
/**
* @return array<class-string<ModuleInterface>,mixed> [key]: Module class, [value]: Configuration
*/
public function getModuleClassConfiguration(): array
{
return [];
}
}
De hoofdplugin levert zijn implementatie voor callPluginInitializationConfiguration
:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->pluginInitializationConfiguration->initialize();
}
}
Op dezelfde manier levert de uitbreidingsklasse zijn implementatie:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->extensionInitializationConfiguration?->initialize();
}
}
Methoden initialize
, configureComponents
en boot
zijn gedefinieerd op het niveau van de ancestor en kunnen worden overschreven door overervende klassen:
abstract class AbstractPlugin implements PluginInterface
{
// ...
public function initialize(): void
{
$moduleClasses = $this->getModuleClassesToInitialize();
App::getAppLoader()->addModuleClassesToInitialize($moduleClasses);
}
/**
* @return array<class-string<ModuleInterface>> List of `Module` class to initialize
*/
abstract protected function getModuleClassesToInitialize(): array;
public function configureComponents(): void
{
$classNamespace = ClassHelpers::getClassPSR4Namespace(get_called_class());
$moduleClass = $classNamespace . '\\Module';
App::getModule($moduleClass)->setPluginFolder(dirname($this->pluginFile));
}
public function boot(): void
{
// By default, do nothing
}
}
Alle methoden kunnen overschreven worden door AbstractMainPlugin
of AbstractExtension
om ze uit te breiden met hun eigen functionaliteit.
Voor de hoofdplugin verwijdert de methode setup
ook alle caching van de WordPress instantie wanneer de plugin of een van zijn uitbreidingen wordt geactiveerd of gedeactiveerd:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
public function setup(): void
{
parent::setup();
// ...
// Main-plugin specific methods
add_action(
'activate_plugin',
function (string $pluginFile): void {
$this->maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);
}
);
add_action(
'deactivate_plugin',
function (string $pluginFile): void {
$this->maybeRegenerateContainerWhenPluginActivatedOrDeactivated($pluginFile);
}
);
}
public function maybeRegenerateContainerWhenPluginActivatedOrDeactivated(string $pluginFile): void
{
// Removed code for simplicity
}
// ...
}
Op dezelfde manier verwijdert de methode deactivate
caching en voert boot
alleen voor de hoofdplugin extra action hooks uit:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
public function deactivate(): void
{
parent::deactivate();
$this->removeTimestamps();
}
protected function removeTimestamps(): void
{
$userSettingsManager = UserSettingsManagerFacade::getInstance();
$userSettingsManager->removeTimestamps();
}
public function boot(): void
{
parent::boot();
add_filter(
'admin_body_class',
function (string $classes): string {
$extensions = PluginApp::getExtensionManager()->getExtensions();
$commercialExtensionActivatedLicenseObjectProperties = SettingsHelpers::getCommercialExtensionActivatedLicenseObjectProperties();
foreach ($extensions as $extension) {
$extensionCommercialExtensionActivatedLicenseObjectProperties = $commercialExtensionActivatedLicenseObjectProperties[$extension->getPluginSlug()] ?? null;
if ($extensionCommercialExtensionActivatedLicenseObjectProperties === null) {
continue;
}
return $classes . ' is-gatographql-customer';
}
return $classes;
}
);
}
}
Uit alle hierboven gepresenteerde code is het duidelijk dat we bij het ontwerpen en coderen van een WordPress plugin rekening moeten houden met de behoeften van de uitbreidingen en dat we code zoveel mogelijk moeten hergebruiken. Het implementeren van goede Object-georiënteerde programmeerpatronen (zoals de SOLID principes) helpt om dit te bereiken, waardoor de codebase onderhoudbaar wordt voor de lange termijn.
De versieafhankelijkheid declareren en valideren
Omdat de extensie erft van een PHP klasse die wordt geleverd door de plugin, is het cruciaal om te valideren dat de vereiste versie van de plugin aanwezig is. Als je dat niet doet, kan dat conflicten veroorzaken die de site platleggen.
Als bijvoorbeeld de klasse AbstractExtension
wordt bijgewerkt met cruciale wijzigingen en een major versie 4.0.0
uitbrengt ten opzichte van de vorige 3.4.0
, kan het laden van de extensie zonder de versie te controleren resulteren in een PHP fout, waardoor WordPress niet kan worden geladen.
Om dit te voorkomen moet de extensie valideren dat de geïnstalleerde plugin versie 3.x.x
is. Als versie 4.0.0
is geïnstalleerd, wordt de extensie uitgeschakeld, waardoor fouten worden voorkomen.
De extensie kan deze validatie uitvoeren met de volgende logica, uitgevoerd op de plugins_loaded
hook (omdat de hoofdplugin dan al geladen is) in het hoofdpluginbestand van de extensie. Deze logica heeft toegang tot de ExtensionManager
klasse, die is opgenomen in de hoofdplugin om extensies te beheren:
/**
* Create and set-up the extension
*/
add_action(
'plugins_loaded',
function (): void {
/**
* Extension's name and version.
*
* Use a stability suffix as supported by Composer.
*/
$extensionVersion = '1.1.0';
$extensionName = __('Gato GraphQL - Extension Template');
/**
* The minimum version required from the Gato GraphQL plugin
* to activate the extension.
*/
$gatoGraphQLPluginVersionConstraint = '^1.0';
/**
* Validate Gato GraphQL is active
*/
if (!class_exists(\GatoGraphQL\GatoGraphQL\Plugin::class)) {
add_action('admin_notices', function () use ($extensionName) {
printf(
'<div class="notice notice-error"><p>%s</p></div>',
sprintf(
__('Plugin <strong>%s</strong> is not installed or activated. Without it, plugin <strong>%s</strong> will not be loaded.'),
__('Gato GraphQL'),
$extensionName
)
);
});
return;
}
$extensionManager = \GatoGraphQL\GatoGraphQL\PluginApp::getExtensionManager();
if (!$extensionManager->assertIsValid(
GatoGraphQLExtension::class,
$extensionVersion,
$extensionName,
$gatoGraphQLPluginVersionConstraint
)) {
return;
}
// Load Composer’s autoloader
require_once(__DIR__ . '/vendor/autoload.php');
// Create and set-up the extension instance
$extensionManager->register(new GatoGraphQLExtension(
__FILE__,
$extensionVersion,
$extensionName,
))->setup();
}
);
Let op hoe de uitbreiding een dependency verklaart van versiebeperking ^1.0
van de hoofdplugin (met behulp van Composer’s versiebeperkingen). Dus als versie 2.0.0
van Gato GraphQL is geïnstalleerd, zal de extensie niet worden geactiveerd.
De versiebeperking wordt gevalideerd via de methode ExtensionManager::assertIsValid
, die Semver::satisfies
callt (geleverd door het pakket composer/semver
):
use Composer\Semver\Semver;
class ExtensionManager extends AbstractPluginManager
{
/**
* Validate that the required version of the Gato GraphQL for WP plugin is installed.
*
* If the assertion fails, it prints an error on the WP admin and returns false
*
* @param string|null $mainPluginVersionConstraint the semver version constraint required for the plugin (eg: "^1.0" means >=1.0.0 and <2.0.0)
*/
public function assertIsValid(
string $extensionClass,
string $extensionVersion,
?string $extensionName = null,
?string $mainPluginVersionConstraint = null,
): bool {
$mainPlugin = \GatoGraphQL\GatoGraphQL\PluginApp::getMainPluginManager()->getPlugin();
$mainPluginVersion = $mainPlugin->getPluginVersion();
if (
$mainPluginVersionConstraint !== null && !Semver::satisfies(
$mainPluginVersion,
$mainPluginVersionConstraint
)
) {
$this->printAdminNoticeErrorMessage(
sprintf(
__('Extension or bundle <strong>%s</strong> requires plugin <strong>%s</strong> to satisfy version constraint <code>%s</code>, but the current version <code>%s</code> does not. The extension or bundle has not been loaded.', 'gatographql'),
$extensionName ?? $extensionClass,
$mainPlugin->getPluginName(),
$mainPluginVersionConstraint,
$mainPlugin->getPluginVersion(),
)
);
return false;
}
return true;
}
protected function printAdminNoticeErrorMessage(string $errorMessage): void
{
\add_action('admin_notices', function () use ($errorMessage): void {
$adminNotice_safe = sprintf(
'<div class="notice notice-error"><p>%s</p></div>',
$errorMessage
);
echo $adminNotice_safe;
});
}
}
Integratietests uitvoeren tegen een WordPress server
Om het gemakkelijker te maken voor externe ontwikkelaars om uitbreidingen voor je plugins te maken, moet je ze voorzien van tools voor ontwikkeling en testen, inclusief workflows voor hun continue integratie en continue levering (CI/CD) processen.
Tijdens de ontwikkeling kan iedereen eenvoudig een webserver opstarten met DevKinsta, de plugin installeren waarvoor ze de extensie coderen en meteen valideren dat de extensie compatibel is met de plugin.
Om het testen tijdens CI/CD te automatiseren, moeten we de webserver via een netwerk toegankelijk maken voor de CI/CD service. Diensten zoals InstaWP kunnen voor dit doel een sandbox site maken waarop WordPress is geïnstalleerd.
Als de codebase van de extensie wordt gehost op GitHub, dan kunnen ontwikkelaars GitHub Actions gebruiken om integratietests uit te voeren tegen de InstaWP service. De volgende workflow installeert de extensie op een InstaWP sandbox site (naast de laatste stabiele versie van de hoofdplugin) en voert dan de integratietesten uit:
name: Integration tests (InstaWP)
on:
workflow_run:
workflows: [Generate plugins]
types:
- completed
jobs:
provide_data:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
name: Retrieve the GitHub Action artifact URLs to install in InstaWP
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: "ramsey/composer-install@v2"
- name: Retrieve artifact URLs from GitHub workflow
uses: actions/github-script@v6
id: artifact-url
with:
script: |
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const artifactURLs = allArtifacts.data.artifacts.map((artifact) => {
return artifact.url.replace('https://api.github.com/repos', 'https://nightly.link') + '.zip'
}).concat([
"https://downloads.wordpress.org/plugin/gatographql.latest-stable.zip"
]);
return artifactURLs.join(',');
result-encoding: string
- name: Artifact URL for InstaWP
run: echo "Artifact URL for InstaWP - ${{ steps.artifact-url.outputs.result }}"
shell: bash
outputs:
artifact_url: ${{ steps.artifact-url.outputs.result }}
process:
needs: provide_data
name: Launch InstaWP site from template 'integration-tests' and execute integration tests against it
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: "ramsey/composer-install@v2"
- name: Create InstaWP instance
uses: instawp/wordpress-testing-automation@main
id: create-instawp
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}
INSTAWP_TEMPLATE_SLUG: "integration-tests"
REPO_ID: 25
INSTAWP_ACTION: create-site-template
ARTIFACT_URL: ${{ needs.provide_data.outputs.artifact_url }}
- name: InstaWP instance URL
run: echo "InstaWP instance URL - ${{ steps.create-instawp.outputs.instawp_url }}"
shell: bash
- name: Extract InstaWP domain
id: extract-instawp-domain
run: |
instawp_domain="$(echo "${{ steps.create-instawp.outputs.instawp_url }}" | sed -e s#https://##)"
echo "instawp-domain=$(echo $instawp_domain)" >> $GITHUB_OUTPUT
- name: Run tests
run: |
INTEGRATION_TESTS_WEBSERVER_DOMAIN=${{ steps.extract-instawp-domain.outputs.instawp-domain }} \
INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_USERNAME=${{ steps.create-instawp.outputs.iwp_wp_username }} \
INTEGRATION_TESTS_AUTHENTICATED_ADMIN_USER_PASSWORD=${{ steps.create-instawp.outputs.iwp_wp_password }} \
vendor/bin/phpunit --filter=Integration
- name: Destroy InstaWP instance
uses: instawp/wordpress-testing-automation@main
id: destroy-instawp
if: ${{ always() }}
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INSTAWP_TOKEN: ${{ secrets.INSTAWP_TOKEN }}
INSTAWP_TEMPLATE_SLUG: "integration-tests"
REPO_ID: 25
INSTAWP_ACTION: destroy-site
Deze workflow opent het .zip bestand via Nightly Link, een service die toegang geeft tot een artefact van GitHub zonder in te loggen, wat de configuratie van InstaWP vereenvoudigt.
De extensie-plugin vrijgeven
We kunnen tools leveren om te helpen bij het uitbrengen van de extensies, waarbij we de procedures zoveel mogelijk automatiseren.
De Monorepo Builder is een bibliotheek voor het beheren van elk PHP project, inclusief een WordPress plugin. Het biedt het commando monorepo-builder release
om een versie van het project vrij te geven, waarbij de major, minor of patch component van de versie wordt verhoogd volgens semantisch versiebeheer.
Dit commando voert een reeks release workers uit, wat PHP klassen zijn die bepaalde logica uitvoeren. De standaard workers zijn er een die een git tag
aanmaakt met de nieuwe versie en een andere die de tag naar de remote repository pusht. Custom workers kunnen voor, na of tussen deze stappen worden geïnjecteerd.
De release workers worden geconfigureerd via een configuratiebestand:
use Symplify\MonorepoBuilder\Config\MBConfig;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\AddTagToChangelogReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\PushNextDevReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\PushTagReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\SetCurrentMutualDependenciesReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\SetNextMutualDependenciesReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\TagVersionReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\UpdateBranchAliasReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\UpdateReplaceReleaseWorker;
return static function (MBConfig $mbConfig): void {
// release workers - in order to execute
$mbConfig->workers([
UpdateReplaceReleaseWorker::class,
SetCurrentMutualDependenciesReleaseWorker::class,
AddTagToChangelogReleaseWorker::class,
TagVersionReleaseWorker::class,
PushTagReleaseWorker::class,
SetNextMutualDependenciesReleaseWorker::class,
UpdateBranchAliasReleaseWorker::class,
PushNextDevReleaseWorker::class,
]);
};
We kunnen aangepaste release workers leveren om het release proces uit te breiden, aangepast aan de behoeften van een WordPress plugin. Bijvoorbeeld, de InjectStableTagVersionInPluginReadmeFileReleaseWorker
stelt de nieuwe versie in als de “Stable tag” vermelding in het readme.txt bestand van de extensie:
use Nette\Utils\Strings;
use PharIo\Version\Version;
use Symplify\SmartFileSystem\SmartFileInfo;
use Symplify\SmartFileSystem\SmartFileSystem;
class InjectStableTagVersionInPluginReadmeFileReleaseWorker implements ReleaseWorkerInterface
{
public function __construct(
// This class is provided by the Monorepo Builder
private SmartFileSystem $smartFileSystem,
) {
}
public function getDescription(Version $version): string
{
return 'Have the "Stable tag" point to the new version in the plugin\'s readme.txt file';
}
public function work(Version $version): void
{
$replacements = [
'/Stable tag:\s+[a-z0-9.-]+/' => 'Stable tag: ' . $version->getVersionString(),
];
$this->replaceContentInFiles(['/readme.txt'], $replacements);
}
/**
* @param string[] $files
* @param array<string,string> $regexPatternReplacements regex pattern to search, and its replacement
*/
protected function replaceContentInFiles(array $files, array $regexPatternReplacements): void
{
foreach ($files as $file) {
$fileContent = $this->smartFileSystem->readFile($file);
foreach ($regexPatternReplacements as $regexPattern => $replacement) {
$fileContent = Strings::replace($fileContent, $regexPattern, $replacement);
}
$this->smartFileSystem->dumpFile($file, $fileContent);
}
}
}
Door InjectStableTagVersionInPluginReadmeFileReleaseWorker
toe te voegen aan de configuratielijst, wordt bij het uitvoeren van het monorepo-builder release
commando om een nieuwe versie van de plugin vrij te geven, de “Stable tag” in het readme.txt bestand van de extensie automatisch bijgewerkt.
De extensie-plugin publiceren naar de WP.org directory
We kunnen ook een workflow verspreiden om de extensie naar de WordPress Plugin Directory te publiceren. Bij het taggen van het project op de externe repository zal de volgende workflow de WordPress extensie plugin publiceren naar de directory:
# See: https://github.com/10up/action-wordpress-plugin-deploy#deploy-on-pushing-a-new-tag
name: Deploy to WordPress.org Plugin Directory (SVN)
on:
push:
tags:
- "*"
jobs:
tag:
name: New tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: WordPress Plugin Deploy
uses: 10up/action-wordpress-plugin-deploy@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SLUG: ${{ secrets.SLUG }}
Deze workflow gebruikt de 10up/action-wordpress-plugin-deploy
actie, die de code ophaalt van een Git repository en het naar de WordPress.org SVN repository pusht, wat de operatie vereenvoudigt.
Samenvatting
Bij het maken van een uitbreidbare plugin voor WordPress, is het ons doel om het zo gemakkelijk mogelijk te maken voor externe ontwikkelaars om deze uit te breiden, waardoor de kans op een levendig ecosysteem rondom onze plugins wordt gemaximaliseerd.
Hoewel het verstrekken van uitgebreide documentatie ontwikkelaars kan helpen bij het uitbreiden van de plugin, is een nog effectievere aanpak het leveren van de benodigde PHP code en tools voor het ontwikkelen, testen en uitbrengen van hun uitbreidingen.
Door de extra code die uitbreidingen nodig hebben direct in onze plugin op te nemen, vereenvoudigen we het proces voor ontwikkelaars.
Ben jij van plan om je WordPress plugin uitbreidbaar te maken? Laat het ons weten in de comments.
Laat een reactie achter