ns_paths as $path_index => $extensions_path ) { if ( file_exists( $output_paths[ $output_format ][ $path_index ] ) || ( ! $force_convert_deleted && file_exists( $output_paths[ $output_format ][ $path_index ] . '.' . SkipLarger::DELETED_FILE_EXTENSION ) ) ) { unset( $source_paths[ $output_format ][ $path_index ] ); unset( $output_paths[ $output_format ][ $path_index ] ); $this->files_available[ $output_format ]--; } } } } try { $converted_files = $this->init_connections( $source_paths, $plugin_settings, $output_paths ); $this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings ); if ( $this->failed_converted_source_files ) { $converted_files = $this->init_connections( $this->failed_converted_source_files, $plugin_settings, $output_paths ); $this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings ); } } catch ( RemoteErrorResponseException $e ) { $this->save_conversion_error( $e->getMessage(), $plugin_settings, true ); } $this->token_repository->update_token( $this->token ); } /** * @param mixed[] $converted_files . * @param mixed[] $source_paths . * @param mixed[] $output_paths . * @param mixed[] $plugin_settings . * * @return void */ private function save_converted_files( array $converted_files, array $source_paths, array $output_paths, array $plugin_settings ) { foreach ( $converted_files as $output_format => $format_converted_files ) { foreach ( $format_converted_files as $path_index => $converted_file ) { $source_path = $source_paths[ $output_format ][ $path_index ]; $output_path = $output_paths[ $output_format ][ $path_index ]; file_put_contents( $output_path, $converted_file ); do_action( 'webpc_after_conversion', $output_path, $source_path ); try { $this->skip_crashed->delete_crashed_file( $output_path ); $this->skip_larger->remove_image_if_is_larger( $output_path, $source_path, $plugin_settings ); $this->update_conversion_stats( $source_path, $output_path, $output_format ); } catch ( LargerThanOriginalException $e ) { $this->files_converted[ $output_format ]--; } } } } /** * @param string[] $paths . * @param mixed[] $plugin_settings . * @param string $output_format . * * @return string[] * * @throws SourcePathException * @throws OutputPathException */ private function get_source_paths( array $paths, array $plugin_settings, string $output_format ): array { $source_paths = []; foreach ( $paths as $path ) { $source_path = $this->get_image_source_path( $path ); if ( filesize( $source_path ) > self::MAX_FILESIZE_BYTES ) { $this->save_conversion_error( ( new FilesizeOversizeException( [ self::MAX_FILESIZE_BYTES, $source_path ] ) )->getMessage(), $plugin_settings ); $this->skip_crashed->create_crashed_file( $this->get_image_output_path( $source_path, $output_format ) ); continue; } $path_extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) ); if ( $path_extension === $output_format ) { continue; } $source_paths[] = $this->get_image_source_path( $path ); } return $source_paths; } /** * @param string[] $source_paths . * @param string $output_format . * * @return string[] * * @throws OutputPathException */ private function get_output_paths( array $source_paths, string $output_format ): array { $output_path = []; foreach ( $source_paths as $path ) { $output_path[] = $this->get_image_output_path( $path, $output_format ); } return $output_path; } /** * @param mixed[] $source_paths . * @param mixed[] $plugin_settings . * @param mixed[] $output_paths . * * @return mixed[] * * @throws RemoteErrorResponseException */ private function init_connections( array $source_paths, array $plugin_settings, array $output_paths ): array { $mh_items = []; $values = []; $mh = curl_multi_init(); if ( ! $mh ) { return $values; } foreach ( $source_paths as $output_format => $format_source_paths ) { foreach ( $format_source_paths as $resource_id => $source_path ) { $connect = $this->get_curl_connection( $source_path, $output_format, $plugin_settings ); if ( ! $connect ) { continue; } curl_multi_add_handle( $mh, $connect ); $mh_items[ $output_format ] = $mh_items[ $output_format ] ?? []; $mh_items[ $output_format ][ $resource_id ] = $connect; } } $running = null; do { curl_multi_exec( $mh, $running ); curl_multi_select( $mh ); } while ( $running > 0 ); foreach ( $mh_items as $output_format => $format_mh_items ) { foreach ( $format_mh_items as $resource_id => $mh_item ) { $http_code = curl_getinfo( $mh_item, CURLINFO_HTTP_CODE ); $response = curl_multi_getcontent( $mh_item ); if ( ( $http_code === 200 ) && ( strlen( $response ) > 10 ) ) { $values[ $output_format ] = $values[ $output_format ] ?? []; $values[ $output_format ][ $resource_id ] = $response; $this->files_converted[ $output_format ]++; } else { $this->handle_request_error( $source_paths[ $output_format ][ $resource_id ], $output_paths[ $output_format ][ $resource_id ], $output_format, (int) $resource_id, $plugin_settings, $http_code, $response ); } curl_multi_remove_handle( $mh, $mh_item ); } } curl_multi_close( $mh ); return $values; } /** * @param string $source_path . * @param string $output_format . * @param mixed[] $plugin_settings . * * @return resource|null */ private function get_curl_connection( string $source_path, string $output_format, array $plugin_settings ) { $connect = curl_init( WebpConverterConstants::API_CONVERSION_URL ); if ( ! $connect ) { return null; } curl_setopt( $connect, CURLOPT_SSL_VERIFYPEER, false ); curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $connect, CURLOPT_TIMEOUT, apply_filters( 'webpc_remote_timeout', 30 ) ); curl_setopt( $connect, CURLOPT_POST, true ); curl_setopt( $connect, CURLOPT_POSTFIELDS, [ 'access_token' => $plugin_settings[ AccessTokenOption::OPTION_NAME ], 'domain_host' => parse_url( get_site_url(), PHP_URL_HOST ), 'source_file' => curl_file_create( $source_path ), 'output_format' => $output_format, 'quality_level' => $plugin_settings[ ImagesQualityOption::OPTION_NAME ], 'strip_metadata' => ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_KEEP_METADATA, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) ), 'max_width' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' ) ? $plugin_settings[ ImageResizeOption::OPTION_NAME ][1] : 0, 'max_height' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' ) ? $plugin_settings[ ImageResizeOption::OPTION_NAME ][2] : 0, ] ); curl_setopt( $connect, CURLOPT_HTTPHEADER, [ 'Content-Type: multipart/form-data', 'Expect:', ] ); curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $connect, CURLOPT_HEADERFUNCTION, [ $this, 'handle_request_header' ] ); return $connect; } /** * @param resource $curl . * @param string $header . * * @return int */ public function handle_request_header( $curl, string $header ): int { $header_length = strlen( $header ); $header_data = explode( ':', $header ); if ( count( $header_data ) > 2 ) { return $header_length; } $header_key = strtolower( trim( $header_data[0] ) ); if ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_USAGE ) { $this->token->set_images_usage( intval( $header_data[1] ) ); } elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_MAX ) { $this->token->set_images_limit( intval( $header_data[1] ) ); } elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_SUBSCRIPTION_ACTIVE ) { $this->token->set_valid_status( ( trim( $header_data[1] ) === '1' ) ); } return $header_length; } /** * @param string $source_path . * @param string $output_path . * @param string $output_format . * @param int $resource_id . * @param mixed[] $plugin_settings . * @param int $http_code . * @param string|null $response . * * @return void * * @throws RemoteErrorResponseException */ private function handle_request_error( string $source_path, string $output_path, string $output_format, int $resource_id, array $plugin_settings, int $http_code, string $response = null ) { $response_value = ( $response ) ? json_decode( $response, true ) : []; $error_message = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_MESSAGE ] ?? ''; $error_fatal_status = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_FATAL_STATUS ] ?? false; if ( $error_message && $error_fatal_status ) { throw new RemoteErrorResponseException( $error_message ); } elseif ( $error_message ) { $this->save_conversion_error( $error_message, $plugin_settings ); } elseif ( $http_code === 200 ) { $this->skip_crashed->create_crashed_file( $output_path ); if ( ! isset( $this->failed_converted_source_files[ $output_format ] ) ) { $this->failed_converted_source_files[ $output_format ] = []; } $this->failed_converted_source_files[ $output_format ][ $resource_id ] = $source_path; } else { $this->save_conversion_error( ( new RemoteRequestException( [ $http_code, $source_path ] ) )->getMessage(), $plugin_settings ); } } }