I plugin di WordPress possono essere estesi con funzionalità aggiuntive, come dimostrano plugin popolari come WooCommerce e Gravity Forms. Nell’articolo “Progettare un plugin WordPress che supporti le estensioni“, abbiamo visto che esistono due modi principali per rendere estensibile un plugin WordPress:
- Creando degli hook (azioni e filtri) che permettano ai plugin di estendere le proprie funzionalità.
- Fornendo delle classi PHP che i plugin di estensione possono ereditare.
Il primo metodo si basa maggiormente sulla documentazione, che illustra in dettaglio gli hook disponibili e il loro utilizzo. Il secondo metodo, invece, offre codice pronto all’uso per le estensioni, riducendo la necessità di una documentazione esaustiva. Questo è un vantaggio perché la creazione di documentazione accanto al codice può complicare la gestione e il rilascio del plugin.
La fornitura diretta di classi PHP sostituisce efficacemente la documentazione con il codice. Invece di insegnare come implementare una funzione, il plugin fornisce il codice PHP necessario, semplificando il compito agli sviluppatori di terze parti.
Esploriamo alcune tecniche per raggiungere questo obiettivo, con l’obiettivo finale di promuovere un ecosistema di integrazioni intorno al nostro plugin WordPress.
Definire le classi PHP di base nel plugin WordPress
Il plugin di WordPress includerà classi PHP destinate all’utilizzo da parte dei plugin di estensione. Queste classi PHP potrebbero non essere utilizzate dal plugin principale, ma sono fornite appositamente per essere utilizzate da altri.
Vediamo come questo viene implementato nel plugin open-source Gato GraphQL.
Classe AbstractPlugin:
AbstractPlugin
rappresenta un plugin, sia per il plugin principale di Gato GraphQL che per le sue estensioni:
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);
}
// ...
}
Classe AbstractMainPlugin:
AbstractMainPlugin
estende AbstractPlugin
per rappresentare il plugin principale:
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:
Allo stesso modo, AbstractExtension
estende AbstractPlugin
per rappresentare un plugin di estensione:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
public function __construct(
string $pluginFile,
string $pluginVersion,
?string $pluginName,
protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration,
) {
parent::__construct(
$pluginFile,
$pluginVersion,
$pluginName,
);
}
// ...
}
Da notare che AbstractExtension
è incluso nel plugin principale e fornisce le funzionalità per registrare e inizializzare un’estensione. Tuttavia, viene utilizzato solo dalle estensioni e non dal plugin principale.
La classe AbstractPlugin
contiene codice di inizializzazione condiviso invocato in momenti diversi. Questi metodi sono definiti a livello di antenato ma vengono invocati dalle classi ereditarie in base al loro ciclo di vita.
Il plugin principale e le estensioni vengono inizializzati eseguendo il metodo setup
sulla classe corrispondente, invocato dal file principale del plugin WordPress.
Ad esempio, in Gato GraphQL, questo avviene in gatographql.php
:
$pluginFile = __FILE__;
$pluginVersion = '2.4.0';
$pluginName = __('Gato GraphQL', 'gatographql');
PluginApp::getMainPluginManager()->register(new Plugin(
$pluginFile,
$pluginVersion,
$pluginName
))->setup();
Metodo setup:
A livello di antenati, setup
contiene la logica comune tra il plugin e le sue estensioni, come ad esempio la loro disiscrizione quando il plugin viene disattivato. Questo metodo non è definitivo; può essere sovrascritto dalle classi ereditarie per aggiungere le loro funzionalità:
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);
}
}
Metodo setup del plugin principale:
Il metodo setup
del plugin principale inizializza il ciclo di vita dell’applicazione. Esegue le funzionalità del plugin principale attraverso metodi come initialize
, configureComponents
, configure
e boot
e attiva gli hook di azione corrispondenti per le estensioni:
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');
}
// ...
}
// ...
}
Metodo di configurazione delle estensioni:
La classe AbstractExtension
esegue la sua logica sugli hook corrispondenti:
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);
}
}
I metodi initialize
, configureComponents
, configure
e boot
sono comuni sia al plugin principale che alle estensioni e possono condividere la logica. Questa logica condivisa è memorizzata nella classe AbstractPlugin
.
Ad esempio, il metodo configure
configura il plugin o le estensioni, chiamando callPluginInitializationConfiguration
, che ha implementazioni diverse per il plugin principale e le estensioni ed è definito come astratto e getModuleClassConfiguration
, che fornisce un comportamento predefinito ma può essere sovrascritto se necessario:
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 [];
}
}
Il plugin principale fornisce la sua implementazione per callPluginInitializationConfiguration
:
abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->pluginInitializationConfiguration->initialize();
}
}
Allo stesso modo, la classe di estensione fornisce la sua implementazione:
abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface
{
// ...
protected function callPluginInitializationConfiguration(): void
{
$this->extensionInitializationConfiguration?->initialize();
}
}
I metodi initialize
, configureComponents
e boot
sono definiti a livello di antenato e possono essere sovrascritti dalle classi ereditarie:
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
}
}
Tutti i metodi possono essere sovrascritti da AbstractMainPlugin
o AbstractExtension
per estenderli con le loro funzionalità personalizzate.
Per il plugin principale, il metodo setup
rimuove anche la cache dall’istanza di WordPress quando il plugin o una delle sue estensioni viene attivata o disattivata:
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
}
// ...
}
Allo stesso modo, il metodo deactivate
rimuove la cache e boot
esegue hook di azione aggiuntivi solo per il plugin principale:
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;
}
);
}
}
Da tutto il codice presentato sopra, è chiaro che quando si progetta e si codifica un plugin di WordPress, è necessario considerare le esigenze delle sue estensioni e riutilizzare il codice il più possibile. L’implementazione di validi modelli di programmazione orientata agli oggetti (come i principi SOLID ) aiuta a raggiungere questo obiettivo, rendendo la base di codice manutenibile a lungo termine.
Dichiarare e convalidare la dipendenza dalla versione
Poiché l’estensione eredita da una classe PHP fornita dal plugin, è fondamentale convalidare la presenza della versione richiesta del plugin. In caso contrario, potrebbero verificarsi conflitti che potrebbero causare il blocco del sito.
Ad esempio, se la classe AbstractExtension
viene aggiornata con modifiche di rottura e rilascia una versione maggiore 4.0.0
rispetto alla precedente 3.4.0
, caricare l’estensione senza verificare la versione potrebbe causare un errore PHP, impedendo il caricamento di WordPress.
Per evitare questo problema, l’estensione deve convalidare che il plugin installato sia la versione 3.x.x
. Quando viene installata la versione 4.0.0
, l’estensione viene disabilitata, evitando così gli errori.
L’estensione può effettuare questa convalida utilizzando la seguente logica, eseguita sull’hook plugins_loaded
(poiché il plugin principale sarà già stato caricato) nel file del plugin principale dell’estensione. Questa logica accede alla classe ExtensionManager
che è inclusa nel plugin principale per gestire le estensioni:
/**
* 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();
}
);
Da notare come l’estensione dichiari una dipendenza dal vincolo di versione ^1.0
del plugin principale (utilizzando i vincoli di versione di Composer). Pertanto, quando viene installata la versione 2.0.0
di Gato GraphQL, l’estensione non verrà attivata.
Il vincolo di versione viene convalidato tramite il metodo ExtensionManager::assertIsValid
, che chiama Semver::satisfies
(fornito dal pacchetto 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;
});
}
}
Eseguire test di integrazione su un server WordPress
Per facilitare gli sviluppatori di terze parti nella creazione di estensioni per i vostri plugin, fornite loro strumenti per lo sviluppo e il test, compresi i flussi di lavoro per i loro processi di continuous integration e continuous delivery (CI/CD).
Durante lo sviluppo, chiunque può facilmente avviare un server web utilizzando DevKinsta, installare il plugin per il quale si sta progettando l’estensione e convalidare immediatamente la compatibilità dell’estensione con il plugin.
Per automatizzare i test durante il CI/CD, è necessario che il server web sia accessibile in rete al servizio CI/CD. Servizi come InstaWP possono creare un sito sandbox con WordPress installato a questo scopo.
Se la base di codice dell’estensione è ospitata su GitHub, gli sviluppatori possono utilizzare GitHub Actions per eseguire test di integrazione contro il servizio InstaWP. Il seguente flusso di lavoro installa l’estensione su un sito sandbox InstaWP (insieme all’ultima versione stabile del plugin principale) e poi esegue i test di integrazione:
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
Questo flusso di lavoro accede al file .zip tramite Nightly Link, un servizio che consente di accedere a un artefatto da GitHub senza effettuare il login, semplificando la configurazione di InstaWP.
Rilasciare il plugin di estensione
Possiamo fornire strumenti per aiutare a rilasciare le estensioni, automatizzando il più possibile le procedure.
Monorepo Builder è una libreria per la gestione di qualsiasi progetto PHP, compreso un plugin WordPress. Fornisce il comando monorepo-builder release
per rilasciare una versione del progetto, incrementando la componente maggiore, minore o patch della versione in base al versioning semantico.
Questo comando esegue una serie di release worker, ovvero classi PHP che eseguono una determinata logica. I worker predefiniti includono uno che crea un git tag
con la nuova versione e un altro che invia il tag al repository remoto. I worker personalizzati possono essere iniettati prima, dopo o tra questi passaggi.
I worker di rilascio sono configurati tramite un file di configurazione:
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,
]);
};
Possiamo fornire dei release worker personalizzati per aumentare il processo di rilascio in base alle esigenze di un plugin WordPress. Ad esempio, il file InjectStableTagVersionInPluginReadmeFileReleaseWorker
imposta la nuova versione come voce “Stable tag” nel file readme.txt dell’estensione:
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);
}
}
}
Aggiungendo InjectStableTagVersionInPluginReadmeFileReleaseWorker
all’elenco di configurazione, ogni volta che si esegue il comando monorepo-builder release
per rilasciare una nuova versione del plugin, il “tag Stable” nel file readme.txt dell’estensione verrà aggiornato automaticamente.
Pubblicare il plugin di estensione nella directory di WP.org
Possiamo anche distribuire un workflow per aiutare a rilasciare l’estensione nella directory dei plugin di WordPress. Quando si assegna il tag al progetto sul repository remoto, il seguente flusso di lavoro pubblicherà il plugin di estensione WordPress nella 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 }}
Questo flusso di lavoro utilizza l’azione 10up/action-wordpress-plugin-deploy
che recupera il codice da un repository Git e lo invia al repository SVN di WordPress.org, semplificando l’operazione.
Riepilogo
Quando creiamo un plugin estensibile per WordPress, il nostro obiettivo è quello di rendere il più semplice possibile l’estensione da parte di sviluppatori terzi, massimizzando così le possibilità di promuovere un ecosistema vivace intorno ai nostri plugin.
Sebbene fornire un’ampia documentazione possa guidare gli sviluppatori su come estendere il plugin, un approccio ancora più efficace è quello di fornire il codice PHP e gli strumenti necessari per sviluppare, testare e rilasciare le loro estensioni.
Includendo il codice aggiuntivo necessario alle estensioni direttamente nel nostro plugin, semplifichiamo il processo per gli sviluppatori.
Avete intenzione di rendere estensibile il vostro plugin per WordPress? Fatecelo sapere nella sezione commenti.
Lascia un commento