WordPress-Plugins können mit zusätzlichen Funktionen erweitert werden, wie beliebte Plugins wie WooCommerce und Gravity Forms zeigen. In dem Artikel Ein WordPress-Plugin so gestalten, dass es Erweiterungen unterstützt erfahren wir, dass es zwei Möglichkeiten gibt, ein WordPress-Plugin erweiterbar zu machen:
- Durch das Einrichten von Hooks (Aktionen und Filter), mit denen Erweiterungs-Plugins ihre eigenen Funktionen einfügen können
- Durch die Bereitstellung von PHP-Klassen, die Erweiterungs-Plugins erben können
Die erste Methode stützt sich eher auf die Dokumentation, in der die verfügbaren Hooks und ihre Verwendung detailliert beschrieben werden. Die zweite Methode hingegen bietet gebrauchsfertigen Code für Erweiterungen und macht eine umfangreiche Dokumentation überflüssig. Das ist von Vorteil, da die Erstellung von Dokumentation neben dem Code die Verwaltung und Veröffentlichung des Plugins erschweren kann.
Die direkte Bereitstellung von PHP-Klassen ersetzt die Dokumentation effektiv durch Code. Anstatt zu lehren, wie eine Funktion zu implementieren ist, liefert das Plugin den notwendigen PHP-Code und vereinfacht damit die Aufgabe für Drittentwickler.
Wir wollen uns einige Techniken ansehen, um dies zu erreichen, mit dem Ziel, ein Ökosystem von Integrationen rund um unser WordPress-Plugin zu schaffen.
Definition von PHP-Basisklassen im WordPress-Plugin
Das WordPress-Plugin enthält PHP-Klassen, die von Erweiterungs-Plugins verwendet werden können. Diese PHP-Klassen werden möglicherweise nicht vom Haupt-Plugin selbst verwendet, sondern sind speziell für die Nutzung durch andere Plugins vorgesehen.
Schauen wir uns an, wie dies im Open-Source-Plugin Gato GraphQL umgesetzt wird.
AbstractPlugin-Klasse:
AbstractPlugin
stellt ein Plugin dar, sowohl für das Haupt-Plugin von Gato GraphQL als auch für seine Erweiterungen:
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
erweitert AbstractPlugin
, um das Hauptplugin zu repräsentieren:
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:
Ähnlich, AbstractExtension
erweitert AbstractPlugin
, um ein Erweiterungs-Plugin darzustellen:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
public function __construct(
string $pluginFile,
string $pluginVersion,
?string $pluginName,
protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration,
) {
parent::__construct(
$pluginFile,
$pluginVersion,
$pluginName,
);
}
// ...
}
Beachte, dass AbstractExtension
im Hauptplugin enthalten ist und Funktionen zur Registrierung und Initialisierung einer Erweiterung bereitstellt. Es wird jedoch nur von Erweiterungen verwendet, nicht vom Hauptplugin selbst.
Die Klasse AbstractPlugin
enthält einen gemeinsamen Initialisierungscode, der zu verschiedenen Zeitpunkten aufgerufen wird. Diese Methoden sind auf der Ebene der Vorfahren definiert, werden aber von den vererbenden Klassen entsprechend ihrer Lebenszyklen aufgerufen.
Das Hauptplugin und die Erweiterungen werden initialisiert, indem die Methode setup
der entsprechenden Klasse ausgeführt wird, die in der Hauptdatei des WordPress-Plugins aufgerufen wird.
In Gato GraphQL geschieht dies zum Beispiel in gatographql.php
:
$pluginFile = __FILE__;
$pluginVersion = '2.4.0';
$pluginName = __('Gato GraphQL', 'gatographql');
PluginApp::getMainPluginManager()->register(new Plugin(
$pluginFile,
$pluginVersion,
$pluginName
))->setup();
setup-Methode:
Auf der Vorgängerebene enthält setup
die gemeinsame Logik zwischen dem Plugin und seinen Erweiterungen, z. B. die Aufhebung der Registrierung, wenn das Plugin deaktiviert wird. Diese Methode ist nicht endgültig; sie kann von den vererbenden Klassen überschrieben werden, um ihre Funktionalität zu erweitern:
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);
}
}
Die Setup-Methode des Haupt-Plugins:
Die setup
Methode des Hauptplugins initialisiert den Lebenszyklus der Anwendung. Sie führt die Funktionen des Hauptplugins über Methoden wie initialize
, configureComponents
, configure
und boot
aus und löst die entsprechenden Aktionshaken für Erweiterungen aus:
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');
}
// ...
}
// ...
}
Methode „Extension setup“:
Die Klasse AbstractExtension
führt ihre Logik über die entsprechenden Hooks aus:
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);
}
}
Die Methoden initialize
, configureComponents
, configure
und boot
sind sowohl für das Hauptplugin als auch für die Erweiterungen gleich und können eine gemeinsame Logik haben. Diese gemeinsame Logik wird in der Klasse AbstractPlugin
gespeichert.
Die Methode configure
beispielsweise konfiguriert das Plugin oder die Erweiterungen, indem sie callPluginInitializationConfiguration
aufruft, die für das Hauptplugin und die Erweiterungen unterschiedliche Implementierungen hat und als abstrakt definiert ist, und getModuleClassConfiguration
, das ein Standardverhalten bietet, aber bei Bedarf überschrieben werden kann:
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 [];
}
}
Das Haupt-Plugin stellt seine Implementierung für callPluginInitializationConfiguration
zur Verfügung:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->pluginInitializationConfiguration->initialize();
}
}
Ebenso stellt die Erweiterungsklasse ihre Implementierung zur Verfügung:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->extensionInitializationConfiguration?->initialize();
}
}
Die Methoden initialize
, configureComponents
und boot
sind auf der Vorfahrenebene definiert und können von den erbenden Klassen überschrieben werden:
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 können von AbstractMainPlugin
oder AbstractExtension
überschrieben werden, um sie mit ihren eigenen Funktionen zu erweitern.
Für das Hauptplugin entfernt die Methode setup
auch jegliches Caching aus der WordPress-Instanz, wenn das Plugin oder eine seiner Erweiterungen aktiviert oder deaktiviert wird:
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
}
// ...
}
In ähnlicher Weise entfernt die Methode deactivate
das Caching und boot
führt zusätzliche Aktionshaken nur für das Hauptplugin aus:
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;
}
);
}
}
Aus dem oben dargestellten Code wird deutlich, dass wir bei der Entwicklung und Programmierung eines WordPress-Plugins die Bedürfnisse der Erweiterungen berücksichtigen und den Code so oft wie möglich wiederverwenden müssen. Die Umsetzung solider objektorientierter Programmiermuster (wie die SOLID-Prinzipien) hilft dabei und macht die Codebasis langfristig wartbar.
Deklaration und Validierung der Versionsabhängigkeit
Da die Erweiterung von einer PHP-Klasse erbt, die vom Plugin bereitgestellt wird, ist es wichtig zu überprüfen, ob die erforderliche Version des Plugins vorhanden ist. Wenn du das nicht tust, kann es zu Konflikten kommen, die die Website zum Absturz bringen.
Wenn z. B. die Klasse AbstractExtension
aktualisiert wird und eine Hauptversion 4.0.0
von der vorherigen Version 3.4.0
veröffentlicht wird, kann das Laden der Erweiterung ohne Überprüfung der Version zu einem PHP-Fehler führen und WordPress am Laden hindern.
Um dies zu vermeiden, muss die Erweiterung überprüfen, ob das installierte Plugin die Version 3.x.x
hat. Wenn die Version 4.0.0
installiert ist, wird die Erweiterung deaktiviert und verhindert so Fehler.
Die Erweiterung kann diese Überprüfung mit der folgenden Logik durchführen, die am plugins_loaded
-Hook (da das Haupt-Plugin zu diesem Zeitpunkt bereits geladen ist) in der Haupt-Plugin-Datei der Erweiterung ausgeführt wird. Diese Logik greift auf die ExtensionManager
Klasse zu, die im Hauptplugin enthalten ist, um Erweiterungen zu verwalten:
/**
* 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();
}
);
Beachte, dass die Erweiterung eine Abhängigkeit von der Versionsbeschränkung ^1.0
des Hauptplugins deklariert (unter Verwendung der Versionsbeschränkungen des Composers). Wenn also die Version 2.0.0
von Gato GraphQL installiert ist, wird die Erweiterung nicht aktiviert.
Die Versionsbeschränkung wird über die Methode ExtensionManager::assertIsValid
überprüft, die Semver::satisfies
aufruft (bereitgestellt durch das Paket 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;
});
}
}
Integrationstests gegen einen WordPress-Server durchführen
Um es Entwicklern von Drittanbietern zu erleichtern, Erweiterungen für deine Plugins zu erstellen, solltest du ihnen Werkzeuge für die Entwicklung und das Testen zur Verfügung stellen, einschließlich Workflows für ihre kontinuierlichen Integrations- und Lieferprozesse (CI/CD).
Während der Entwicklung kann jeder mit DevKinsta ganz einfach einen Webserver aufsetzen, das Plugin installieren, für das er die Erweiterung programmiert, und sofort überprüfen, ob die Erweiterung mit dem Plugin kompatibel ist.
Um die Tests während des CI/CD zu automatisieren, muss der Webserver über ein Netzwerk für den CI/CD-Dienst zugänglich sein. Dienste wie InstaWP können zu diesem Zweck eine Sandbox-Website mit installiertem WordPress erstellen.
Wenn die Codebasis der Erweiterung auf GitHub gehostet wird, können Entwickler GitHub Actions nutzen, um Integrationstests gegen den InstaWP-Dienst durchzuführen. Der folgende Arbeitsablauf installiert die Erweiterung auf einer InstaWP-Sandbox-Website (zusammen mit der neuesten stabilen Version des Haupt-Plugins) und führt dann die Integrationstests aus:
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
Dieser Workflow greift auf die .zip-Datei über Nightly Link zu, einen Dienst, der es ermöglicht, auf ein Artefakt von GitHub zuzugreifen, ohne sich anzumelden, was die Konfiguration von InstaWP vereinfacht.
Freigabe des Erweiterungs-Plugins
Wir können Werkzeuge bereitstellen, die bei der Veröffentlichung der Erweiterungen helfen und die Abläufe so weit wie möglich automatisieren.
Der Monorepo Builder ist eine Bibliothek zur Verwaltung beliebiger PHP-Projekte, einschließlich eines WordPress-Plugins. Er bietet den Befehl monorepo-builder release
, um eine Version des Projekts freizugeben, wobei entweder die Major-, Minor- oder Patch-Komponente der Version gemäß der semantischen Versionierung erhöht wird.
Dieser Befehl führt eine Reihe von Release Workern aus, d. h. PHP-Klassen, die eine bestimmte Logik ausführen. Zu den Standardworkern gehört einer, der eine git tag
mit der neuen Version erstellt, und ein anderer, der das Tag in das entfernte Repository überträgt. Benutzerdefinierte Worker können vor, nach oder zwischen diesen Schritten eingefügt werden.
Die Release Worker werden über eine Konfigurationsdatei konfiguriert:
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,
]);
};
Wir können benutzerdefinierte Release Worker bereitstellen, um den Release-Prozess auf die Bedürfnisse eines WordPress-Plugins zuzuschneiden. Zum Beispiel kann der InjectStableTagVersionInPluginReadmeFileReleaseWorker
setzt die neue Version als „Stable tag“-Eintrag in der readme.txt-Datei der Erweiterung:
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);
}
}
}
Wenn du InjectStableTagVersionInPluginReadmeFileReleaseWorker
zur Konfigurationsliste hinzufügst, wird bei der Ausführung des Befehls monorepo-builder release
zur Veröffentlichung einer neuen Version des Plugins der „Stable-Tag“ in der readme.txt-Datei der Erweiterung automatisch aktualisiert.
Veröffentlichung des Erweiterungs-Plugins im WP.org-Verzeichnis
Wir können auch einen Arbeitsablauf bereitstellen, der die Veröffentlichung der Erweiterung im WordPress Plugin-Verzeichnis unterstützt. Wenn du das Projekt auf dem remoten Repository taggst, veröffentlicht der folgende Workflow das WordPress-Erweiterungs-Plugin im Verzeichnis:
# 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 }}
Dieser Arbeitsablauf verwendet die 10up/action-wordpress-plugin-deploy
Aktion, die den Code aus einem Git-Repository abruft und ihn in das SVN-Repository von WordPress.org pusht, was den Vorgang vereinfacht.
Zusammenfassung
Wenn wir ein erweiterbares Plugin für WordPress entwickeln, wollen wir es Entwicklern von Drittanbietern so einfach wie möglich machen, das Plugin zu erweitern, um so die Chancen auf ein lebendiges Ökosystem rund um unsere Plugins zu erhöhen.
Eine ausführliche Dokumentation kann Entwicklern zeigen, wie sie das Plugin erweitern können. Noch effektiver ist es jedoch, den notwendigen PHP-Code und die Werkzeuge für die Entwicklung, das Testen und die Veröffentlichung ihrer Erweiterungen bereitzustellen.
Indem wir den zusätzlichen Code, den die Erweiterungen benötigen, direkt in unser Plugin integrieren, vereinfachen wir den Prozess für Entwickler.
Hast du vor, dein WordPress-Plugin erweiterbar zu machen? Lass es uns in den Kommentaren wissen.
Schreibe einen Kommentar