first commit

This commit is contained in:
2026-04-28 15:13:50 +02:00
commit a95acc355b
63745 changed files with 9487948 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
# Compat Checker for WooCommerce Extensions
A simple library to run compatibility checks for WooCommerce extensions.
## Getting Started
1. Include this library in your WooCommerce plugin's `composer.json` like shown below:
```json
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/woocommerce/grow"
}
],
"require": {
"woocommerce/grow": "dev-compat-checker"
}
}
```
2. Run `composer update` to include the `woocommerce/grow` repo in the `vendor` folder.
3. In the main plugin file that contains the plugin header, add the compatibility check like the below example:
```php
require __DIR__ . '/vendor/autoload.php';
use Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checker;
add_action( 'plugins_loaded', 'wc_plugin_init' );
function wc_plugin_init() {
define( 'WC_BRANDS_VERSION', '1.6.56' ); // WRCS: DEFINED_VERSION.
if ( ! Checker::instance()->is_compatible( __FILE__, WC_BRANDS_VERSION ) ) {
return;
}
// Continue initializing the plugin.
}
```

View File

@@ -0,0 +1,21 @@
{
"name": "woogrow/compat-checker",
"description": "Compatibility checker for Woo Grow extensions",
"version": "0.0.1",
"license": "GPL-3.0",
"minimum-stability": "stable",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Automattic\\WooCommerce\\Grow\\Tools\\CompatChecker\\v0_0_1\\": "src/"
}
},
"require-dev": {
"woocommerce/woocommerce-sniffs": "^0.1.3"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

View File

@@ -0,0 +1,420 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d2bf099e9e6b95c169258509897c9198",
"packages": [],
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.7.2",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0"
},
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"time": "2022-02-04T12:51:07+00:00"
},
{
"name": "phpcompatibility/php-compatibility",
"version": "9.3.5",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
"shasum": ""
},
"require": {
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
},
"conflict": {
"squizlabs/php_codesniffer": "2.6.2"
},
"require-dev": {
"phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
"roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Wim Godden",
"homepage": "https://github.com/wimg",
"role": "lead"
},
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
}
],
"description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
"homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
"keywords": [
"compatibility",
"phpcs",
"standards"
],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibility"
},
"time": "2019-12-27T09:44:58+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-paragonie",
"version": "1.3.2",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
"reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
"reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
"shasum": ""
},
"require": {
"phpcompatibility/php-compatibility": "^9.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"paragonie/random_compat": "dev-master",
"paragonie/sodium_compat": "dev-master"
},
"suggest": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
"roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Wim Godden",
"role": "lead"
},
{
"name": "Juliette Reinders Folmer",
"role": "lead"
}
],
"description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
"homepage": "http://phpcompatibility.com/",
"keywords": [
"compatibility",
"paragonie",
"phpcs",
"polyfill",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
},
"time": "2022-10-25T01:46:02+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-wp",
"version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
"reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
"reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
"shasum": ""
},
"require": {
"phpcompatibility/php-compatibility": "^9.0",
"phpcompatibility/phpcompatibility-paragonie": "^1.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7"
},
"suggest": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
"roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Wim Godden",
"role": "lead"
},
{
"name": "Juliette Reinders Folmer",
"role": "lead"
}
],
"description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
"homepage": "http://phpcompatibility.com/",
"keywords": [
"compatibility",
"phpcs",
"standards",
"static analysis",
"wordpress"
],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
},
"time": "2022-10-24T09:00:36+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.7.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
"bin/phpcs",
"bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
"time": "2023-02-22T23:07:41+00:00"
},
{
"name": "woocommerce/woocommerce-sniffs",
"version": "0.1.3",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-sniffs.git",
"reference": "4576d54595614d689bc4436acff8baaece3c5bb0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/4576d54595614d689bc4436acff8baaece3c5bb0",
"reference": "4576d54595614d689bc4436acff8baaece3c5bb0",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"php": ">=7.0",
"phpcompatibility/phpcompatibility-wp": "^2.1.0",
"wp-coding-standards/wpcs": "^2.3.0"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Claudio Sanches",
"email": "claudio@automattic.com"
}
],
"description": "WooCommerce sniffs",
"keywords": [
"phpcs",
"standards",
"woocommerce",
"wordpress"
],
"support": {
"issues": "https://github.com/woocommerce/woocommerce-sniffs/issues",
"source": "https://github.com/woocommerce/woocommerce-sniffs/tree/0.1.3"
},
"time": "2022-02-17T15:34:51+00:00"
},
{
"name": "wp-coding-standards/wpcs",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "7da1894633f168fe244afc6de00d141f27517b62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62",
"reference": "7da1894633f168fe244afc6de00d141f27517b62",
"shasum": ""
},
"require": {
"php": ">=5.4",
"squizlabs/php_codesniffer": "^3.3.1"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6",
"phpcompatibility/php-compatibility": "^9.0",
"phpcsstandards/phpcsdevtools": "^1.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
"description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
"keywords": [
"phpcs",
"standards",
"wordpress"
],
"support": {
"issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
"time": "2020-05-13T23:57:56+00:00"
}
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": true,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<ruleset name="WooCommerce Coding Standards">
<description>Compat Checker Tool ruleset.</description>
<!-- Configs -->
<config name="testVersion" value="5.6-" />
<!-- Rules -->
<rule ref="WooCommerce-Core" />
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array" value="woogrow-compat-checker" />
</properties>
</rule>
<rule ref="PHPCompatibility">
<exclude-pattern>tests/</exclude-pattern>
</rule>
</ruleset>

View File

@@ -0,0 +1,103 @@
<?php
/**
* The Compatibility Checker tool.
*
* @package Automattic/WooCommerce/Grow/Tools
*/
namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1;
use Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks\WPCompatibility;
use Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks\WCCompatibility;
defined( 'ABSPATH' ) || exit;
/**
* The CompatChecker main class.
*/
class Checker {
/**
* The Checker instance.
*
* @var Checker
*/
private static $instance;
/**
* The Plugin instance.
*
* @return Checker
*/
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Parses the plugin contents to retrieve plugin's metadata.
*
* @param string $plugin_file The Absolute path to the main plugin file.
* @param string $file_version The plugin file version. Can be the same as the plugin version.
*/
public function get_plugin_data( $plugin_file, $file_version ) {
$default_headers = [
'Name' => 'Plugin Name',
'Version' => 'Version',
'RequiresWP' => 'Requires at least',
'RequiresPHP' => 'Requires PHP',
'RequiresWC' => 'WC requires at least',
'TestedWP' => 'Tested up to',
'TestedWC' => 'WC tested up to',
];
$transient_key = 'wc_grow_compat_checker_' . plugin_basename( $plugin_file ) . $file_version;
$plugin_data = get_transient( $transient_key );
if ( false === $plugin_data ) {
$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
$plugin_data['File'] = $plugin_file;
set_transient( $transient_key, $plugin_data, MONTH_IN_SECONDS );
}
return $plugin_data;
}
/**
* Runs all compatibility checks.
*
* @param string $plugin_file_path The Absolute path to the main plugin file.
* @param string $file_version The plugin file version. Can be the same as the plugin version.
*
* @return bool
*/
public function is_compatible( $plugin_file_path, $file_version ) {
$checks = [
WPCompatibility::class,
WCCompatibility::class,
];
$plugin_data = $this->get_plugin_data( $plugin_file_path, $file_version );
$plugin_basename = plugin_basename( $plugin_file_path );
// Remove dismissable notices on plugin deactivation.
register_deactivation_hook(
$plugin_file_path,
[
WCCompatibility::instance( $plugin_basename ),
'remove_dismissable_notices',
]
);
// Run all compatibility checks.
foreach ( $checks as $compatibility ) {
if ( ! $compatibility::instance( $plugin_basename )->is_compatible( $plugin_data ) ) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,190 @@
<?php
/**
* The Compatibility Checker parent.
*
* @package Automattic/WooCommerce/Grow/Tools
*/
namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks;
use WC_Admin_Notices;
defined( 'ABSPATH' ) || exit;
/**
* The CompatCheck class.
*/
abstract class CompatCheck {
/**
* Array of admin notices.
*
* @var array
*/
protected $notices = [];
/**
* Array of CompatCheck instances.
*
* @var array
*/
private static $instances = [];
/**
* The plugin data.
*
* @var array
*/
protected $plugin_data = [];
/**
* Run checks
*
* @return bool
*/
abstract protected function run_checks();
/**
* Get the instance of the CompatCheck object.
*
* @param string $plugin_basename The basename of the plugin.
*
* @return CompatCheck
*/
public static function instance( $plugin_basename ) {
$class = get_called_class();
if ( ! isset( self::$instances[ $class ][ $plugin_basename ] ) ) {
self::$instances[ $class ][ $plugin_basename ] = new $class();
}
return self::$instances[ $class ][ $plugin_basename ];
}
/**
* Adds an admin notice to be displayed.
*
* @param string $slug The slug for the notice.
* @param string $css_class The CSS class for the notice.
* @param string $message The notice message.
*/
protected function add_admin_notice( $slug, $css_class, $message ) {
$screen = get_current_screen();
$hidden = [ 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' ];
$show = isset( $screen->id ) && ! in_array( $screen->id, $hidden, true );
$slug = isset( $this->plugin_data['File'] ) ? plugin_basename( $this->plugin_data['File'] ) . '-' . $slug : $slug;
/**
* The Compat Check filter to show an admin notice.
*
* @since 0.0.1
*
* @param bool $show Whether to show the admin notice.
* @param string $slug The slug for the notice.
*/
if ( ! apply_filters( 'wc_grow_compat_check_show_admin_notice', $show, $slug ) ) {
return;
}
// If the notice is a warning and WooCommerce admin notice system is available. Then use it.
if ( str_contains( $css_class, 'warning' ) && class_exists( WC_Admin_Notices::class ) ) {
// Do not display the notice if it was dismissed.
if ( ! get_user_meta( get_current_user_id(), 'dismissed_' . $slug . '_notice', true ) ) {
WC_Admin_Notices::add_custom_notice( $slug, $message );
}
} else {
$this->notices[ $slug ] = [
'class' => $css_class,
'message' => $message,
];
}
}
/**
* Compares major version.
*
* @param string $left First version number.
* @param string $right Second version number.
* @param string $operator An optional operator. The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
*
* @return int|bool
*/
protected function compare_major_version( $left, $right, $operator = null ) {
$pattern = '/^(\d+\.\d+).*/';
$replace = '$1.0';
$left = preg_replace( $pattern, $replace, $left );
$right = preg_replace( $pattern, $replace, $right );
return version_compare( $left, $right, $operator );
}
/**
* Display admin notices generated by the checker.
*/
public function display_admin_notices() {
$allowed_tags = [
'a' => [
'class' => [],
'href' => [],
'target' => [],
],
'strong' => [],
];
foreach ( $this->notices as $key => $notice ) {
$class = $notice['class'];
$message = $notice['message'];
printf(
'<div class="notice notice-%1$s"><p>%2$s</p></div>',
esc_attr( $class ),
wp_kses( $message, $allowed_tags )
);
}
}
/**
* Sets the plugin data.
*
* @param array $plugin_data The plugin data.
*/
protected function set_plugin_data( $plugin_data ) {
$defaults = [
'Name' => '',
'Version' => '',
'RequiresWP' => '',
'RequiresPHP' => '',
'RequiresWC' => '',
'TestedWP' => '',
'TestedWC' => '',
];
$this->plugin_data = wp_parse_args( $plugin_data, $defaults );
}
/**
* Determines if the plugin is WooCommerce compatible.
*
* @param array $plugin_data The plugin data.
*
* @return bool
*/
public function is_compatible( $plugin_data ) {
$this->set_plugin_data( $plugin_data );
add_action( 'admin_notices', [ $this, 'display_admin_notices' ], 20 );
return $this->run_checks();
}
/**
* Remove dismissable notices.
*/
public function remove_dismissable_notices() {
if ( class_exists( WC_Admin_Notices::class ) ) {
$plugin_basename = plugin_basename( $this->plugin_data['File'] );
$all_notices = WC_Admin_Notices::get_notices();
foreach ( $all_notices as $notice_name ) {
if ( 0 === strpos( $notice_name, $plugin_basename ) ) {
WC_Admin_Notices::remove_notice( $notice_name );
}
}
}
}
}

View File

@@ -0,0 +1,387 @@
<?php
/**
* WooCommerce Compatibility Check.
*
* @package Automattic/WooCommerce/Grow/Tools
*/
namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks;
use Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Exception\IncompatibleException;
defined( 'ABSPATH' ) || exit;
/**
* The WooCommerce Compatibility Check class.
*/
class WCCompatibility extends CompatCheck {
/**
* WooCommerce plugin file path.
*
* @var string
*/
private const WC_PLUGIN_FILE = 'woocommerce/woocommerce.php';
/**
* Define the L-n support policy here.
*
* @var int|float The miniumum supported WooCommerce versions before the latest.
*
* @see https://woocommerce.com/support-policy/
*/
private $min_wc_semver = 0.2; // By default, the latest minus two version.
/**
* Determins if WooCommerce is installed.
*
* @return bool
*/
private function is_wc_installed() {
$installed_plugins = get_plugins();
return isset( $installed_plugins[ self::WC_PLUGIN_FILE ] );
}
/**
* Determines if WooCommerce is activated.
*
* @return bool
*/
private function is_wc_activated() {
return class_exists( 'WooCommerce' );
}
/**
* Gets the version of the currently installed WooCommerce.
*
* @return string|null Woocommerce version number or null if undetermined.
*/
public static function get_wc_version() {
return defined( 'WC_VERSION' ) && WC_VERSION ? WC_VERSION : null;
}
/**
* Determines if the WooCommerce compatible.
*
* @return bool
*/
private function is_wc_compatible() {
$wc_version = $this->get_wc_version();
$wc_version_required = $this->plugin_data['RequiresWC'];
if ( empty( $wc_version_required ) ) {
return true;
}
return version_compare( $wc_version, $wc_version_required, '>=' );
}
/**
* Determines if the WooCommerce version is untested.
*
* @return bool
*/
private function is_wc_untested() {
if ( empty( $this->plugin_data['TestedWC'] ) ) {
return false;
}
$wc_version = $this->get_wc_version();
$wc_version_tested_upto = $this->plugin_data['TestedWC'];
return $this->compare_major_version( $wc_version, $wc_version_tested_upto, '<=' );
}
/**
* Check WooCommerce installation and activation.
*
* @return bool
* @throws IncompatibleException If WooCommerce is not activated.
*/
private function check_wc_installation_and_activation() {
if ( ! $this->is_wc_activated() ) {
add_action( 'admin_notices', [ $this, 'wc_fail_load' ] );
throw new IncompatibleException( esc_html__( 'WooCommerce is not installed or activated.', 'woogrow-compat-checker' ) );
}
return true;
}
/**
* Check WooCommerce version.
*
* @return bool
* @throws IncompatibleException If WooCommerce version is not compatible.
*/
private function check_wc_version() {
if ( ! $this->is_wc_compatible() ) {
add_action( 'admin_notices', [ $this, 'wc_out_of_date' ] );
throw new IncompatibleException( esc_html__( 'WooCommerce version not compatible.', 'woogrow-compat-checker' ) );
}
if ( ! $this->is_wc_untested() ) {
add_action( 'admin_notices', [ $this, 'wc_untested' ] );
}
return true;
}
/**
* Retrieves a list of the latest available WooCommerce versions.
*
* Excludes betas, release candidates and development versions.
* Versions are sorted from most recent to least recent.
*
* @return string[] Array of semver strings.
*/
private function get_latest_wc_versions() {
$latest_wc_versions = get_transient( 'compat_checker_wc_versions' );
if ( ! is_array( $latest_wc_versions ) ) {
/**
* The endpoint to fetch the latest WooCommerce versions.
*
* @link https://codex.wordpress.org/WordPress.org_API
*/
$wp_org_request = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/woocommerce.json', [ 'timeout' => 1 ] );
if ( is_array( $wp_org_request ) && isset( $wp_org_request['body'] ) ) {
$plugin_info = json_decode( $wp_org_request['body'], true );
if ( is_array( $plugin_info ) && ! empty( $plugin_info['versions'] ) && is_array( $plugin_info['versions'] ) ) {
$latest_wc_versions = [];
// Reverse the array as WordPress supplies oldest version first, newest last.
foreach ( array_keys( array_reverse( $plugin_info['versions'] ) ) as $wc_version ) {
// Skip trunk, release candidates, betas and other non-final or irregular versions.
if (
is_string( $wc_version )
&& '' !== $wc_version
&& is_numeric( $wc_version[0] )
&& false === strpos( $wc_version, '-' )
) {
$latest_wc_versions[] = $wc_version;
}
}
set_transient( 'compat_checker_wc_versions', $latest_wc_versions, WEEK_IN_SECONDS );
}
}
}
return is_array( $latest_wc_versions ) ? $latest_wc_versions : [];
}
/**
* Get the L-n version of WooCommerce.
*
* @return string
*/
private function get_supported_wc_version() {
$latest_wc_versions = $this->get_latest_wc_versions();
if ( empty( $latest_wc_versions ) ) {
return '';
}
$latest_wc_version = current( $latest_wc_versions );
$supported_wc_version = $latest_wc_version;
$latest_semver = explode( '.', $latest_wc_version );
$supported_semver = explode( '.', (string) $this->min_wc_semver );
$supported_major = max( 0, (int) $latest_semver[0] - (int) $supported_semver[0] );
$supported_minor = isset( $supported_semver[1] ) ? (int) $supported_semver[1] : 0;
$previous_minor = null;
// Loop known WooCommerce versions from the most recent until we get the oldest supported one.
foreach ( $latest_wc_versions as $older_wc_version ) {
// As we loop through the versions, the latest one before we break the loop will be the minimum supported version.
$supported_wc_version = $older_wc_version;
$older_semver = explode( '.', $older_wc_version );
$older_major = (int) $older_semver[0];
$older_minor = isset( $older_semver[1] ) ? (int) $older_semver[1] : 0;
// if major is ignored, skip; if the minor hasn't changed (patch must be), skip.
if ( $older_major > $supported_major || $older_minor === $previous_minor ) {
continue;
}
// We reached the maximum number of supported minor versions.
if ( $supported_minor <= 0 ) {
break;
}
// Store the previous minor while we loop patch versions, which we ignore.
$previous_minor = $older_minor;
--$supported_minor;
}
return $supported_wc_version;
}
/**
* Check for WooCommerce upgrade recommendation.
*
* @return bool
*/
private function check_wc_upgrade_recommendation() {
// Bail on frontend requests or if there is no definied versions to compare.
if ( ! is_admin() || empty( $this->min_wc_semver ) || ! is_numeric( $this->min_wc_semver ) ) {
return;
}
$current_wc_version = $this->get_wc_version();
$supported_wc_version = $this->get_supported_wc_version();
if ( ! $this->compare_major_version( $current_wc_version, $supported_wc_version, '>=' ) ) {
add_action( 'admin_notices', [ $this, 'make_upgrade_recommendation' ] );
}
return true;
}
/**
* Add notices for WooCommerce not being installed or activated.
*/
public function wc_fail_load() {
$plugin_name = $this->plugin_data['Name'];
if ( $this->is_wc_installed() ) {
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
$activation_url = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . self::WC_PLUGIN_FILE . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . self::WC_PLUGIN_FILE );
$message = sprintf(
/* translators: %1$s - Plugin Name, %2$s - activate WooCommerce link open, %3$s - activate WooCommerce link close. */
esc_html__( '%1$s requires WooCommerce to be activated. Please %2$sactivate WooCommerce%3$s.', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
'<a href="' . esc_url( $activation_url ) . '">',
'</a>'
);
$this->add_admin_notice(
'woocommerce-not-activated',
'error',
$message
);
} else {
if ( ! current_user_can( 'install_plugins' ) ) {
return;
}
$install_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=woocommerce' ), 'install-plugin_woocommerce' );
$message = sprintf(
/* translators: %1$s - Plugin Name, %2$s - install WooCommerce link open, %3$s - install WooCommerce link close. */
esc_html__( '%1$s requires WooCommerce to be installed and activated. Please %2$sinstall WooCommerce%3$s.', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
'<a href="' . esc_url( $install_url ) . '">',
'</a>'
);
$this->add_admin_notice(
'woocommerce-not-installed',
'error',
$message
);
}
}
/**
* Add notices for out of date WooCommerce.
*/
public function wc_out_of_date() {
if ( ! current_user_can( 'update_plugins' ) ) {
return;
}
$plugin_name = $this->plugin_data['Name'];
$wc_version_required = ( 1 === substr_count( $this->plugin_data['RequiresWC'], '.' ) ) ? $this->plugin_data['RequiresWC'] . '.0' : $this->plugin_data['RequiresWC']; // Pad .0 if the min required WC version is not in semvar format.
$message = sprintf(
/* translators: %1$s - Plugin Name, %2$s - minimum WooCommerce version, %3$s - update WooCommerce link open, %4$s - update WooCommerce link close, %5$s - download minimum WooCommerce link open, %6$s - download minimum WooCommerce link close. */
esc_html__( '%1$s requires WooCommerce version %2$s or higher. Please %3$supdate WooCommerce%4$s to the latest version, or %5$sdownload the minimum required version &raquo;%6$s', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
$wc_version_required,
'<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '#update-plugins-table">',
'</a>',
'<a href="' . esc_url( 'https://downloads.wordpress.org/plugin/woocommerce.' . $wc_version_required . '.zip' ) . '">',
'</a>'
);
$this->add_admin_notice(
'woocommerce-out-of-date',
'error',
$message
);
}
/**
* Adds notice for untested WooCommerce.
*/
public function wc_untested() {
$plugin_name = $this->plugin_data['Name'];
$wc_version = $this->get_wc_version();
$plugin_version = $this->plugin_data['Version'];
$message = sprintf(
/* translators: %1$s - Plugin Name, %2$s - Plugin version, %3$s - WooCommerce version number */
esc_html__( '%1$s - version %2$s is untested with WooCommerce %3$s.', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
$plugin_version,
$wc_version
);
$this->add_admin_notice(
'woocommerce-untested',
'warning is-dismissible',
$message
);
}
/**
* Adds notice for WooCommerce upgrade recommendation.
*/
public function make_upgrade_recommendation() {
// Bail if the user does not have the update plugins capability.
if ( ! current_user_can( 'update_plugins' ) ) {
return;
}
$plugin_name = $this->plugin_data['Name'];
$current_wc_version = $this->get_wc_version();
$message = sprintf(
/* translators: Placeholders: %1$s - plugin name, %2$s - WooCommerce version number, %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag */
esc_html__( 'Heads up! %1$s will soon discontinue support for WooCommerce %2$s. Please %3$supdate WooCommerce%4$s to take advantage of the latest updates and features.', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
$current_wc_version,
'<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '#update-plugins-table">',
'</a>'
);
$this->add_admin_notice(
'woocommerce-upgrade-recommendation',
'warning is-dismissible',
$message
);
}
/**
* Run all compatibility checks.
*/
protected function run_checks() {
try {
$this->check_wc_installation_and_activation();
$this->check_wc_version();
$this->check_wc_upgrade_recommendation();
return true;
} catch ( IncompatibleException $e ) {
return false;
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* WordPress Compatibility Check.
*
* @package Automattic/WooCommerce/Grow/Tools
*/
namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Checks;
defined( 'ABSPATH' ) || exit;
/**
* The WooCommerce Compatibility Check class.
*/
class WPCompatibility extends CompatCheck {
/**
* Check if the current WordPress version is compatible.
*/
private function check_wp_version() {
global $wp_version;
if ( $this->compare_major_version( $this->plugin_data['TestedWP'], $wp_version, '<' ) ) {
add_action( 'admin_notices', [ $this, 'wp_not_tested' ] );
}
return true;
}
/**
* Display WordPress version not tested warning.
*/
public function wp_not_tested() {
global $wp_version;
$plugin_name = $this->plugin_data['Name'];
$plugin_version = $this->plugin_data['Version'];
$message = sprintf(
/* translators: %1$s - Plugin Name, %2$s - Plugin version, %3$s - WordPress version number */
esc_html__( '%1$s - %2$s is untested with WordPress %3$s.', 'woogrow-compat-checker' ),
'<strong>' . $plugin_name . '</strong>',
$plugin_version,
$wp_version
);
$this->add_admin_notice(
'wordpress-untested',
'warning is-dismissible',
$message
);
}
/**
* Run all compatibility checks.
*/
protected function run_checks() {
$this->check_wp_version();
return true;
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Compat Checker Exception.
*
* @package Automattic/WooCommerce/Grow/Tools
*/
namespace Automattic\WooCommerce\Grow\Tools\CompatChecker\v0_0_1\Exception;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* The IncompatibleException class.
*/
class IncompatibleException extends Exception {}