File "utils.php"
Full Path: /home/fresvfqn/waterdamagerestorationandrepairsmithtown.com/wp-content/plugins/surerank/inc/import-export/utils.php
File size: 14.51 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Import Export Utilities
*
* Utility functions for import/export functionality.
*
* @package SureRank\Inc\Import_Export
* @since 1.2.0
*/
namespace SureRank\Inc\Import_Export;
use SureRank\Inc\Functions\Sanitize;
use SureRank\Inc\Functions\Validate;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Class Utils
*
* Utility functions for import/export operations.
*/
class Utils {
/**
* Image setting keys that need special handling during import.
*/
private const IMAGE_KEYS = [
'fallback_image',
'home_page_facebook_image_url',
'home_page_twitter_image_url',
];
/**
* Create success response
*
* @param mixed $data Success data.
* @param string $message Success message.
* @return array<string, mixed> Success response array.
*/
public static function success_response( $data = [], $message = '' ) {
$response = [
'success' => true,
'data' => $data,
];
if ( ! empty( $message ) ) {
$response['message'] = $message;
}
return $response;
}
/**
* Create error response
*
* @param string $message Error message.
* @param array<int, string> $errors Array of error details.
* @param mixed $data Additional data.
* @return array<string, mixed> Error response array.
*/
public static function error_response( $message = '', $errors = [], $data = null ) {
$response = [
'success' => false,
'message' => $message,
];
if ( ! empty( $errors ) ) {
$response['errors'] = $errors;
}
if ( null !== $data ) {
$response['data'] = $data;
}
return $response;
}
/**
* Create validation result
*
* @param bool $valid Whether validation passed.
* @param string $message Validation message.
* @param array<int, string> $errors Array of validation errors.
* @return array<string, mixed> Validation result array.
*/
public static function validation_result( $valid, $message = '', $errors = [] ) {
return [
'valid' => $valid,
'message' => $message,
'errors' => Validate::array( $errors, [] ),
];
}
/**
* Validate file upload data
*
* @param array<string, mixed> $file_data $_FILES array data.
* @return array<string, mixed> Validation result.
*/
public static function validate_uploaded_file( $file_data ) {
$errors = [];
// Validate file_data structure.
if ( ! is_array( $file_data ) ) {
return self::validation_result(
false,
__( 'Invalid file data.', 'surerank' ),
[ __( 'File data must be an array.', 'surerank' ) ]
);
}
// Check for upload errors.
if ( ! empty( $file_data['error'] ) ) {
switch ( $file_data['error'] ) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$errors[] = __( 'The uploaded file exceeds the maximum file size.', 'surerank' );
break;
case UPLOAD_ERR_PARTIAL:
$errors[] = __( 'The uploaded file was only partially uploaded.', 'surerank' );
break;
case UPLOAD_ERR_NO_FILE:
$errors[] = __( 'No file was uploaded.', 'surerank' );
break;
default:
$errors[] = __( 'File upload failed.', 'surerank' );
}
}
// Validate required fields.
if ( empty( $file_data['name'] ) ) {
$errors[] = __( 'File name is missing.', 'surerank' );
}
if ( empty( $file_data['tmp_name'] ) ) {
$errors[] = __( 'Temporary file path is missing.', 'surerank' );
}
// Check file type.
if ( ! empty( $file_data['name'] ) ) {
$file_extension = strtolower( pathinfo( $file_data['name'], PATHINFO_EXTENSION ) );
if ( 'json' !== $file_extension ) {
$errors[] = __( 'Only JSON files are allowed.', 'surerank' );
}
}
// Check file size (max 5MB).
$max_size = 5 * 1024 * 1024; // 5MB
if ( ! empty( $file_data['size'] ) && $file_data['size'] > $max_size ) {
$errors[] = sprintf(
/* translators: %s: Maximum file size limit */
__( 'File size exceeds maximum limit of %s.', 'surerank' ),
number_format( $max_size / 1024 / 1024, 2 ) . ' MB'
);
}
if ( ! empty( $errors ) ) {
return self::validation_result(
false,
__( 'File validation failed.', 'surerank' ),
$errors
);
}
return self::validation_result(
true,
__( 'File is valid.', 'surerank' )
);
}
/**
* Validate JSON data
*
* @param string $json_data JSON string to validate.
* @return array<string, mixed> Validation result with decoded data.
*/
public static function validate_json( $json_data ) {
if ( ! is_string( $json_data ) || empty( trim( $json_data ) ) ) {
return self::validation_result(
false,
__( 'Invalid JSON data.', 'surerank' ),
[ __( 'JSON data must be a non-empty string.', 'surerank' ) ]
);
}
$decoded_data = json_decode( $json_data, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return self::validation_result(
false,
__( 'Invalid JSON format.', 'surerank' ),
[ json_last_error_msg() ]
);
}
$result = self::validation_result(
true,
__( 'JSON is valid.', 'surerank' )
);
$result['data'] = $decoded_data;
return $result;
}
/**
* Validate import data structure
*
* @param array<string, mixed> $settings_data Import data to validate.
* @return array<string, mixed> Validation result.
*/
public static function validate_import_data( $settings_data ) {
$errors = [];
// Check if data is array.
if ( ! is_array( $settings_data ) ) {
return self::validation_result(
false,
__( 'Invalid settings data format.', 'surerank' ),
[ __( 'Settings data must be an array.', 'surerank' ) ]
);
}
// Check required fields.
if ( ! isset( $settings_data['plugin'] ) || 'surerank' !== $settings_data['plugin'] ) {
$errors[] = __( 'Invalid plugin identifier. This file does not contain SureRank settings.', 'surerank' );
}
if ( ! isset( $settings_data['settings'] ) || ! is_array( $settings_data['settings'] ) ) {
$errors[] = __( 'Missing or invalid settings data.', 'surerank' );
}
// Validate version compatibility if needed.
if ( isset( $settings_data['version'] ) ) {
$version_check = self::validate_version_compatibility( $settings_data['version'] );
if ( ! $version_check['compatible'] ) {
$errors[] = $version_check['message'];
}
}
if ( ! empty( $errors ) ) {
return self::validation_result(
false,
__( 'Import data validation failed.', 'surerank' ),
$errors
);
}
return self::validation_result(
true,
__( 'Import data is valid.', 'surerank' )
);
}
/**
* Validate version compatibility
*
* @param string $import_version Version from import data.
* @return array<string, mixed> Compatibility result.
*/
public static function validate_version_compatibility( $import_version ) {
// For now, we'll accept all versions.
// In the future, you might want to add version-specific logic.
return [
'compatible' => true,
'message' => __( 'Version compatible.', 'surerank' ),
];
}
/**
* Sanitize categories array
*
* @param array<int, string> $categories Categories to sanitize.
* @param array<int, string> $valid_categories Valid category keys.
* @return array<int, string> Sanitized categories.
*/
public static function sanitize_categories( $categories, $valid_categories = [] ) {
if ( ! is_array( $categories ) ) {
return [];
}
$sanitized = [];
foreach ( $categories as $category ) {
$clean_category = Sanitize::text( $category );
if ( ! empty( $clean_category ) && ( empty( $valid_categories ) || in_array( $clean_category, $valid_categories, true ) ) ) {
$sanitized[] = $clean_category;
}
}
return array_unique( $sanitized );
}
/**
* Get export header with metadata
*
* @return array<string, mixed> Export metadata.
*/
public static function get_export_header() {
return [
'plugin' => 'surerank',
'version' => defined( 'SURERANK_VERSION' ) ? SURERANK_VERSION : '1.0.0',
'timestamp' => current_time( 'mysql' ),
'site_url' => get_site_url(),
'wp_version' => get_bloginfo( 'version' ),
];
}
/**
* Generate backup key
*
* @return string Backup option key.
*/
public static function generate_backup_key() {
return 'surerank_settings_backup_' . time();
}
/**
* Read file content safely
*
* @param string $file_path Path to file.
* @return array<string, mixed> Result with content or error.
*/
public static function read_file_content( $file_path ) {
if ( ! is_string( $file_path ) || empty( trim( $file_path ) ) ) {
return self::error_response(
__( 'Invalid file path.', 'surerank' )
);
}
if ( ! file_exists( $file_path ) ) {
return self::error_response(
__( 'File does not exist.', 'surerank' )
);
}
if ( ! is_readable( $file_path ) ) {
return self::error_response(
__( 'File is not readable.', 'surerank' )
);
}
// Use VIP-compatible file reading if available, otherwise fallback to standard function.
$content = file_get_contents( $file_path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
if ( false === $content ) {
return self::error_response(
__( 'Failed to read file content.', 'surerank' )
);
}
return self::success_response( $content );
}
/**
* Initialize import results structure
*
* @return array<string, mixed> Initial import results array.
*/
public static function init_import_results() {
return [
'success' => false,
'imported_count' => 0,
'errors' => [],
'warnings' => [],
'success_items' => [],
'backup_key' => null,
'message' => '',
];
}
/**
* Download and save image from URL to WordPress media library
*
* @param string $image_url URL of the image to download.
* @param string $setting_key Setting key for naming context (unused, kept for compatibility).
* @return array<string, mixed> Result with new URL or error.
*/
public static function download_and_save_image( $image_url, $setting_key = '' ) {
if ( empty( $image_url ) || ! filter_var( $image_url, FILTER_VALIDATE_URL ) ) {
return self::error_response(
__( 'Invalid image URL.', 'surerank' )
);
}
// Include required WordPress functions.
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
// Prepare file array for sideload.
$file_array = [];
// Get filename from URL.
$file_array['name'] = wp_basename( $image_url );
// Download file to temp location.
$file_array['tmp_name'] = download_url( $image_url );
// If error downloading, return the error.
if ( is_wp_error( $file_array['tmp_name'] ) ) {
return self::error_response(
/* translators: %s: Error message from download_url */
sprintf( __( 'Failed to download image: %s', 'surerank' ), $file_array['tmp_name']->get_error_message() )
);
}
// Do the validation and storage using WordPress media_handle_sideload.
$attachment_id = media_handle_sideload( $file_array, 0, null );
// If error storing permanently, clean up and return error.
if ( is_wp_error( $attachment_id ) ) {
@unlink( $file_array['tmp_name'] ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_unlink
return self::error_response(
/* translators: %s: Error message from media_handle_sideload */
sprintf( __( 'Failed to save image: %s', 'surerank' ), $attachment_id->get_error_message() )
);
}
// Store the original source URL in meta for reference.
add_post_meta( $attachment_id, '_surerank_source_url', $image_url );
add_post_meta( $attachment_id, '_surerank_imported', true );
// Get the new URL.
$new_url = wp_get_attachment_url( $attachment_id );
if ( ! $new_url ) {
return self::error_response(
__( 'Failed to get attachment URL.', 'surerank' )
);
}
$filename = get_attached_file( $attachment_id );
$filename = $filename ? basename( $filename ) : '';
return self::success_response(
[
'url' => $new_url,
'attachment_id' => $attachment_id,
'filename' => $filename,
'source' => 'download_new_file',
'reused' => false,
],
__( 'Image downloaded and saved successfully.', 'surerank' )
);
}
/**
* Process image settings during import.
*
* @param array<string, mixed> $image_urls Settings array to process.
* @return array<string, mixed> Processed settings with updated image URLs.
*/
public static function process_image_settings_import( $image_urls ) {
$image_keys = self::IMAGE_KEYS;
$processed_settings = ! empty( $image_urls ) ? $image_urls : [];
foreach ( $image_keys as $key ) {
if ( ! isset( $processed_settings[ $key ] ) || empty( $processed_settings[ $key ] ) ) {
continue;
}
$image_url = $processed_settings[ $key ];
// Skip local URLs to avoid downloading the same images.
if ( strpos( $image_url, home_url() ) !== false ) {
continue;
}
// Try to download from URL if it's accessible.
if ( filter_var( $image_url, FILTER_VALIDATE_URL ) && self::is_url_accessible( $image_url ) ) {
$download_result = self::download_and_save_image( $image_url, $key );
if ( $download_result['success'] ) {
$processed_settings[ $key ] = $download_result['data']['url'] ?? '';
}
}
}
return $processed_settings;
}
/**
* Get image setting keys
*
* @return array<int, string> Array of image setting keys.
*/
public static function get_image_setting_keys() {
return self::IMAGE_KEYS;
}
/**
* Check if a URL is accessible and returns an image
*
* @param string $url URL to check.
* @return bool True if URL is accessible and returns valid image content.
*/
public static function is_url_accessible( $url ) {
if ( empty( $url ) || ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
return false;
}
// Use WordPress HTTP API with minimal timeout for quick check.
$response = wp_safe_remote_head(
$url,
[
'timeout' => 3, // Quick timeout for accessibility check.
'user-agent' => 'SureRank WordPress Plugin',
]
);
if ( is_wp_error( $response ) ) {
return false;
}
$response_code = wp_remote_retrieve_response_code( $response );
if ( $response_code !== 200 ) {
return false;
}
// Check if the response indicates it's an image.
$content_type = wp_remote_retrieve_header( $response, 'content-type' );
if ( ! empty( $content_type ) ) {
// Handle case where content-type could be an array.
$content_type_string = is_array( $content_type ) ? $content_type[0] : $content_type;
if ( strpos( $content_type_string, 'image/' ) === 0 ) {
return true;
}
}
// If no content-type header or not an image type, return false.
return false;
}
}