- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
510 lines
16 KiB
PHP
510 lines
16 KiB
PHP
/**
|
|
* Descomplicar® Crescimento Digital
|
|
* https://descomplicar.pt
|
|
*/
|
|
|
|
<?php
|
|
/**
|
|
* Asset Optimizer for Care Booking Block plugin
|
|
* Provides enterprise-grade asset minification and optimization
|
|
*
|
|
* @package CareBookingBlock
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Asset Optimizer class for maximum performance
|
|
*/
|
|
class Care_Booking_Asset_Optimizer
|
|
{
|
|
/**
|
|
* Cache key for asset versions
|
|
*/
|
|
const ASSET_VERSION_KEY = 'care_booking_asset_versions';
|
|
|
|
/**
|
|
* Cache duration for assets (24 hours)
|
|
*/
|
|
const ASSET_CACHE_DURATION = DAY_IN_SECONDS;
|
|
|
|
/**
|
|
* Initialize asset optimization
|
|
*/
|
|
public static function init()
|
|
{
|
|
// Enqueue optimized assets
|
|
add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_optimized_frontend_assets'], 5);
|
|
add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_optimized_admin_assets'], 5);
|
|
|
|
// Asset optimization hooks
|
|
add_filter('script_loader_src', [__CLASS__, 'optimize_script_src'], 10, 2);
|
|
add_filter('style_loader_src', [__CLASS__, 'optimize_style_src'], 10, 2);
|
|
|
|
// Preload critical assets
|
|
add_action('wp_head', [__CLASS__, 'preload_critical_assets'], 1);
|
|
|
|
// Asset combination and minification
|
|
add_action('wp_footer', [__CLASS__, 'output_combined_assets'], 25);
|
|
}
|
|
|
|
/**
|
|
* Enqueue optimized frontend assets
|
|
*/
|
|
public static function enqueue_optimized_frontend_assets()
|
|
{
|
|
if (is_admin() || !self::should_load_frontend_assets()) {
|
|
return;
|
|
}
|
|
|
|
$version = self::get_asset_version();
|
|
$min_suffix = self::get_min_suffix();
|
|
|
|
// Optimized CSS with intelligent loading
|
|
wp_enqueue_style(
|
|
'care-booking-frontend',
|
|
CARE_BOOKING_BLOCK_PLUGIN_URL . "public/css/frontend{$min_suffix}.css",
|
|
[],
|
|
$version,
|
|
'all'
|
|
);
|
|
|
|
// Optimized JavaScript with async loading for non-critical
|
|
wp_enqueue_script(
|
|
'care-booking-frontend',
|
|
CARE_BOOKING_BLOCK_PLUGIN_URL . "public/js/frontend{$min_suffix}.js",
|
|
['jquery'],
|
|
$version,
|
|
true // Load in footer
|
|
);
|
|
|
|
// Add async/defer attributes for better performance
|
|
add_filter('script_loader_tag', [__CLASS__, 'add_script_attributes'], 10, 3);
|
|
}
|
|
|
|
/**
|
|
* Enqueue optimized admin assets
|
|
*/
|
|
public static function enqueue_optimized_admin_assets($hook)
|
|
{
|
|
// Only load on Care Booking admin pages
|
|
if (!self::is_care_booking_admin_page($hook)) {
|
|
return;
|
|
}
|
|
|
|
$version = self::get_asset_version();
|
|
$min_suffix = self::get_min_suffix();
|
|
|
|
// Combined and minified admin CSS
|
|
wp_enqueue_style(
|
|
'care-booking-admin',
|
|
CARE_BOOKING_BLOCK_PLUGIN_URL . "admin/css/admin-style{$min_suffix}.css",
|
|
[],
|
|
$version,
|
|
'all'
|
|
);
|
|
|
|
// Combined and minified admin JavaScript
|
|
wp_enqueue_script(
|
|
'care-booking-admin',
|
|
CARE_BOOKING_BLOCK_PLUGIN_URL . "admin/js/admin-script{$min_suffix}.js",
|
|
['jquery', 'wp-util'],
|
|
$version,
|
|
true
|
|
);
|
|
|
|
// Optimized localization with minimal data
|
|
$localize_data = self::get_optimized_admin_localize_data();
|
|
wp_localize_script('care-booking-admin', 'careBookingAjax', $localize_data);
|
|
}
|
|
|
|
/**
|
|
* Get optimized admin localization data
|
|
*
|
|
* @return array Minimal required data
|
|
*/
|
|
private static function get_optimized_admin_localize_data()
|
|
{
|
|
return [
|
|
'ajaxurl' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('care_booking_admin'),
|
|
'strings' => [
|
|
'error' => __('An error occurred. Please try again.', 'care-booking-block'),
|
|
'success_update' => __('Updated successfully.', 'care-booking-block'),
|
|
'success_bulk' => __('Bulk operation completed.', 'care-booking-block'),
|
|
'confirm_bulk' => __('Are you sure you want to update selected items?', 'care-booking-block')
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Add async/defer attributes to scripts for better performance
|
|
*
|
|
* @param string $tag Script tag
|
|
* @param string $handle Script handle
|
|
* @param string $src Script source
|
|
* @return string Modified script tag
|
|
*/
|
|
public static function add_script_attributes($tag, $handle, $src)
|
|
{
|
|
// Add async to non-critical frontend scripts
|
|
if ($handle === 'care-booking-frontend' && !is_admin()) {
|
|
// Only add async if jQuery is already loaded or loading
|
|
if (wp_script_is('jquery', 'done') || wp_script_is('jquery', 'to_do')) {
|
|
$tag = str_replace(' src', ' async src', $tag);
|
|
}
|
|
}
|
|
|
|
return $tag;
|
|
}
|
|
|
|
/**
|
|
* Preload critical assets for better performance
|
|
*/
|
|
public static function preload_critical_assets()
|
|
{
|
|
if (!self::should_load_frontend_assets()) {
|
|
return;
|
|
}
|
|
|
|
$version = self::get_asset_version();
|
|
$min_suffix = self::get_min_suffix();
|
|
|
|
// Preload critical CSS
|
|
$css_url = CARE_BOOKING_BLOCK_PLUGIN_URL . "public/css/frontend{$min_suffix}.css?ver={$version}";
|
|
echo "<link rel='preload' href='{$css_url}' as='style' onload=\"this.onload=null;this.rel='stylesheet'\">\n";
|
|
|
|
// Fallback for browsers that don't support preload
|
|
echo "<noscript><link rel='stylesheet' href='{$css_url}'></noscript>\n";
|
|
}
|
|
|
|
/**
|
|
* Optimize script source URLs
|
|
*
|
|
* @param string $src Script source
|
|
* @param string $handle Script handle
|
|
* @return string Optimized source
|
|
*/
|
|
public static function optimize_script_src($src, $handle)
|
|
{
|
|
// Add cache busting and CDN optimization for Care Booking scripts
|
|
if (strpos($handle, 'care-booking') === 0) {
|
|
// Add integrity checking for security
|
|
if (!is_admin() && defined('CARE_BOOKING_ENABLE_SRI') && CARE_BOOKING_ENABLE_SRI) {
|
|
add_filter('script_loader_tag', function($tag, $h, $s) use ($handle, $src) {
|
|
if ($h === $handle) {
|
|
$integrity = self::get_file_integrity($src);
|
|
if ($integrity) {
|
|
$tag = str_replace('></script>', " integrity='{$integrity}' crossorigin='anonymous'></script>", $tag);
|
|
}
|
|
}
|
|
return $tag;
|
|
}, 10, 3);
|
|
}
|
|
}
|
|
|
|
return $src;
|
|
}
|
|
|
|
/**
|
|
* Optimize style source URLs
|
|
*
|
|
* @param string $src Style source
|
|
* @param string $handle Style handle
|
|
* @return string Optimized source
|
|
*/
|
|
public static function optimize_style_src($src, $handle)
|
|
{
|
|
// Add performance optimizations for Care Booking styles
|
|
if (strpos($handle, 'care-booking') === 0) {
|
|
// Ensure proper media attribute for optimal loading
|
|
add_filter('style_loader_tag', function($html, $h, $href, $media) use ($handle) {
|
|
if ($h === $handle && $media === 'all') {
|
|
// Add performance attributes
|
|
$html = str_replace("media='all'", "media='all' data-optimized='true'", $html);
|
|
}
|
|
return $html;
|
|
}, 10, 4);
|
|
}
|
|
|
|
return $src;
|
|
}
|
|
|
|
/**
|
|
* Get asset version with intelligent cache busting
|
|
*
|
|
* @return string Asset version
|
|
*/
|
|
private static function get_asset_version()
|
|
{
|
|
$versions = get_transient(self::ASSET_VERSION_KEY);
|
|
|
|
if ($versions === false) {
|
|
$versions = self::generate_asset_versions();
|
|
set_transient(self::ASSET_VERSION_KEY, $versions, self::ASSET_CACHE_DURATION);
|
|
}
|
|
|
|
return $versions['global'] ?? CARE_BOOKING_BLOCK_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Generate asset versions based on file modification times
|
|
*
|
|
* @return array Asset versions
|
|
*/
|
|
private static function generate_asset_versions()
|
|
{
|
|
$versions = ['global' => CARE_BOOKING_BLOCK_VERSION];
|
|
|
|
$asset_files = [
|
|
'frontend_css' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.css',
|
|
'frontend_js' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.js',
|
|
'admin_css' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.css',
|
|
'admin_js' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.js'
|
|
];
|
|
|
|
foreach ($asset_files as $key => $file) {
|
|
if (file_exists($file)) {
|
|
$versions[$key] = filemtime($file);
|
|
}
|
|
}
|
|
|
|
// Generate global version from all file versions
|
|
$versions['global'] = md5(serialize($versions));
|
|
|
|
return $versions;
|
|
}
|
|
|
|
/**
|
|
* Get minification suffix based on environment
|
|
*
|
|
* @return string Empty string or '.min'
|
|
*/
|
|
private static function get_min_suffix()
|
|
{
|
|
// Use minified assets in production, original in development
|
|
return (defined('WP_DEBUG') && WP_DEBUG) ? '' : '.min';
|
|
}
|
|
|
|
/**
|
|
* Check if frontend assets should be loaded
|
|
*
|
|
* @return bool True if should load
|
|
*/
|
|
private static function should_load_frontend_assets()
|
|
{
|
|
global $post;
|
|
|
|
// Load on pages with KiviCare content
|
|
if ($post && (
|
|
has_shortcode($post->post_content, 'kivicare') ||
|
|
has_block('kivicare/booking', $post->post_content)
|
|
)) {
|
|
return true;
|
|
}
|
|
|
|
// Load on specific templates
|
|
$template = get_page_template_slug();
|
|
if (in_array($template, ['page-booking.php', 'page-appointment.php'])) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if current admin page is Care Booking related
|
|
*
|
|
* @param string $hook Admin page hook
|
|
* @return bool True if Care Booking admin page
|
|
*/
|
|
private static function is_care_booking_admin_page($hook)
|
|
{
|
|
$care_booking_pages = [
|
|
'tools_page_care-booking-control',
|
|
'admin_page_care-booking-settings'
|
|
];
|
|
|
|
return in_array($hook, $care_booking_pages);
|
|
}
|
|
|
|
/**
|
|
* Get file integrity hash for Subresource Integrity
|
|
*
|
|
* @param string $file_url File URL
|
|
* @return string|null Integrity hash
|
|
*/
|
|
private static function get_file_integrity($file_url)
|
|
{
|
|
// Convert URL to file path
|
|
$file_path = str_replace(
|
|
CARE_BOOKING_BLOCK_PLUGIN_URL,
|
|
CARE_BOOKING_BLOCK_PLUGIN_DIR,
|
|
$file_url
|
|
);
|
|
|
|
if (file_exists($file_path)) {
|
|
$hash = hash('sha384', file_get_contents($file_path), true);
|
|
return 'sha384-' . base64_encode($hash);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Output combined assets for maximum performance
|
|
*/
|
|
public static function output_combined_assets()
|
|
{
|
|
// Only combine assets if not in debug mode
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
return;
|
|
}
|
|
|
|
// This would combine multiple CSS/JS files into single requests
|
|
// For now, we rely on the individual optimizations above
|
|
self::output_performance_markers();
|
|
}
|
|
|
|
/**
|
|
* Output performance markers for monitoring
|
|
*/
|
|
private static function output_performance_markers()
|
|
{
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
echo "\n<!-- Care Booking Block: Assets optimized for performance -->\n";
|
|
|
|
$memory = memory_get_usage();
|
|
$peak_memory = memory_get_peak_usage();
|
|
|
|
echo "<!-- Memory Usage: " . size_format($memory) . " | Peak: " . size_format($peak_memory) . " -->\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate minified CSS from source files
|
|
*
|
|
* @param string $source_file Source CSS file
|
|
* @param string $output_file Output minified file
|
|
* @return bool Success status
|
|
*/
|
|
public static function generate_minified_css($source_file, $output_file)
|
|
{
|
|
if (!file_exists($source_file)) {
|
|
return false;
|
|
}
|
|
|
|
$css = file_get_contents($source_file);
|
|
$minified_css = self::minify_css($css);
|
|
|
|
return file_put_contents($output_file, $minified_css) !== false;
|
|
}
|
|
|
|
/**
|
|
* Generate minified JavaScript from source files
|
|
*
|
|
* @param string $source_file Source JS file
|
|
* @param string $output_file Output minified file
|
|
* @return bool Success status
|
|
*/
|
|
public static function generate_minified_js($source_file, $output_file)
|
|
{
|
|
if (!file_exists($source_file)) {
|
|
return false;
|
|
}
|
|
|
|
$js = file_get_contents($source_file);
|
|
$minified_js = self::minify_js($js);
|
|
|
|
return file_put_contents($output_file, $minified_js) !== false;
|
|
}
|
|
|
|
/**
|
|
* Minify CSS content
|
|
*
|
|
* @param string $css CSS content
|
|
* @return string Minified CSS
|
|
*/
|
|
public static function minify_css($css)
|
|
{
|
|
// Remove comments
|
|
$css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
|
|
|
|
// Remove whitespace
|
|
$css = str_replace(["\r\n", "\r", "\n", "\t"], '', $css);
|
|
|
|
// Remove extra spaces
|
|
$css = preg_replace('/\s+/', ' ', $css);
|
|
|
|
// Remove spaces around specific characters
|
|
$css = str_replace(['; ', ' {', '{ ', ' }', '} ', ': ', ', ', ' ,'], [';', '{', '{', '}', '}', ':', ',', ','], $css);
|
|
|
|
// Remove trailing semicolon before }
|
|
$css = str_replace(';}', '}', $css);
|
|
|
|
return trim($css);
|
|
}
|
|
|
|
/**
|
|
* Basic JavaScript minification
|
|
*
|
|
* @param string $js JavaScript content
|
|
* @return string Minified JavaScript
|
|
*/
|
|
public static function minify_js($js)
|
|
{
|
|
// Basic minification - remove comments and extra whitespace
|
|
// Note: For production, consider using a proper JS minifier
|
|
|
|
// Remove single-line comments (but preserve URLs)
|
|
$js = preg_replace('#(?<!:)//.*#', '', $js);
|
|
|
|
// Remove multi-line comments
|
|
$js = preg_replace('#/\*.*?\*/#s', '', $js);
|
|
|
|
// Remove extra whitespace
|
|
$js = preg_replace('/\s+/', ' ', $js);
|
|
|
|
// Remove spaces around operators and punctuation
|
|
$js = str_replace([' = ', ' + ', ' - ', ' * ', ' / ', ' { ', ' } ', ' ( ', ' ) ', ' [ ', ' ] ', ' ; ', ' , '],
|
|
['=', '+', '-', '*', '/', '{', '}', '(', ')', '[', ']', ';', ','], $js);
|
|
|
|
return trim($js);
|
|
}
|
|
|
|
/**
|
|
* Build minified assets for production
|
|
*/
|
|
public static function build_production_assets()
|
|
{
|
|
$assets = [
|
|
'admin-style.css' => 'admin/css/admin-style.min.css',
|
|
'admin-script.js' => 'admin/js/admin-script.min.js',
|
|
'frontend.css' => 'public/css/frontend.min.css',
|
|
'frontend.js' => 'public/js/frontend.min.js'
|
|
];
|
|
|
|
$results = [];
|
|
|
|
foreach ($assets as $source => $target) {
|
|
$source_path = CARE_BOOKING_BLOCK_PLUGIN_DIR . str_replace('.min', '', $target);
|
|
$target_path = CARE_BOOKING_BLOCK_PLUGIN_DIR . $target;
|
|
|
|
$extension = pathinfo($source, PATHINFO_EXTENSION);
|
|
|
|
if ($extension === 'css') {
|
|
$results[$source] = self::generate_minified_css($source_path, $target_path);
|
|
} elseif ($extension === 'js') {
|
|
$results[$source] = self::generate_minified_js($source_path, $target_path);
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
}
|
|
|
|
// Initialize asset optimizer
|
|
Care_Booking_Asset_Optimizer::init(); |