The development and design of an WordPress plugin, which is developed with the help by PHP classes (r)

Aug 15, 2024

-sidebar-toc> -language-notice>

  1. Installing hooks (actions and filters) to plugin extensions, make sure they add functions of their own
  2. When you are offering PHP classes Extensions to plugins can be employed to

The first relies on documentation, which describes the available hooks as well as the way they're used. The other, however it comes with software that is extensible to enhance functionality. It means there's not a need to provide a comprehensive description. This is advantageous since writing documents and code could have a negative impact on the administration of plugins, aswell being detrimental to distribution.

This article will look at ways for making this happen to build a community around WordPress. WordPress plugin.

The basic PHP classes are used by WordPress. WordPress plugin

Let's see what this could be able to integrate into the open-source Gato GraphQL plugin.

AbstractPlugin class:

AbstractPlugin can be known as an extension plug-in that integrates together with Gato GraphQL plugin and its extensions:

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 class:

AbstractMainPlugin increases the capabilities of AbstractPlugin up to an extent that allows it to replicate the primary tasks:

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 class:

This is also true of AbstractExtension. AbstractExtension is able to extend AbstractPlugin in order to make it an extension plugin

abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface public function __construct( string $pluginFile, string $pluginVersion, ?string $pluginName, protected ?ExtensionInitializationConfigurationInterface $extensionInitializationConfiguration, ) parent::__construct( $pluginFile, $pluginVersion, $pluginName, ); // ... 

It is vital to understand that this is due to the nature of the plugin. AbstractExtension is the main element in the plug-in. It permits you to start by joining an extension. However, it is solely used for adding extensions, and is not a plugin by itself.

AbstractPlugin is in AbstractPlugin is one of AbstractPlugin classes. AbstractPlugin AbstractPlugin can be described as an abstract type that is a shared initalization procedure that is executed at various times. It is built on the basis of the previous one, however they are utilized by other classes that inherit on the base of the amount of time they have been in existence.

The plugin's primary and extensions start by running the initialization process within the class used within the primary WordPress plugin's application.

In Gato GraphQL, this is performed through gatographql.php:

$pluginFile = __FILE__; $pluginVersion = '2.4.0'; $pluginName = __('Gato GraphQL', 'gatographql'); PluginApp::getMainPluginManager()->register(new Plugin( $pluginFile, $pluginVersion, $pluginName ))->setup(); 

Procedure for setting up

If an extension is the parent extension, the configuration includes the same logic that applies to the extension and the plugin in the case of removing them from registration after the plugin has been removed. The method does not have to be be absolute, however, it can be altered by inheriting classes in order to enhance the capabilities of an extension:

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); 

How do you set up the first plugin?

The main configuration method starts the full setup of the plugin. The most common method to perform the tasks is to execute procedures like an initialization, configureComponents, configure, and the begin and stop actions hooks that extend the functionality of extensions.

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'); // ... // ...

Setting up extensions

AbstractExtension class AbstractExtension class is able to use its logic through hooks in classes:

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);

The techniques to beginning, configureComponents, configure, and begin are all used by the main plugin as extensions. They can all have the similar concept. The reasoning behind these techniques can be found in the AbstractPlugin class.

For example, the configure method configures the plugin or extensions, calling callPluginInitializationConfiguration, which has different implementations for the main plugin and extensions and is defined as abstract and getModuleClassConfiguration, which provides a default behavior but can be overridden if needed:

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,mixed> [key]: Module class, [value]: Configuration */ public function getModuleClassConfiguration(): array return []; 

The main plugin provides its implementation for callPluginInitializationConfiguration:

abstract class AbstractMainPlugin extends AbstractPlugin implements MainPluginInterface // ... protected function callPluginInitializationConfiguration(): void $this->pluginInitializationConfiguration->initialize(); 

This extension class equipped to provide an own implementation

abstract class AbstractExtension extends AbstractPlugin implements ExtensionInterface // ... protected function callPluginInitializationConfiguration(): void $this->extensionInitializationConfiguration?->initialize(); 

Methods that start, configureComponents and the time they begin will be determined by the parents. They can be altered by inheriting classes

abstract class AbstractPlugin implements PluginInterface // ... public function initialize(): void $moduleClasses = $this->getModuleClassesToInitialize(); App::getAppLoader()->addModuleClassesToInitialize($moduleClasses); /** * @return array> 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

Every method is customizable by using AbstractMainPlugin or AbstractExtension to incorporate features of your choice.

The primary method of configuration method also removes all cached WordPress instances when the plugin or extension is removed or activated:

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 // ... 

The same way, this deactivate method gets rid of cache as well as boots and also performs other hooks which are exclusive to the plugin. However, they are only intended to:

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; );

Checking for the dependency, and then showing that it is a version

Since the extension is derived partially from PHP class that is used to create the extension, it's crucial to make sure that the correct edition of the extension is installed. If there is an oversight, it may result in problems that could lead to the loss of the website.

It is the same for example, in the instance that AbstractExtension is upgraded to version 4.0.0, the AbstractExtension class, has been modified to incorporate major changes to the code and can be downloaded with upgrade 4.0.0 of the prior version 3.4.0. 4.0.0 from the earlier version 3.4.0, loading the extension without verification of version can cause a PHP error which causes WordPress from loading.

To prevent the chance of this happening in order to avoid this taking place, the extension should be in use with versions 3.x.x. When 4.0.0 version is installed, the extension will be removed. 4.0.0 is installed the extension is deleted in order to prevent any errors.

It is possible to check this by following the method which is explained in this article. It it is carried out through the plugins_loaded hook. the plugins_loaded hook (since the core extension plugin is in operation up to the present) can be found within the plugin's main file. It allows users to connect with extensions using Extension Manager classes. Extensions management class forms a part of the plugin's core. The class is accountable for the management of extensions.

Code >/** Create and set the extension. This process involves to include( "plugins_loaded" function () Extension eliminated. /*** Name of extension as along with the version. It's recommended to select an extension suffix that's stable since it's recognized by Composer. */ $extensionVersion = '1.1.0';$extensionName is __('Gato GraphQL - Extension Template'); *** The minimum version required from Gato GraphQL is 1.1.0. Gato GraphQL plugin * to let the extension be operational. */ $gatoGraphQLPluginVersionConstraint = '^1.0'; /** * Validate Gato GraphQL is active */ if (!class_exists(\GatoGraphQL\GatoGraphQL\Plugin::class)) add_action('admin_notices', function () use ($extensionName) printf( '%s', sprintf( __('Plugin %s is not installed or activated. If the plugin doesn't work and it doesn't seem to be in use and is unable to be installed, the plugin is not activated. '), __('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(); );

Important to note that this extension can to state its dependency on the restriction on version ^1.0 of the principal extension (using Composer's version constraints). If version 2.0.0 of Gato GraphQL is installed and not switched off, the extension is disabled.

The version constraint is validated via the ExtensionManager::assertIsValid method, which calls Semver::satisfies (provided by the composer/semver package):

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 to be successful, it will report an error report to the administrator of WP and cause an error to be displayed. String

Integration testing by making use of WordPress server WordPress server

To automate testing throughout the process of CI/CD, it is necessary to connect using an internet connection to the server for CI/CD. Software such as InstaWP can be utilized to build Sandbox websites with WordPress and can be used for Sandboxes.

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 

The workflow downloads files from the .zip file via the nightly Link that allows access to artifacts on GitHub and doesn't require users to sign in. This workflow can also be beneficial in the process of establishing the setup of InstaWP.

Extension plugins can be easily accessed.

Tools are available to aid in the process of releasing extensions. They make this process as easy as is possible.

It's an extension to the Monorepo Builder. It is a program which is utilized to control each PHP project. It is also an element of WordPress. WordPress plugin. It works with monorepobuilder release command which lets you publish an update on your project. It's possible to modify or expand the size of the major, minor or the patch portions of an update depending on what specifics of the software used for the version.

This command is able to start a group of release workers comprised of PHP classes that perform certain actions. Built-in builders can be used as default. One builder creates one which creates Git tags and then updates them to the latest version. Others builders push tags to remote repositories. A custom-designed worker may be added before, following or even between the actions.

The release worker can be configured using the configuration file

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, ]); ; 

Our own release team assists in the process of the release of WordPress plugins specifically designed to satisfy needs of WordPress. WordPress plugin. For example, the InjectStableTagVersionInPluginReadmeFileReleaseWorker sets the new version as the "Stable tag" entry in the extension's readme.txt file:

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 $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);

By adding InjectStableTagVersionInPluginReadmeFileReleaseWorker to the configuration list, whenever executing the monorepo-builder release command to release a new version of the plugin, the "Stable tag" in the extension's readme.txt file will be automatically updated.

The extension plugin is downloaded from WP.org. WP.org directory.

If you prefer, follow the path of creating an automated workflow to help you release the extension to WordPress's WordPress the directory for plugins. Once you have tagged the plugin in the remote repository, following the instructions below will permit the upload of your WordPress extension into the 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 

Summary

If we create extensions-friendly plugins for WordPress we'll permit third-party developers to create extensions that work with the plugin, to increase the likelihood of creating communities around our plugins.

The data provided in the documentation will help users to discover how to enhance the performance of the plugin. The method used is to offer all the PHP tools as well as the source code to create, test and testing extensions. finally, it's time to release them.

Integrating any additional code needed by extensions directly into our extension plugin is more convenient to extension developers.

     Have you considered developing your own WordPress plugin that is extensible? Let us know your thoughts making comments in the section below.

Leonardo Losoviz

Leo is a blogger. He's a journalist as well as an author who writes on modern Web techniques for developing typically using PHP, WordPress and GraphQL. Leo is available through leoloso.com and twitter.com/losoviz.

The post was made available on this web site.

The article was first published on this website.

The post first appeared on this site. Here

This article first appeared here. this site

Article was posted on here