124 lines
3.7 KiB
PHP
124 lines
3.7 KiB
PHP
<?php
|
||
/**
|
||
* Uninstall script for Maple Local Fonts.
|
||
*
|
||
* This file is executed when the plugin is deleted from WordPress.
|
||
* It removes all fonts imported by this plugin and cleans up any data.
|
||
*
|
||
* @package Maple_Local_Fonts
|
||
*/
|
||
|
||
// If uninstall not called from WordPress, exit.
|
||
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Clean up all plugin data on uninstall.
|
||
*
|
||
* Uses batched processing to prevent memory exhaustion on sites with many fonts.
|
||
*/
|
||
function mlf_uninstall() {
|
||
$font_dir = wp_get_font_dir();
|
||
$batch_size = 50; // Process 50 fonts at a time to prevent memory issues
|
||
$processed = 0;
|
||
$max_iterations = 100; // Safety limit: max 5000 fonts (100 × 50)
|
||
|
||
// Process fonts in batches
|
||
for ($i = 0; $i < $max_iterations; $i++) {
|
||
$fonts = get_posts([
|
||
'post_type' => 'wp_font_family',
|
||
'posts_per_page' => $batch_size,
|
||
'post_status' => 'any',
|
||
'meta_key' => '_mlf_imported',
|
||
'meta_value' => '1',
|
||
'fields' => 'ids', // Only get IDs for memory efficiency
|
||
]);
|
||
|
||
// No more fonts to process
|
||
if (empty($fonts)) {
|
||
break;
|
||
}
|
||
|
||
// Get all font faces for this batch in a single query
|
||
$all_faces = get_posts([
|
||
'post_type' => 'wp_font_face',
|
||
'posts_per_page' => $batch_size * 20, // Max ~20 faces per font
|
||
'post_status' => 'any',
|
||
'post_parent__in' => $fonts,
|
||
]);
|
||
|
||
// Delete font face files and posts
|
||
foreach ($all_faces as $face) {
|
||
$settings = json_decode($face->post_content, true);
|
||
|
||
if (isset($settings['src'])) {
|
||
// Convert file:. URL to path
|
||
$src = $settings['src'];
|
||
$src = str_replace('file:./', '', $src);
|
||
$file_path = trailingslashit($font_dir['path']) . basename($src);
|
||
|
||
// Validate path and extension before deletion
|
||
if (mlf_uninstall_validate_font_path($file_path, $font_dir)
|
||
&& pathinfo($file_path, PATHINFO_EXTENSION) === 'woff2'
|
||
&& file_exists($file_path)) {
|
||
wp_delete_file($file_path);
|
||
}
|
||
}
|
||
|
||
wp_delete_post($face->ID, true);
|
||
}
|
||
|
||
// Delete family posts
|
||
foreach ($fonts as $font_id) {
|
||
wp_delete_post($font_id, true);
|
||
}
|
||
|
||
$fonts_count = count($fonts);
|
||
$processed += $fonts_count;
|
||
|
||
// Free memory
|
||
unset($fonts, $all_faces);
|
||
|
||
// If we got fewer than batch_size, we're done
|
||
if ($fonts_count < $batch_size) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Clear caches
|
||
delete_transient('wp_font_library_fonts');
|
||
delete_transient('mlf_imported_fonts_list');
|
||
}
|
||
|
||
/**
|
||
* Validate that a path is within the WordPress fonts directory.
|
||
*
|
||
* @param string $path Full path to validate.
|
||
* @param array $font_dir Font directory info.
|
||
* @return bool True if path is safe, false otherwise.
|
||
*/
|
||
function mlf_uninstall_validate_font_path($path, $font_dir) {
|
||
$fonts_path = wp_normalize_path(trailingslashit($font_dir['path']));
|
||
|
||
// Resolve to real path (handles ../ etc)
|
||
$real_path = realpath($path);
|
||
|
||
// If realpath fails, file doesn't exist yet - validate the directory
|
||
if ($real_path === false) {
|
||
$dir = dirname($path);
|
||
$real_dir = realpath($dir);
|
||
if ($real_dir === false) {
|
||
return false;
|
||
}
|
||
$real_path = wp_normalize_path($real_dir . '/' . basename($path));
|
||
} else {
|
||
$real_path = wp_normalize_path($real_path);
|
||
}
|
||
|
||
// Must be within fonts directory
|
||
return strpos($real_path, $fonts_path) === 0;
|
||
}
|
||
|
||
// Run uninstall
|
||
mlf_uninstall();
|