: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
namespace WPForms\Requirements;
* Requirements management.
* Whether deactivate addon if requirements not met.
const DEACTIVATE_IF_NOT_MET = true;
* Whether to show PHP version notice.
const SHOW_PHP_NOTICE = true;
* Whether to show PHP extension notice.
const SHOW_EXT_NOTICE = true;
* Whether to show WordPress version notice.
const SHOW_WP_NOTICE = true;
* Whether to show WPForms version notice.
const SHOW_WPFORMS_NOTICE = true;
* Whether to show license level notice.
const SHOW_LICENSE_NOTICE = false;
* Whether to show addon version notice.
const SHOW_ADDON_NOTICE = true;
* Keys of the requirements' arrays.
const WPFORMS = 'wpforms';
const LICENSE = 'license';
const PRIORITY = 'priority';
const ADDON_VERSION_CONSTANT = 'addon_version_constant';
const VERSION = 'version';
const COMPARE = 'compare';
const COMPARE_DEFAULT = '>=';
* Development version of WPForms. Can be specified in an addon.
const WPFORMS_DEV_VERSION_IN_ADDON = '{WPFORMS_VERSION}';
* Plus, Pro and Top level licenses.
* Must be a list separated by comma and space.
const PLUS_PRO_AND_TOP = [ 'plus', 'pro', 'elite', 'agency', 'ultimate' ];
* Pro and Top level licenses.
* Must be a list separated by comma and space.
const PRO_AND_TOP = [ 'pro', 'elite', 'agency', 'ultimate' ];
* Must be a list separated by comma and space.
const TOP = [ 'elite', 'agency', 'ultimate' ];
* Default minimal addon requirements.
self::WPFORMS => self::WPFORMS_DEV_VERSION_IN_ADDON,
self::LICENSE => self::PRO_AND_TOP,
* @todo Add custom message for form-templates-pack.
// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned, WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow
* Array has the format 'addon basename' => 'addon requirements array'.
* The requirement array can have the following keys:
* self::PHP ('php') for the minimal PHP version required,
* self::EXT ('ext') for the PHP extensions required,
* self::WP ('wp') for the minimal WordPress version required,
* self::WPFORMS ('wpforms') for the minimal WPForms version required,
* self::LICENSE ('license') for the license level required,
* self::ADDON ('addon') for the minimal addon version required,
* self::ADDON_VERSION_CONSTANT ('addon_version_constant') for the addon version constant.
* self::PRIORITY ('priority') for the priority of the current requirements.
* 'php' value can be string like '5.6' or an array like 'php' => [ 'version' => '7.2', compare => '=' ].
* 'ext' value can be string like 'curl' or an array like 'ext' => [ 'curl', 'mbstring' ].
* 'wp' value can be string like '5.5' or an array like 'wp' => [ 'version' => '6.4', compare => '=' ].
* 'wpforms' value can be string like '1.8.2' or an array like 'wpforms' => [ 'version' => '1.7.5', compare => '=' ].
* When 'wpforms' value is '{WPFORMS_VERSION}', it is not checked and should be used for development.
* 'license' value can be string like 'elite, agency, ultimate', an array like 'license' => [ 'elite', 'agency', 'ultimate' ].
* When 'license' value is an empty like null, false, [], it is not checked.
* 'addon' value can be string like '2.0.1' or an array like 'addon' => [ 'version' => '2.0.1', 'compare' => '<=' ].
* 'addon_version_constant' must be a string like 'WPFORMS_ACTIVECAMPAIGN_VERSION'.
* 'priority' must be an integer like 20. By default, it is 10.
* By default, 'compare' is '>='.
* Default addon version constant is formed from addon directory name like this:
* wpforms-activecampaign -> WPFORMS_ACTIVECAMPAIGN_VERSION.
* Requirements can be specified here or in the addon as a parameter of wpforms_requirements().
* The priorities from lower to higher (if PRIORITY is not set or equal):
* 1. Default parameters from $this->defaults.
* 2. Current array $this->requirements.
* 3. Parameter of wpforms_requirements() call in the addon.
* Settings with a higher priority overwrite lower priority settings.
* Minimal required version of WPForms should be specified in the addons.
* Minimal required version of addons should be specified here, in $this->requirements array.
* We do not plan to restrict the lower addon version so far.
* However, if in the future we may need to do so,
* we should add to the addon-related requirement array the line like
* self::ADDON => '1.x.x' or
* self::ADDON => '{WPFORMS_ACTIVECAMPAIGN_VERSION}'.
* Here 1.x.x is the specific addon version, and
* WPFORMS_ACTIVECAMPAIGN_VERSION is the addon version constant name.
* The script will replace the addon version constant name during the addon release.
private $requirements = [
'wpforms-activecampaign/wpforms-activecampaign.php' => [
self::LICENSE => self::TOP,
'wpforms-authorize-net/wpforms-authorize-net.php' => [
self::LICENSE => self::TOP,
'wpforms-aweber/wpforms-aweber.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-calculations/wpforms-calculations.php' => [],
'wpforms-campaign-monitor/wpforms-campaign-monitor.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-captcha/wpforms-captcha.php' => [
self::LICENSE => 'basic, plus, pro, elite, agency, ultimate',
self::VERSION => [ '1.8.3', '1.8.7' ],
self::COMPARE => [ '>=', '<' ],
'wpforms-conversational-forms/wpforms-conversational-forms.php' => [],
'wpforms-convertkit/wpforms-convertkit.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-coupons/wpforms-coupons.php' => [],
'wpforms-drip/wpforms-drip.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-form-abandonment/wpforms-form-abandonment.php' => [],
'wpforms-form-locker/wpforms-form-locker.php' => [],
'wpforms-form-pages/wpforms-form-pages.php' => [],
'wpforms-form-templates-pack/wpforms-form-templates-pack.php' => [
self::VERSION => '1.6.8',
'wpforms-geolocation/wpforms-geolocation.php' => [],
'wpforms-getresponse/wpforms-getresponse.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-google-sheets/wpforms-google-sheets.php' => [],
'wpforms-hubspot/wpforms-hubspot.php' => [
self::LICENSE => self::TOP,
'wpforms-lead-forms/wpforms-lead-forms.php' => [],
'wpforms-mailchimp/wpforms-mailchimp.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-mailerlite/wpforms-mailerlite.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-offline-forms/wpforms-offline-forms.php' => [],
'wpforms-paypal-commerce/wpforms-paypal-commerce.php' => [],
'wpforms-paypal-standard/wpforms-paypal-standard.php' => [],
'wpforms-post-submissions/wpforms-post-submissions.php' => [],
'wpforms-salesforce/wpforms-salesforce.php' => [
self::LICENSE => self::TOP,
'wpforms-save-resume/wpforms-save-resume.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-sendinblue/wpforms-sendinblue.php' => [
self::LICENSE => self::PLUS_PRO_AND_TOP,
'wpforms-signatures/wpforms-signatures.php' => [
'wpforms-square/wpforms-square.php' => [
'wpforms-stripe/wpforms-stripe.php' => [],
'wpforms-surveys-polls/wpforms-surveys-polls.php' => [],
'wpforms-user-journey/wpforms-user-journey.php' => [],
'wpforms-user-registration/wpforms-user-registration.php' => [],
'wpforms-webhooks/wpforms-webhooks.php' => [
self::LICENSE => self::TOP,
'wpforms-zapier/wpforms-zapier.php' => [],
// phpcs:enable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned, WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow
private $addon_requirements = [];
private $not_validated = [];
* Get a single instance of the addon.
public static function get_instance(): Requirements {
private function init() {
foreach ( $this->requirements as $basename => $requirement ) {
$this->init_addon_requirements( $basename );
private function hooks() {
add_action( 'admin_init', [ $this, 'deactivate' ] );
add_action( 'admin_notices', [ $this, 'show_notices' ] );
add_action( 'network_admin_notices', [ $this, 'show_notices' ] );
* @param array $addon_requirements Addon requirements.
public function validate( array $addon_requirements ): bool {
$this->addon_requirements = $addon_requirements;
// Requirements' array must contain the addon main filename.
if ( ! isset( $this->addon_requirements['file'] ) ) {
$this->basename = plugin_basename( $this->addon_requirements['file'] );
$this->init_addon_requirements( $this->basename );
$this->addon_requirements = $this->merge_requirements(
$this->requirements[ $this->basename ],
$this->addon_requirements
$php_valid = $this->validate_php();
$ext_valid = $this->validate_ext();
$wp_valid = $this->validate_wp();
$wpforms_valid = $this->validate_wpforms();
$license_valid = $this->validate_license();
$addon_valid = $this->validate_addon();
if ( $php_valid && $ext_valid && $wp_valid && $wpforms_valid && $license_valid && $addon_valid ) {
$this->validated[] = $this->basename;
$this->requirements[ $this->basename ] = $this->addon_requirements;
return empty( $this->not_validated[ $this->basename ] );
* Merge requirements by priority.
* @param array $defaults Default requirements.
* @param array $requirements Requirements.
* @param array $addon_requirements Addon requirements.
private function merge_requirements( array $defaults, array $requirements, array $addon_requirements ): array {
$chunks = [ $defaults, $requirements, $addon_requirements ];
static function ( $chunk1, $chunk2 ) {
// phpcs:ignore WPForms.Formatting.EmptyLineBeforeReturn.AddEmptyLineBeforeReturnStatement
return ( $chunk1[ self::PRIORITY ] ?? 10 ) <=> ( $chunk2[ self::PRIORITY ] ?? 10 );
return array_merge( ...$chunks );
* Try to deactivate not valid addon.
* @param string $plugin Path to the plugin file relative to the plugins' directory.
* @return bool True if addon was deactivated.
public function deactivate_not_valid_addon( string $plugin ): bool {
if ( ! self::DEACTIVATE_IF_NOT_MET ) {
// No more actions if we not demand deactivation.
if ( ! $this->is_wpforms_addon( $plugin ) ) {
// No more actions if it is not a wpforms addon.
// Finalise activation of wpforms addon.
$addon_load_function = $this->get_addon_load_function( $plugin );
if ( ! is_callable( $addon_load_function ) ) {
// Invoke addon loading function, which checks requirements.
// Addon may get deactivated after this statement.
return ! is_plugin_active( $plugin );
* Check whether a plugin is a wpforms addon.
* @param string $plugin Path to the plugin file relative to the plugins' directory.
private function is_wpforms_addon( string $plugin ): bool {
if ( strpos( $plugin, 'wpforms-' ) !== 0 ) {
// No more actions for general plugin.
* There are some forks of our plugins having the 'wpforms-' prefix.
* We have to check the Author name in the plugin header.
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
$plugin_author = isset( $plugin_data['Author'] ) ? strtolower( $plugin_data['AuthorName'] ) : '';
// No more actions on forks.
return $plugin_author === 'wpforms';
* Get addon function hooked on wpforms_load.
* @param string $plugin Path to the plugin file relative to the plugins' directory.
private function get_addon_load_function( string $plugin ): string {
$callbacks = $wp_filter['wpforms_loaded']->callbacks;
$prefix = explode( '/', $plugin, 2 )[0];