Files
roi-theme/wp-content/plugins/wp-debug/inc/asset-tracker.php

370 lines
10 KiB
PHP

<?php
/**
* Asset Tracker Module
*
* Tracks all enqueued CSS and JS assets, detects duplicates,
* missing versions, and calculates file sizes.
*
* @package WP_Debug
* @since 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Class WP_Debug_Asset_Tracker
*
* Tracks and analyzes enqueued CSS and JS assets.
*/
class WP_Debug_Asset_Tracker {
/**
* Enqueued assets storage
*
* @var array
*/
private static $assets_enqueued = array(
'styles' => array(),
'scripts' => array()
);
/**
* Initialize the asset tracker
*
* @return void
*/
public static function init() {
// Hook into wp_enqueue_scripts with high priority to catch all assets
add_action('wp_enqueue_scripts', array(__CLASS__, 'track_assets'), PHP_INT_MAX);
// Hook into admin_enqueue_scripts for admin assets
add_action('admin_enqueue_scripts', array(__CLASS__, 'track_assets'), PHP_INT_MAX);
// Record assets on shutdown
add_action('shutdown', array(__CLASS__, 'record_assets'), 1);
if (class_exists('WP_Debug_Logger')) {
WP_Debug_Logger::debug('Asset Tracker initialized');
}
}
/**
* Track enqueued assets
*
* @return void
*/
public static function track_assets() {
global $wp_styles, $wp_scripts;
// Track styles
if (isset($wp_styles) && is_object($wp_styles)) {
self::track_styles($wp_styles);
}
// Track scripts
if (isset($wp_scripts) && is_object($wp_scripts)) {
self::track_scripts($wp_scripts);
}
}
/**
* Track stylesheet assets
*
* @param WP_Styles $wp_styles WordPress styles object
* @return void
*/
private static function track_styles($wp_styles) {
if (empty($wp_styles->queue)) {
return;
}
foreach ($wp_styles->queue as $handle) {
if (!isset($wp_styles->registered[$handle])) {
continue;
}
$style = $wp_styles->registered[$handle];
$asset_data = array(
'handle' => $handle,
'src' => $style->src,
'dependencies' => $style->deps,
'version' => $style->ver,
'media' => $style->args,
'type' => 'style',
'size' => self::get_file_size($style->src),
'local' => self::is_local_asset($style->src),
'has_version' => !empty($style->ver),
'extra' => isset($style->extra) ? $style->extra : array()
);
self::$assets_enqueued['styles'][$handle] = $asset_data;
}
}
/**
* Track script assets
*
* @param WP_Scripts $wp_scripts WordPress scripts object
* @return void
*/
private static function track_scripts($wp_scripts) {
if (empty($wp_scripts->queue)) {
return;
}
foreach ($wp_scripts->queue as $handle) {
if (!isset($wp_scripts->registered[$handle])) {
continue;
}
$script = $wp_scripts->registered[$handle];
$asset_data = array(
'handle' => $handle,
'src' => $script->src,
'dependencies' => $script->deps,
'version' => $script->ver,
'in_footer' => isset($script->extra['group']) && $script->extra['group'] === 1,
'type' => 'script',
'size' => self::get_file_size($script->src),
'local' => self::is_local_asset($script->src),
'has_version' => !empty($script->ver),
'extra' => isset($script->extra) ? $script->extra : array()
);
self::$assets_enqueued['scripts'][$handle] = $asset_data;
}
}
/**
* Check if asset is local
*
* @param string $src Asset source URL
* @return bool
*/
private static function is_local_asset($src) {
if (empty($src)) {
return false;
}
$home_url = home_url();
$site_url = site_url();
// Check if it starts with home or site URL
if (strpos($src, $home_url) === 0 || strpos($src, $site_url) === 0) {
return true;
}
// Check if it starts with relative path
if (strpos($src, '/') === 0 && strpos($src, '//') !== 0) {
return true;
}
return false;
}
/**
* Get file size for local assets
*
* @param string $src Asset source URL
* @return int|null File size in bytes or null if not available
*/
private static function get_file_size($src) {
if (empty($src) || !self::is_local_asset($src)) {
return null;
}
// Convert URL to file path
$file_path = self::url_to_path($src);
if (!$file_path || !file_exists($file_path)) {
return null;
}
$size = @filesize($file_path);
return $size !== false ? $size : null;
}
/**
* Convert asset URL to file system path
*
* @param string $src Asset source URL
* @return string|false File path or false if not convertible
*/
private static function url_to_path($src) {
// Remove query string
$src = strtok($src, '?');
// Handle absolute URLs
$home_url = home_url();
$site_url = site_url();
if (strpos($src, $home_url) === 0) {
$relative_path = str_replace($home_url, '', $src);
return ABSPATH . ltrim($relative_path, '/');
}
if (strpos($src, $site_url) === 0) {
$relative_path = str_replace($site_url, '', $src);
return ABSPATH . ltrim($relative_path, '/');
}
// Handle relative URLs
if (strpos($src, '/') === 0 && strpos($src, '//') !== 0) {
return ABSPATH . ltrim($src, '/');
}
return false;
}
/**
* Get all tracked assets
*
* @return array All tracked assets
*/
public static function get_assets() {
return self::$assets_enqueued;
}
/**
* Detect duplicate assets (same src, different handles)
*
* @return array Detected duplicates
*/
public static function get_duplicates() {
$duplicates = array();
$src_map = array();
// Check styles
foreach (self::$assets_enqueued['styles'] as $handle => $asset) {
if (empty($asset['src'])) {
continue;
}
// Normalize source (remove query strings for comparison)
$normalized_src = strtok($asset['src'], '?');
if (!isset($src_map['styles'][$normalized_src])) {
$src_map['styles'][$normalized_src] = array();
}
$src_map['styles'][$normalized_src][] = $handle;
}
// Check scripts
foreach (self::$assets_enqueued['scripts'] as $handle => $asset) {
if (empty($asset['src'])) {
continue;
}
// Normalize source (remove query strings for comparison)
$normalized_src = strtok($asset['src'], '?');
if (!isset($src_map['scripts'][$normalized_src])) {
$src_map['scripts'][$normalized_src] = array();
}
$src_map['scripts'][$normalized_src][] = $handle;
}
// Find duplicates
foreach ($src_map as $type => $sources) {
foreach ($sources as $src => $handles) {
if (count($handles) > 1) {
$duplicates[] = array(
'type' => $type,
'src' => $src,
'handles' => $handles,
'count' => count($handles)
);
}
}
}
return $duplicates;
}
/**
* Get statistics about tracked assets
*
* @return array Asset statistics
*/
public static function get_statistics() {
$total_styles = count(self::$assets_enqueued['styles']);
$total_scripts = count(self::$assets_enqueued['scripts']);
$total_size = 0;
$local_count = 0;
$external_count = 0;
// Calculate statistics for styles
foreach (self::$assets_enqueued['styles'] as $asset) {
if ($asset['size'] !== null) {
$total_size += $asset['size'];
}
if ($asset['local']) {
$local_count++;
} else {
$external_count++;
}
}
// Calculate statistics for scripts
foreach (self::$assets_enqueued['scripts'] as $asset) {
if ($asset['size'] !== null) {
$total_size += $asset['size'];
}
if ($asset['local']) {
$local_count++;
} else {
$external_count++;
}
}
return array(
'total_assets' => $total_styles + $total_scripts,
'total_styles' => $total_styles,
'total_scripts' => $total_scripts,
'total_size' => $total_size,
'total_size_formatted' => size_format($total_size),
'local_assets' => $local_count,
'external_assets' => $external_count,
'duplicates_count' => count(self::get_duplicates()),
);
}
/**
* Record assets and save to transient
*
* @return void
*/
public static function record_assets() {
if (empty(self::$assets_enqueued['styles']) && empty(self::$assets_enqueued['scripts'])) {
if (class_exists('WP_Debug_Logger')) {
WP_Debug_Logger::debug('No assets to record');
}
return;
}
$assets_data = array(
'assets' => self::$assets_enqueued,
'duplicates' => self::get_duplicates(),
'statistics' => self::get_statistics(),
'timestamp' => current_time('timestamp'),
);
// Save to transient (expires in 1 hour)
set_transient('wp_debug_assets_data', $assets_data, HOUR_IN_SECONDS);
if (class_exists('WP_Debug_Logger')) {
WP_Debug_Logger::debug('Asset Tracker recorded assets', array(
'total_styles' => count(self::$assets_enqueued['styles']),
'total_scripts' => count(self::$assets_enqueued['scripts']),
'duplicates' => count($assets_data['duplicates']),
));
}
}
}