: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Scenario: Trying to find the attachment for a file called x-150x150.jpg.
// 1. Since WordPress adds the -150x150 suffix for thumbnail sizes we cannot be
// sure if this is an attachment or an attachment's generated thumbnail.
// 2. Since both x.jpg and x-150x150.jpg can be uploaded as separate attachments
// we must decide which is a better match.
// 3. The above is why we order by guid length and use the first result.
$attachments_query = $wpdb->prepare( "
ORDER BY CHAR_LENGTH( `guid` ) DESC
", 'attachment', esc_url_raw( $url ), esc_url_raw( $fallback_url ) );
$attachment_id = (int) $wpdb->get_var( $attachments_query );
* Encode image in a base64 format.
* @param array $images Array of data for which images need to be encoded if any.
protected function encode_images( $images ) {
foreach ( $images as $url ) {
$url = wp_get_attachment_url( $id );
$id = $this->_get_attachment_id_by_url( $url );
$image = $this->_encode_attachment_image( $id );
// Case 1: No attachment found.
// Case 2: Attachment found, but file does not exist (may be stored on a CDN, for example).
$image = $this->_encode_remote_image( $url );
// All fetching methods have failed - bail on encoding.
$encoded[ $url ] = array(
// Add image id for replacement purposes.
$encoded[ $url ]['id'] = $id;
* Encode an image attachment.
protected function _encode_attachment_image( $id ) {
* @var WP_Filesystem_Base $wp_filesystem
if ( ! current_user_can( 'read_post', $id ) ) {
$file = get_attached_file( $id );
if ( ! $wp_filesystem->exists( $file ) ) {
$image = $wp_filesystem->get_contents( $file );
return base64_encode( $image );
protected function _encode_remote_image( $url ) {
$request = wp_remote_get( esc_url_raw( $url ), array(
if ( ! is_array( $request ) || is_wp_error( $request ) ) {
if ( ! self::$_->includes( $request['headers']['content-type'], 'image' ) ) {
$image = wp_remote_retrieve_body( $request );
return base64_encode( $image );
* Decode base64 formatted image and upload it to WP media.
* @param array $images Array of encoded images which needs to be uploaded.
protected function upload_images( $images ) {
$filesystem = $this->set_filesystem();
foreach ( $images as $key => $image ) {
$basename = sanitize_file_name( wp_basename( $image['url'] ) );
$attachments = get_posts( array(
'post_type' => 'attachment',
'meta_key' => '_wp_attached_file',
'meta_value' => pathinfo( $basename, PATHINFO_FILENAME ),
'meta_compare' => 'LIKE',
if ( ! is_wp_error( $attachments ) && ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
$attachment_url = wp_get_attachment_url( $attachment->ID );
$file = get_attached_file( $attachment->ID );
$filename = sanitize_file_name( wp_basename( $file ) );
// Use existing image only if the content matches.
if ( $filesystem->get_contents( $file ) === base64_decode( $image['encoded'] ) ) {
$id = isset( $image['id'] ) ? $attachment->ID : 0;
$temp_file = wp_tempnam();
$filesystem->put_contents( $temp_file, base64_decode( $image['encoded'] ) );
$filetype = wp_check_filetype_and_ext( $temp_file, $basename );
// Avoid further duplicates if the proper_file name match an existing image.
if ( isset( $filetype['proper_filename'] ) && $filetype['proper_filename'] !== $basename ) {
if ( isset( $filename ) && $filename === $filetype['proper_filename'] ) {
// Use existing image only if the basenames and content match.
if ( $filesystem->get_contents( $file ) === $filesystem->get_contents( $temp_file ) ) {
$filesystem->delete( $temp_file );
'tmp_name' => $temp_file,
$upload = media_handle_sideload( $file, 0 );
if ( ! is_wp_error( $upload ) ) {
// Set the replacement as an id if the original image was set as an id (for gallery).
$id = isset( $image['id'] ) ? $upload : 0;
$url = wp_get_attachment_url( $upload );
// Make sure the temporary file is removed if media_handle_sideload didn't take care of it.
$filesystem->delete( $temp_file );
// Only declare the replace if a url is set.
$images[$key]['replacement_id'] = $id;
$images[$key]['replacement_url'] = $url;
* Replace encoded image url with a real url
* @param $subject - The string to perform replacing for
* @param array $image - The image settings
* @return string|string[]|null
protected function replace_image_url( $subject, $image ) {
if ( isset( $image['replacement_id'] ) && isset( $image['id'] ) ) {
$replacement = $image['replacement_id'];
$subject = preg_replace( "/(gallery_ids=.*){$search}(.*\")/", "\${1}{$replacement}\${2}", $subject );
if ( isset( $image['url'] ) && isset( $image['replacement_url'] ) && $image['url'] !== $image['replacement_url'] ) {
$replacement = $image['replacement_url'];
$subject = str_replace( $search, $replacement, $subject );
* Replace image urls with newly uploaded images.
* @param array $images Array of new images uploaded.
* @param array $data Array of for which images url needs to be replaced.
* @return array|mixed|object
protected function replace_images_urls( $images, $data ) {
foreach ( $data as $post_id => &$post_data ) {
foreach ( $images as $image ) {
if ( is_array( $post_data ) ) {
foreach ( $post_data as $post_param => &$param_value ) {
if ( ! is_array( $param_value ) ) {
$data[ $post_id ][ $post_param ] = $this->replace_image_url( $param_value, $image );
$data[ $post_id ] = $this->replace_image_url( $post_data, $image );
* Validate data and remove any malicious code.
* @param array $data Array of data which needs to be validated.
* @param array $fields_validation Array of field and validation callback.
protected function validate( $data, $fields_validation = array() ) {
if ( ! is_array( $data ) ) {
foreach ( $data as $key => $value ) {
if ( is_array( $value ) ) {
$data[$key] = $this->validate( $value, $fields_validation );
if ( isset( $fields_validation[$key] ) ) {
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
$data[$key] = call_user_func( $fields_validation[$key], $value );
if ( current_user_can( 'unfiltered_html' ) ) {
$data[ $key ] = wp_kses_post( $value );
* Prevent import and export timeout or memory failure.
* It doesn't need to be reset as in both case the request exit.
protected function prevent_failure() {
// Increase memory which is safe at this stage of the request.
if ( et_core_get_memory_limit() < 256 ) {
@ini_set( 'memory_limit', '256M' );
* Set WP filesystem to direct. This should only be use to create a temporary file.
* It is safe to do so since the created file is removed immediately after import. The method does'nt have
* to be reset since the ajax query is exited.
protected function set_filesystem() {
add_filter( 'filesystem_method', array( $this, 'replace_filesystem_method' ) );
* Proxy method for set_filesystem() to avoid calling it multiple times.
* @return WP_Filesystem_Direct
protected function get_filesystem() {
static $filesystem = null;
if ( null === $filesystem ) {
$filesystem = $this->set_filesystem();
* Check if a temporary file is register. Returns temporary file if it exists.
* @since 4.0 Made method public.
* @param string $id Unique id used when the temporary file was created.
* @param string $group Group name in which files are grouped.
public function has_temp_file( $id, $group ) {
$temp_files = get_option( '_et_core_portability_temp_files', array() );
if ( isset( $temp_files[$group][$id] ) && file_exists( $temp_files[$group][$id] ) ) {
return $temp_files[$group][$id];
* Create a temp file and register it.
* @since 4.0 Made method public. Added $content parameter.
* @param string $id Unique id reference for the temporary file.
* @param string $group Group name in which files are grouped.
* @param string|bool $temp_file Path to the temporary file. False create a new temporary file.
public function temp_file( $id, $group, $temp_file = false, $content = '' ) {
$temp_files = get_option( '_et_core_portability_temp_files', array() );
if ( ! isset( $temp_files[$group] ) ) {
$temp_files[$group] = array();
if ( isset( $temp_files[$group][$id] ) && file_exists( $temp_files[$group][$id] ) ) {
return $temp_files[$group][$id];
$temp_file = $temp_file ? $temp_file : wp_tempnam();
$temp_files[$group][$id] = $temp_file;
update_option( '_et_core_portability_temp_files', $temp_files, false );
if ( ! empty( $content ) ) {
$this->get_filesystem()->put_contents( $temp_file, $content );
* Get temp file contents or an empty string if it does not exist.
* @param string $id Unique id used when the temporary file was created.
* @param string $group Group name in which files are grouped.
public function get_temp_file_contents( $id, $group ) {
$file = $this->has_temp_file( $id, $group );
$content = $this->get_filesystem()->get_contents( $file );
return $content ? $content : '';
* Delete all the temp files.
* @param bool|string $group Group name in which files are grouped. Set to true to remove all groups and files.
* @param array $defined_files Array or temoporary files to delete. No argument deletes all temp files.
public function delete_temp_files( $group = false, $defined_files = false ) {
$filesystem = $this->set_filesystem();
$temp_files = get_option( '_et_core_portability_temp_files', array() );
// Remove all temp files accross all groups if group is true.
foreach( $temp_files as $group_id => $_group ) {
$this->delete_temp_files( $group_id );
if ( ! isset( $temp_files[$group] ) ) {
$delete_files = ( is_array( $defined_files ) && ! empty( $defined_files ) ) ? $defined_files : $temp_files[$group];
foreach ( $delete_files as $id => $temp_file ) {
if ( isset( $temp_files[$group][$id] ) && $filesystem->delete( $temp_files[$group][$id] ) ) {
unset( $temp_files[$group][$id] );
if ( empty( $temp_files[$group] ) ) {
unset( $temp_files[$group] );
if ( empty( $temp_files ) ) {
delete_option( '_et_core_portability_temp_files' );
update_option( '_et_core_portability_temp_files', $temp_files, false );
* Set WP filesystem method to direct.
public function replace_filesystem_method() {
* Get timestamp or create one if it isn't set.
public function get_timestamp() {