chore: add spec-kit and standardize signatures
- 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>
This commit is contained in:
510
care-booking-block/includes/class-asset-optimizer.php
Normal file
510
care-booking-block/includes/class-asset-optimizer.php
Normal file
@@ -0,0 +1,510 @@
|
||||
/**
|
||||
* 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();
|
||||
Reference in New Issue
Block a user