Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2a3283c
chore: add settings view
tijmenbruggeman Jan 2, 2026
1625d03
chore: add logger singleton
tijmenbruggeman Jan 2, 2026
d3e3be1
chore: add diagnostics class
tijmenbruggeman Jan 2, 2026
f38b3c7
chore: download zip file from settings page
tijmenbruggeman Jan 2, 2026
25bbc46
chore: add loader and improve markup
tijmenbruggeman Jan 2, 2026
47cd48f
Add various log points
tijmenbruggeman Jan 2, 2026
df29291
Set timeout and proxy options on curl client
tijmenbruggeman Jan 5, 2026
98eade7
Scaffold tests
tijmenbruggeman Jan 5, 2026
e72c663
formatting
tijmenbruggeman Jan 5, 2026
178d63f
Add initial tests
tijmenbruggeman Jan 5, 2026
131a86a
On constructor log enabled test
tijmenbruggeman Jan 5, 2026
2cfabf1
Manual formatting logger
tijmenbruggeman Jan 5, 2026
f54b12d
Manual formatting fixes in diagnostics
tijmenbruggeman Jan 5, 2026
9adff72
chore: add tests for log path
tijmenbruggeman Jan 5, 2026
76ce4cb
chore: simplify rotation
tijmenbruggeman Jan 6, 2026
396ccbb
Format
tijmenbruggeman Jan 6, 2026
6cfae11
change file name, format and size
tijmenbruggeman Jan 6, 2026
d430ff6
Clear logs when turned on
tijmenbruggeman Jan 7, 2026
e4c3ef3
Add more tests
tijmenbruggeman Jan 7, 2026
1d7cd63
Add mocks for test
tijmenbruggeman Jan 7, 2026
85f8158
set timeout to 300
tijmenbruggeman Jan 7, 2026
cd56cab
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Jan 8, 2026
f995de4
Seperate API timeout and connect timeout
tijmenbruggeman Jan 9, 2026
42623d1
remove server info and add a phpinfo file
tijmenbruggeman Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/class-tiny-compress-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@

class Tiny_Compress_Client extends Tiny_Compress {

/**
* API request timeout in seconds.
*
* @since 3.6.8
* @var int
*/
const API_TIMEOUT = 120;
const CONNECT_TIMEOUT = 8;

private $last_error_code = 0;
private $last_message = '';
private $proxy;
Expand Down Expand Up @@ -92,7 +101,6 @@ protected function compress( $input, $resize_opts, $preserve_opts, $convert_to )
try {
$this->last_error_code = 0;
$this->set_request_options( \Tinify\Tinify::getClient() );

$source = \Tinify\fromBuffer( $input );

if ( $resize_opts ) {
Expand Down Expand Up @@ -138,6 +146,11 @@ protected function compress( $input, $resize_opts, $preserve_opts, $convert_to )
} catch ( \Tinify\Exception $err ) {
$this->last_error_code = $err->status;

Tiny_Logger::error('client compress error', array(
'error' => $err->getMessage(),
'status' => $err->status,
));

throw new Tiny_Exception(
$err->getMessage(),
get_class( $err ),
Expand Down Expand Up @@ -172,6 +185,10 @@ private function set_request_options( $client ) {
$property->setAccessible( true );
$options = $property->getValue( $client );

// Set API request timeout to prevent indefinite hanging
$options[ CURLOPT_TIMEOUT ] = self::API_TIMEOUT;
$options[ CURLOPT_CONNECTTIMEOUT ] = self::CONNECT_TIMEOUT;

if ( TINY_DEBUG ) {
$file = fopen( dirname( __FILE__ ) . '/curl.log', 'w' );
if ( is_resource( $file ) ) {
Expand All @@ -190,5 +207,7 @@ private function set_request_options( $client ) {
$options[ CURLOPT_PROXYUSERPWD ] = $this->proxy->authentication();
}
}

$property->setValue( $client, $options );
}
}
6 changes: 6 additions & 0 deletions src/class-tiny-compress-fopen.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ protected function compress( $input, $resize_opts, $preserve_opts, $convert_to )
$params = $this->request_options( 'POST', $input );
list($details, $headers, $status_code) = $this->request( $params );

Tiny_Logger::debug('client fopen compress out', array(
'details' => $details,
'headers' => $headers,
'status' => $status_code,
));

$output_url = isset( $headers['location'] ) ? $headers['location'] : null;
if ( $status_code >= 400 && is_array( $details ) && isset( $details['error'] ) ) {
throw new Tiny_Exception(
Expand Down
259 changes: 259 additions & 0 deletions src/class-tiny-diagnostics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<?php
/*
* Tiny Compress Images - WordPress plugin.
* Copyright (C) 2015-2026 Tinify B.V.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/**
* Collects diagnostic information and generates downloadable zip files.
*
* @since 3.7.0
*/
class Tiny_Diagnostics {

/**
* Tiny settings
*
* @var Tiny_Settings
*/
private $settings;

/**
* @param Tiny_Settings $settings
*/
public function __construct( $settings ) {
$this->settings = $settings;

add_action(
'wp_ajax_tiny_download_diagnostics',
array( $this, 'download_diagnostics' )
);
}

/**
* Collects all diagnostic information.
*
* File contains:
* - timestamp of export
* - server information
* - site information
* - plugin list
* - tinify settings
* - image settings
* - logs
*
* @since 3.7.0
*
* @return array Array of diagnostic information.
*/
public function collect_info() {
$info = array(
'timestamp' => current_time( 'Y-m-d H:i:s' ),
'site_info' => self::get_site_info(),
'active_plugins' => self::get_active_plugins(),
'tiny_info' => $this->get_tiny_info(),
'image_sizes' => $this->settings->get_active_tinify_sizes(),
);

return $info;
}

/**
* Gets site information.
*
* @since 3.7.0
*
* @return array Site information.
*/
private static function get_site_info() {
global $wp_version;
$theme = wp_get_theme();

return array(
'wp_version' => $wp_version,
'site_url' => get_site_url(),
'home_url' => get_home_url(),
'is_multisite' => is_multisite(),
'site_language' => get_locale(),
'timezone' => wp_timezone_string(),
'theme_name' => $theme->get( 'Name' ),
'theme_version' => $theme->get( 'Version' ),
'theme_uri' => $theme->get( 'ThemeURI' ),
);
}

/**
* Gets list of active plugins.
*
* @since 3.7.0
*
* @return array List of active plugins.
*/
private static function get_active_plugins() {
$active_plugins = get_option( 'active_plugins', array() );
$plugins = array();

foreach ( $active_plugins as $plugin ) {
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
$plugins[] = array(
'name' => $plugin_data['Name'],
'version' => $plugin_data['Version'],
'author' => $plugin_data['Author'],
'file' => $plugin,
);
}

return $plugins;
}

/**
* Gets TinyPNG plugin info & settings.
*
* @since 3.7.0
*
* @return array Plugin settings
*/
private function get_tiny_info() {
return array(
'version' => Tiny_Plugin::version(),
'status' => $this->settings->get_status(),
'php_client_supported' => Tiny_PHP::client_supported(),

'compression_count' => $this->settings->get_compression_count(),
'compression_timing' => $this->settings->get_compression_timing(),
'conversion' => $this->settings->get_conversion_options(),
'paying_state' => $this->settings->get_paying_state(),
);
}

public function download_diagnostics() {
$zippath = $this->create_diagnostic_zip();
return $this->download_zip( $zippath );
}

/**
* Creates a diagnostic zip file.
*
* @since 3.7.0
*
* @return string|WP_Error Path to the created zip file or WP_Error on failure.
*/
public function create_diagnostic_zip() {
if ( ! class_exists( 'ZipArchive' ) ) {
return new WP_Error(
'zip_not_available',
__( 'ZipArchive class is not available on this server.',
'tiny-compress-images'
)
);
}

$upload_dir = wp_upload_dir();
$temp_dir = trailingslashit( $upload_dir['basedir'] ) . 'tiny-compress-temp';
if ( ! file_exists( $temp_dir ) ) {
wp_mkdir_p( $temp_dir );
}

$zip_filename = 'tiny-compress-diagnostics-' . gmdate( 'Y-m-d-His' ) . '.zip';
$zip_path = trailingslashit( $temp_dir ) . $zip_filename;

$zip = new ZipArchive();
if ( true !== $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
return new WP_Error( 'zip_create_failed',
__( 'Failed to create zip file.',
'tiny-compress-images' )
);
}

// Add diagnostic info.
$info = self::collect_info();
$zip->addFromString( 'tiny-diagnostics.json', wp_json_encode( $info, JSON_PRETTY_PRINT ) );

// Add phpinfo HTML.
ob_start();
phpinfo( INFO_GENERAL );
phpinfo( INFO_CONFIGURATION );
phpinfo( INFO_MODULES );
$phpinfo_html = ob_get_clean();
$zip->addFromString( 'phpinfo.html', $phpinfo_html );

// Add log files.
$logger = Tiny_Logger::get_instance();
$log_files = $logger->get_log_files();

foreach ( $log_files as $log_file ) {
if ( file_exists( $log_file ) ) {
$zip->addFile( $log_file, 'logs/' . basename( $log_file ) );
}
}

$zip->close();
return $zip_path;
}

/**
* Downloads and removes the zip
*
* @since 3.7.0
*
* @param string $zip_path Path to the zip file.
*/
public static function download_zip( $zip_path ) {
if ( ! file_exists( $zip_path ) ) {
wp_die( esc_html__( 'Diagnostic file not found.', 'tiny-compress-images' ) );
}

header( 'Content-Type: application/zip' );
header( 'Content-Disposition: attachment; filename="' . basename( $zip_path ) . '"' );
header( 'Content-Length: ' . filesize( $zip_path ) );
header( 'Pragma: no-cache' );
header( 'Expires: 0' );

// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
readfile( $zip_path );

// Clean up.
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
unlink( $zip_path );

exit;
}

/**
* Cleans up old diagnostic zip files.
*
* @since 3.7.0
*/
public static function cleanup_old_diagnostics() {
$upload_dir = wp_upload_dir();
$temp_dir = trailingslashit( $upload_dir['basedir'] ) . 'tiny-compress-temp';

if ( ! file_exists( $temp_dir ) ) {
return;
}

$files = glob( trailingslashit( $temp_dir ) . 'tiny-compress-diagnostics-*.zip' );
$max_age = DAY_IN_SECONDS; // 1 day.

foreach ( $files as $file ) {
if ( file_exists( $file ) && (time() - filemtime( $file )) > $max_age ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
unlink( $file );
}
}
}
}
31 changes: 29 additions & 2 deletions src/class-tiny-image.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ public function get_mime_type() {
}

public function compress() {
Tiny_Logger::debug('compress', array(
'image_id' => $this->id,
'name' => $this->name,
));
if ( $this->settings->get_compressor() === null || ! $this->file_type_allowed() ) {
return;
}
Expand Down Expand Up @@ -212,6 +216,20 @@ public function compress() {
$this->update_tiny_post_meta();
$resize = $this->settings->get_resize_options( $size_name );
$preserve = $this->settings->get_preserve_options( $size_name );
Tiny_Logger::debug('compress size', array(
'image_id' => $this->id,
'size' => $size_name,
'resize' => $resize,
'preserve' => $preserve,
'convert' => $convert_to,
'modified' => $size->modified(),
'filename' => $size->filename,
'is_duplicate' => $size->is_duplicate(),
'exists' => $size->exists(),
'has_been_compressed' => $size->has_been_compressed(),
'filesize' => $size->filesize(),
'mimetype' => $size->mimetype(),
));
try {
$response = $compressor->compress_file(
$size->filename,
Expand All @@ -227,14 +245,23 @@ public function compress() {

$size->add_tiny_meta( $response );
$success++;
Tiny_Logger::debug('compress success', array(
'size' => $size_name,
'image_id' => $this->id,
));
} catch ( Tiny_Exception $e ) {
$size->add_tiny_meta_error( $e );
$failed++;
Tiny_Logger::error('compress failed', array(
'error' => $e->get_message(),
'size' => $size_name,
'image_id' => $this->id,
));
}
$this->add_wp_metadata( $size_name, $size );
$this->update_tiny_post_meta();
}
}
}// End if().
}// End foreach().

/*
Other plugins can hook into this action to execute custom logic
Expand Down
Loading