You've already forked wc-composable-product
Upgrade to PHPUnit 10, add PHPCS with WPCS compliance, add phpcs CI job
All checks were successful
All checks were successful
- Upgrade PHPUnit 9.6 → 10, update phpunit.xml.dist schema - Add PHPCS 3.13 with WordPress-Extra + PHPCompatibilityWP standards - PHPCBF auto-fix + manual fixes for full WPCS compliance - Add phpcs job to release workflow (parallel with lint) - Pin composer platform to PHP 8.3 to prevent incompatible dep locks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,227 +7,240 @@
|
||||
|
||||
namespace Magdev\WcComposableProduct;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Main plugin class - Singleton pattern
|
||||
*/
|
||||
class Plugin {
|
||||
/**
|
||||
* The single instance of the class
|
||||
*
|
||||
* @var Plugin
|
||||
*/
|
||||
protected static $instance = null;
|
||||
/**
|
||||
* The single instance of the class
|
||||
*
|
||||
* @var Plugin
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Twig environment
|
||||
*
|
||||
* @var \Twig\Environment
|
||||
*/
|
||||
private $twig = null;
|
||||
/**
|
||||
* Twig environment
|
||||
*
|
||||
* @var \Twig\Environment
|
||||
*/
|
||||
private $twig = null;
|
||||
|
||||
/**
|
||||
* Main Plugin Instance
|
||||
*
|
||||
* Ensures only one instance is loaded or can be loaded.
|
||||
*
|
||||
* @return Plugin
|
||||
*/
|
||||
public static function instance() {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
/**
|
||||
* Main Plugin Instance
|
||||
*
|
||||
* Ensures only one instance is loaded or can be loaded.
|
||||
*
|
||||
* @return Plugin
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->init_hooks();
|
||||
$this->init_twig();
|
||||
$this->includes();
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->init_hooks();
|
||||
$this->init_twig();
|
||||
$this->includes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into WordPress and WooCommerce
|
||||
*/
|
||||
private function init_hooks() {
|
||||
// Register product type
|
||||
add_filter('product_type_selector', [$this, 'add_product_type']);
|
||||
add_filter('woocommerce_product_class', [$this, 'product_class'], 10, 2);
|
||||
/**
|
||||
* Hook into WordPress and WooCommerce
|
||||
*/
|
||||
private function init_hooks() {
|
||||
// Register product type
|
||||
add_filter( 'product_type_selector', array( $this, 'add_product_type' ) );
|
||||
add_filter( 'woocommerce_product_class', array( $this, 'product_class' ), 10, 2 );
|
||||
|
||||
// Enqueue scripts and styles
|
||||
add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend_scripts']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
|
||||
// Enqueue scripts and styles
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
|
||||
// Admin settings
|
||||
add_filter('woocommerce_get_settings_pages', [$this, 'add_settings_page']);
|
||||
}
|
||||
// Admin settings
|
||||
add_filter( 'woocommerce_get_settings_pages', array( $this, 'add_settings_page' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Twig template engine
|
||||
*/
|
||||
private function init_twig() {
|
||||
$loader = new \Twig\Loader\FilesystemLoader(WC_COMPOSABLE_PRODUCT_PATH . 'templates');
|
||||
$this->twig = new \Twig\Environment($loader, [
|
||||
'cache' => WC_COMPOSABLE_PRODUCT_PATH . 'cache',
|
||||
'auto_reload' => true,
|
||||
'debug' => defined('WP_DEBUG') && WP_DEBUG,
|
||||
]);
|
||||
/**
|
||||
* Initialize Twig template engine
|
||||
*/
|
||||
private function init_twig() {
|
||||
$loader = new \Twig\Loader\FilesystemLoader( WC_COMPOSABLE_PRODUCT_PATH . 'templates' );
|
||||
$this->twig = new \Twig\Environment(
|
||||
$loader,
|
||||
array(
|
||||
'cache' => WC_COMPOSABLE_PRODUCT_PATH . 'cache',
|
||||
'auto_reload' => true,
|
||||
'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
)
|
||||
);
|
||||
|
||||
// Add WordPress functions to Twig
|
||||
$this->twig->addFunction(new \Twig\TwigFunction('__', function($text) {
|
||||
return __($text, 'wc-composable-product');
|
||||
}));
|
||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_html', 'esc_html'));
|
||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_attr', 'esc_attr'));
|
||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_url', 'esc_url'));
|
||||
$this->twig->addFunction(new \Twig\TwigFunction('wc_price', 'wc_price'));
|
||||
// Add WordPress functions to Twig
|
||||
$this->twig->addFunction(
|
||||
new \Twig\TwigFunction(
|
||||
'__',
|
||||
function ( $text ) {
|
||||
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- dynamic Twig template strings.
|
||||
return __( $text, 'wc-composable-product' );
|
||||
}
|
||||
)
|
||||
);
|
||||
$this->twig->addFunction( new \Twig\TwigFunction( 'esc_html', 'esc_html' ) );
|
||||
$this->twig->addFunction( new \Twig\TwigFunction( 'esc_attr', 'esc_attr' ) );
|
||||
$this->twig->addFunction( new \Twig\TwigFunction( 'esc_url', 'esc_url' ) );
|
||||
$this->twig->addFunction( new \Twig\TwigFunction( 'wc_price', 'wc_price' ) );
|
||||
|
||||
// Add WordPress escaping functions as Twig filters
|
||||
$this->twig->addFilter(new \Twig\TwigFilter('esc_html', 'esc_html'));
|
||||
$this->twig->addFilter(new \Twig\TwigFilter('esc_attr', 'esc_attr'));
|
||||
$this->twig->addFilter(new \Twig\TwigFilter('esc_url', 'esc_url'));
|
||||
}
|
||||
// Add WordPress escaping functions as Twig filters
|
||||
$this->twig->addFilter( new \Twig\TwigFilter( 'esc_html', 'esc_html' ) );
|
||||
$this->twig->addFilter( new \Twig\TwigFilter( 'esc_attr', 'esc_attr' ) );
|
||||
$this->twig->addFilter( new \Twig\TwigFilter( 'esc_url', 'esc_url' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Include required files
|
||||
*/
|
||||
private function includes() {
|
||||
// Note: Settings.php is NOT included here because it extends WC_Settings_Page
|
||||
// which isn't loaded until later. It's included in add_settings_page() instead.
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/ProductData.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/ProductType.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/StockManager.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/CartHandler.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/ProductSelector.php';
|
||||
/**
|
||||
* Include required files
|
||||
*/
|
||||
private function includes() {
|
||||
// Note: Settings.php is NOT included here because it extends WC_Settings_Page
|
||||
// which isn't loaded until later. It's included in add_settings_page() instead.
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/ProductData.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/ProductType.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/StockManager.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/CartHandler.php';
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/ProductSelector.php';
|
||||
|
||||
// Initialize components
|
||||
new Admin\ProductData();
|
||||
new CartHandler();
|
||||
}
|
||||
// Initialize components
|
||||
new Admin\ProductData();
|
||||
new CartHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add composable product type to selector
|
||||
*
|
||||
* @param array $types Product types
|
||||
* @return array
|
||||
*/
|
||||
public function add_product_type($types) {
|
||||
$types['composable'] = __('Composable product', 'wc-composable-product');
|
||||
return $types;
|
||||
}
|
||||
/**
|
||||
* Add composable product type to selector
|
||||
*
|
||||
* @param array $types Product types
|
||||
* @return array
|
||||
*/
|
||||
public function add_product_type( $types ) {
|
||||
$types['composable'] = __( 'Composable product', 'wc-composable-product' );
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use custom product class for composable products
|
||||
*
|
||||
* @param string $classname Product class name
|
||||
* @param string $product_type Product type
|
||||
* @return string
|
||||
*/
|
||||
public function product_class($classname, $product_type) {
|
||||
if ($product_type === 'composable') {
|
||||
$classname = 'Magdev\WcComposableProduct\ProductType';
|
||||
}
|
||||
return $classname;
|
||||
}
|
||||
/**
|
||||
* Use custom product class for composable products
|
||||
*
|
||||
* @param string $classname Product class name
|
||||
* @param string $product_type Product type
|
||||
* @return string
|
||||
*/
|
||||
public function product_class( $classname, $product_type ) {
|
||||
if ( 'composable' === $product_type ) {
|
||||
$classname = 'Magdev\WcComposableProduct\ProductType';
|
||||
}
|
||||
return $classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend scripts and styles
|
||||
*/
|
||||
public function enqueue_frontend_scripts() {
|
||||
if (is_product()) {
|
||||
wp_enqueue_style(
|
||||
'wc-composable-product',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/css/frontend.css',
|
||||
[],
|
||||
WC_COMPOSABLE_PRODUCT_VERSION
|
||||
);
|
||||
/**
|
||||
* Enqueue frontend scripts and styles
|
||||
*/
|
||||
public function enqueue_frontend_scripts() {
|
||||
if ( is_product() ) {
|
||||
wp_enqueue_style(
|
||||
'wc-composable-product',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/css/frontend.css',
|
||||
array(),
|
||||
WC_COMPOSABLE_PRODUCT_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wc-composable-product',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/js/frontend.js',
|
||||
['jquery'],
|
||||
WC_COMPOSABLE_PRODUCT_VERSION,
|
||||
true
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'wc-composable-product',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/js/frontend.js',
|
||||
array( 'jquery' ),
|
||||
WC_COMPOSABLE_PRODUCT_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('wc-composable-product', 'wcComposableProduct', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('wc_composable_product_nonce'),
|
||||
'i18n' => [
|
||||
'select_items' => __('Please select items', 'wc-composable-product'),
|
||||
'max_items' => __('Maximum items selected', 'wc-composable-product'),
|
||||
'min_items' => __('Please select at least one item', 'wc-composable-product'),
|
||||
],
|
||||
'price_format' => [
|
||||
'currency_symbol' => get_woocommerce_currency_symbol(),
|
||||
'decimal_separator' => wc_get_price_decimal_separator(),
|
||||
'thousand_separator' => wc_get_price_thousand_separator(),
|
||||
'decimals' => wc_get_price_decimals(),
|
||||
'price_format' => get_woocommerce_price_format(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
wp_localize_script(
|
||||
'wc-composable-product',
|
||||
'wcComposableProduct',
|
||||
array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'wc_composable_product_nonce' ),
|
||||
'i18n' => array(
|
||||
'select_items' => __( 'Please select items', 'wc-composable-product' ),
|
||||
'max_items' => __( 'Maximum items selected', 'wc-composable-product' ),
|
||||
'min_items' => __( 'Please select at least one item', 'wc-composable-product' ),
|
||||
),
|
||||
'price_format' => array(
|
||||
'currency_symbol' => get_woocommerce_currency_symbol(),
|
||||
'decimal_separator' => wc_get_price_decimal_separator(),
|
||||
'thousand_separator' => wc_get_price_thousand_separator(),
|
||||
'decimals' => wc_get_price_decimals(),
|
||||
'price_format' => get_woocommerce_price_format(),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
public function enqueue_admin_scripts($hook) {
|
||||
if ('post.php' === $hook || 'post-new.php' === $hook) {
|
||||
global $post_type;
|
||||
if ('product' === $post_type) {
|
||||
wp_enqueue_style(
|
||||
'wc-composable-product-admin',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/css/admin.css',
|
||||
[],
|
||||
WC_COMPOSABLE_PRODUCT_VERSION
|
||||
);
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
public function enqueue_admin_scripts( $hook ) {
|
||||
if ( 'post.php' === $hook || 'post-new.php' === $hook ) {
|
||||
global $post_type;
|
||||
if ( 'product' === $post_type ) {
|
||||
wp_enqueue_style(
|
||||
'wc-composable-product-admin',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/css/admin.css',
|
||||
array(),
|
||||
WC_COMPOSABLE_PRODUCT_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wc-composable-product-admin',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/js/admin.js',
|
||||
['jquery', 'wc-admin-product-meta-boxes'],
|
||||
WC_COMPOSABLE_PRODUCT_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
wp_enqueue_script(
|
||||
'wc-composable-product-admin',
|
||||
WC_COMPOSABLE_PRODUCT_URL . 'assets/js/admin.js',
|
||||
array( 'jquery', 'wc-admin-product-meta-boxes' ),
|
||||
WC_COMPOSABLE_PRODUCT_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add settings page to WooCommerce
|
||||
*
|
||||
* @param array $settings WooCommerce settings pages
|
||||
* @return array
|
||||
*/
|
||||
public function add_settings_page($settings) {
|
||||
// Include Settings.php here, when WC_Settings_Page is guaranteed to be loaded
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/Settings.php';
|
||||
$settings[] = new Admin\Settings();
|
||||
return $settings;
|
||||
}
|
||||
/**
|
||||
* Add settings page to WooCommerce
|
||||
*
|
||||
* @param array $settings WooCommerce settings pages
|
||||
* @return array
|
||||
*/
|
||||
public function add_settings_page( $settings ) {
|
||||
// Include Settings.php here, when WC_Settings_Page is guaranteed to be loaded
|
||||
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/Settings.php';
|
||||
$settings[] = new Admin\Settings();
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Twig environment
|
||||
*
|
||||
* @return \Twig\Environment
|
||||
*/
|
||||
public function get_twig() {
|
||||
return $this->twig;
|
||||
}
|
||||
/**
|
||||
* Get Twig environment
|
||||
*
|
||||
* @return \Twig\Environment
|
||||
*/
|
||||
public function get_twig() {
|
||||
return $this->twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a Twig template
|
||||
*
|
||||
* @param string $template Template name
|
||||
* @param array $context Template variables
|
||||
* @return string
|
||||
*/
|
||||
public function render_template($template, $context = []) {
|
||||
return $this->twig->render($template, $context);
|
||||
}
|
||||
/**
|
||||
* Render a Twig template
|
||||
*
|
||||
* @param string $template Template name
|
||||
* @param array $context Template variables
|
||||
* @return string
|
||||
*/
|
||||
public function render_template( $template, $context = array() ) {
|
||||
return $this->twig->render( $template, $context );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user