Skip to content
Snippets Groups Projects
Verified Commit 59a2c72a authored by Bartek Jaskulski's avatar Bartek Jaskulski
Browse files

improve into wp-hook and some additional unfinished things

parent e2f03018
No related branches found
No related tags found
3 merge requests!3improve into wp-hook and some additional unfinished things,!21.x,!1Draft: Basic implementation of plugin initializer
This commit is part of merge request !2. Comments created here will be created in the context of that merge request.
Showing
with 329 additions and 283 deletions
......@@ -34,22 +34,19 @@
},
"require": {
"php": ">=7.2 | ^8",
"psr/container": "^1 || ^2"
"php-di/php-di": "^6 || ^7"
},
"require-dev": {
"wpdesk/wp-basic-requirements": "^3",
"php-di/php-di": "^6 || ^7",
"phpunit/phpunit": "^8 || ^9",
"symfony/filesystem": "^6.2",
"brain/monkey": "^2.6"
},
"suggest": {
"wpdesk/wp-basic-requirements": "Enables your plugin to check an environment requirement before instantiation, e.g. PHP version or active plugins",
"php-di/php-di": "Allows a plugin to seamlessly integrate with a dependency injection container"
"wpdesk/wp-basic-requirements": "Enables your plugin to check an environment requirement before instantiation, e.g. PHP version or active plugins"
},
"conflict": {
"wpdesk/wp-basic-requirements": "<3, >=4",
"php-di/php-di": "<6, >=8"
"wpdesk/wp-basic-requirements": "<3, >=4"
},
"scripts": {
"test": "vendor/bin/phpunit --bootstrap tests/bootstrap.php ./tests"
......
<?php
declare( strict_types=1 );
namespace WPDesk\Init\Bundle;
use WPDesk\Init\HookProvider\ConcreteProviders\ActivationDate;
use WPDesk\Init\HookProvider\ConcreteProviders\I18n;
use WPDesk\Init\HookProvider\ConcreteProviders\WooCommerceHPOSCompatibility;
class CommonBundle {
public static function subscribers(): iterable {
return [
ActivationDate::class,
I18n::class,
WooCommerceHPOSCompatibility::class
];
}
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init\Bundle;
use WPDesk\Migrations\Migrator;
class MigrationSubscriber implements \WPDesk\WPHook\HookSubscriber\HookSubscriber {
/** @var Migrator */
private $migrator;
public function __construct(Migrator $migrator) {
$this->migrator = $migrator;
}
public static function register(): iterable {
yield 'plugins_loaded' => 'migrate';
}
public function migrate(): void {
$this->migrator->migrate();
}
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init\Bundle;
use WPDesk\Init\Configuration\ReadableConfig;
use WPDesk\Init\Plugin;
use WPDesk\Migrations\Migrator;
use WPDesk\Migrations\WpdbMigrator;
class MigrationsBundle {
public function build( ContainerBuilder $builder, ReadableConfig $config ): void {
$builder->addDefinitions( [
Migrator::class => function ( Plugin $plugin ) use ( $config ) {
return WpdbMigrator::from_directories(
$config->get( 'migrations', [] ),
$config->get( 'migrations_table_name', $plugin->get_slug() . 'migrations' )
);
}
] );
}
public static function subscribers(): iterable {
return [
MigrationSubscriber::class
];
}
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init;
/**
* Something that can be instantiated conditionally.
*
* In hook provider context a class marked as being conditional can be asked whether its
* hooks should be fired and integrated into WordPress system. An example would be a service that is
* only available on the admin backend.
*
* This allows for a more systematic and automated optimization of how the
* different parts of the plugin are enabled or disabled.
*
* @author Alain Schlesser <alain.schlesser@gmail.com>
*/
interface Conditional {
/**
* Check whether the conditional object is currently needed.
*
* @return bool Whether the conditional object is needed.
*/
public function is_needed(): bool;
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\Configuration;
class Configuration implements ReadableConfig, \ArrayAccess {
/** @var array<string, mixed> */
private $config;
public function __construct( array $config ) {
$this->config = $config;
}
/**
* @param string $key
* @param mixed $default
*
* @return mixed|null
*/
public function get( string $key, $default = null ) {
return $this->config[ $key ] ?? $default;
}
public function has( string $key ): bool {
return isset( $this->config[ $key ] );
}
public function set( string $key, $value ): void {
$this->config[ $key ] = $value;
}
public function remove( string $key ): void {
unset( $this->config[ $key ] );
}
public function offsetExists( $offset ) {
return $this->has( $offset );
}
#[\ReturnTypeWillChange]
public function offsetGet( $offset ) {
return $this->get( $offset );
}
public function offsetSet( $offset, $value ): void {
$this->set( $offset, $value );
}
public function offsetUnset( $offset ): void {
$this->remove( $offset );
}
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init\Configuration;
/**
* Allows to read configuration.
*/
interface ReadableConfig {
public function get( string $key, $default = null );
public function has( string $key ): bool;
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace WPDesk\Init;
use Psr\Container\ContainerInterface;
trait ContainerAwareTrait {
/** @var ContainerInterface */
private $container;
public function set_container( ContainerInterface $container ): void {
$this->container = $container;
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
use WPDesk\Init\HooksProvider;
use WPDesk\Init\PluginAwareInterface;
use WPDesk\Init\PluginAwareTrait;
abstract class AbstractHookProvider implements HooksProvider, PluginAwareInterface {
use PluginAwareTrait;
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
use WPDesk\Init\HooksProvider;
use WPDesk\Init\PluginAwareInterface;
use WPDesk\Init\PluginAwareTrait;
class ActivationDate implements HooksProvider, PluginAwareInterface {
use PluginAwareTrait;
public function register_hooks(): void {
add_action(
'activated_plugin',
function ( $plugin_file, $network_wide = false ) {
if ( ! $network_wide && $this->plugin->get_basename() === $plugin_file ) {
$option_name = 'plugin_activation_' . $plugin_file;
$activation_date = get_option( $option_name, '' );
if ( '' === $activation_date ) {
$activation_date = current_time( 'mysql' );
update_option( $option_name, $activation_date );
}
}
}
);
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider\ConcreteProviders;
use WPDesk\Init\Plugin;
use WPDesk\WPHook\HookSubscriber\HookSubscriber;
class ActivationDate implements HookSubscriber {
/** @var Plugin */
private $plugin;
public function __construct( Plugin $plugin ) {
$this->plugin = $plugin;
}
public static function register(): iterable {
yield 'activated_plugin' => 'save_activation_date';
}
public function save_activation_date( $plugin_file, $network_wide = false ): void {
if ( ! $network_wide && $this->plugin->get_basename() === $plugin_file ) {
$option_name = 'plugin_activation_' . $plugin_file;
$activation_date = get_option( $option_name, '' );
if ( '' === $activation_date ) {
$activation_date = current_time( 'mysql' );
update_option( $option_name, $activation_date );
}
}
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
namespace WPDesk\Init\HookProvider\ConcreteProviders;
abstract class ActivationHook extends AbstractHookProvider {
use WPDesk\Init\HookProvider\HooksProvider;
use WPDesk\Init\Plugin;
abstract class ActivationHook implements HooksProvider {
/** @var Plugin */
private $plugin;
public function __construct( Plugin $plugin ) {
$this->plugin = $plugin;
}
public function register_hooks(): void {
register_activation_hook(
......
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider\ConcreteProviders;
use Psr\Container\ContainerInterface;
use WPDesk\Init\HookProvider\HooksProvider;
use WPDesk\Init\Plugin;
class ContainerHookProvider implements HooksProvider {
/** @var Plugin */
private $plugin;
/** @var ContainerInterface */
private $container;
public function __construct( Plugin $plugin, ContainerInterface $container ) {
$this->plugin = $plugin;
$this->container = $container;
}
public function register_hooks(): void {
add_action(
'plugins_loaded',
function () {
if ( $this->container->has( 'hooks' ) ) {
$this->plugin->register_hooks( ...$this->container->get( 'hooks' ) );
}
}
);
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
namespace WPDesk\Init\HookProvider\ConcreteProviders;
abstract class DeactivationHook extends AbstractHookProvider {
use WPDesk\Init\HookProvider\HooksProvider;
use WPDesk\Init\Plugin;
abstract class DeactivationHook implements HooksProvider {
/** @var Plugin */
private $plugin;
public function __construct( Plugin $plugin ) {
$this->plugin = $plugin;
}
public function register_hooks(): void {
register_deactivation_hook(
......
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
namespace WPDesk\Init\HookProvider\ConcreteProviders;
use WPDesk\Init\HooksProvider;
use WPDesk\Init\PluginAwareInterface;
use WPDesk\Init\PluginAwareTrait;
use WPDesk\Init\Plugin;
use WPDesk\WPHook\HookSubscriber\HookSubscriber;
class I18n implements HooksProvider, PluginAwareInterface {
use PluginAwareTrait;
class I18n implements HookSubscriber {
/** @var Plugin */
private $plugin;
public function register_hooks(): void {
if ( did_action( 'plugins_loaded' ) ) {
$this->load_textdomain();
} else {
add_action( 'plugins_loaded', [ $this, 'load_textdomain' ] );
public function __construct( Plugin $plugin ) {
$this->plugin = $plugin;
}
public static function register(): iterable {
yield 'plugins_loaded' => 'load_textdomain';
}
public function load_textdomain(): void {
......
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider\ConcreteProviders;
use WPDesk\Init\HookProvider\HooksProvider;
use WPDesk\Init\Plugin;
use WPDesk\WPHook\HookSubscriber\Conditional;
use WPDesk\WPHook\HookSubscriber\Deferred;
use WPDesk\WPHook\HookSubscriber\HookSubscriber;
class WooCommerceHPOSCompatibility implements HookSubscriber, Deferred, Conditional {
/** @var Plugin */
private $plugin;
public function __construct( Plugin $plugin ) {
$this->plugin = $plugin;
}
public static function register(): iterable {
yield 'before_woocommerce_init' => 'register_hooks';
}
public static function register_after() {
return 'woocommerce_init';
}
public function is_needed(): bool {
return class_exists( 'WooCommerce' );
}
public function register_hooks(): void {
$features_util_class = '\\' . 'Automattic' . '\\' . 'WooCommerce' . '\\' . 'Utilities' . '\\' . 'FeaturesUtil';
if ( class_exists( $features_util_class ) ) {
$features_util_class::declare_compatibility( 'custom_order_tables', $this->plugin->get_basename(), true );
}
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
use WPDesk\Init\ContainerAwareInterface;
use WPDesk\Init\ContainerAwareTrait;
use WPDesk\Init\HooksProvider;
use WPDesk\Init\PluginAwareInterface;
use WPDesk\Init\PluginAwareTrait;
class ContainerHookProvider implements HooksProvider, PluginAwareInterface, ContainerAwareInterface {
use PluginAwareTrait;
use ContainerAwareTrait;
public function register_hooks(): void {
add_action(
'plugins_loaded',
function () {
if ( $this->container->has( 'hooks' ) ) {
foreach ( $this->container->get( 'hooks' ) as $hook_provider ) {
$this->plugin->register_hooks( $hook_provider );
}
}
}
);
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init\HookProvider;
use WPDesk\Init\HooksProvider;
use WPDesk\Init\PluginAwareInterface;
use WPDesk\Init\PluginAwareTrait;
class WooCommerceHPOSCompatibility implements HooksProvider, PluginAwareInterface {
use PluginAwareTrait;
public function register_hooks(): void {
add_action(
'before_woocommerce_init',
function () {
$features_util_class = '\\' . 'Automattic' . '\\' . 'WooCommerce' . '\\' . 'Utilities' . '\\' . 'FeaturesUtil';
if ( class_exists( $features_util_class ) ) {
$features_util_class::declare_compatibility( 'custom_order_tables', $this->plugin->get_basename(), true );
}
}
);
}
}
<?php
declare( strict_types=1 );
namespace WPDesk\Init;
/**
* Hook is a special kind of service which lives only to integrate with WordPress lifecycle
* system. By design, hook providers should be lightweight classes which focus its main logic on
* integration with actions and filters.
*
* @author Brady Vercher
*/
interface HooksProvider {
/**
* Register hooks for the plugin.
*/
public function register_hooks(): void;
}
\ No newline at end of file
<?php
declare( strict_types=1 );
namespace WPDesk\Init;
/**
* Hooks trait.
*
* Allows protected and private methods to be used as hook callbacks in PHP <8.1. Since PHP 8.1
* you are able to take advantage of first class callable and register private methods in hooks
* without any workarounds.
*
* @author John P. Bloch
* @link https://github.com/johnpbloch/wordpress-dev/blob/master/src/Hooks.php
*/
trait HooksTrait {
/**
* Add a WordPress filter.
*
* @param callable $method
*
* @return true
*/
protected function add_filter(
string $hook,
$method,
int $priority = 10,
int $arg_count = 1
): bool {
return add_filter(
$hook,
$this->map_filter( $method, $arg_count ),
$priority,
$arg_count
);
}
/**
* Add a WordPress action.
*
* This is an alias of add_filter().
*
* @param callable $method
*
* @return true
*/
protected function add_action( string $hook, $method, int $priority = 10, int $arg_count = 1 ): bool {
return $this->add_filter( $hook, $method, $priority, $arg_count );
}
/**
* Remove a WordPress filter.
*
* @param callable $method
*
* @return bool Whether the function existed before it was removed.
*/
protected function remove_filter(
string $hook,
$method,
int $priority = 10,
int $arg_count = 1
): bool {
return remove_filter(
$hook,
$this->map_filter( $method, $arg_count ),
$priority
);
}
/**
* Remove a WordPress action.
*
* This is an alias of remove_filter().
*
* @param callable $method
*
* @return bool Whether the function is removed.
*/
protected function remove_action(
string $hook,
$method,
int $priority = 10,
int $arg_count = 1
): bool {
return $this->remove_filter( $hook, $method, $priority, $arg_count );
}
/**
* Map a filter to a closure that inherits the class' internal scope.
*
* This allows hooks to use protected and private methods.
*
* @param string $callable
* @param int $arg_count
*
* @return \Closure The callable actually attached to a WP hook
*/
private function map_filter( $callable, int $arg_count ): \Closure {
if ( is_string( $callable ) && method_exists( $this, $callable ) ) {
$object = $this;
$method = $callable;
}
if ( is_array( $callable ) ) {
[ $object, $method ] = $callable;
}
return static function () use ( $object, $method, $arg_count ) {
return $object->{$method}( ...array_slice( func_get_args(), 0, $arg_count ) );
};
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment