Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
169 changes: 88 additions & 81 deletions inc/media_rename/attachment_db_renamer.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ private function is_text_col( $type ) {
/**
* Process a single column for replacements
*
* @param string $table Table name.
* @param string $column Column name.
* @param array $primary_keys Primary keys.
* @param string $old_url Old URL.
* @param string $new_url New URL.
* @param string $table Table name.
* @param string $column Column name.
* @param list<string> $primary_keys Primary keys.
* @param string $old_url Old URL.
* @param string $new_url New URL.
*
* @return int Number of replacements
*/
Expand All @@ -179,42 +179,80 @@ private function process_column( $table, $column, $primary_keys, $old_url, $new_
$count = $this->php_handle_column( $table, $column, $primary_keys, $old_url, $new_url );
} else {
// Use direct SQL replacement for non-serialized data
$count = $this->sql_handle_column( $table, $column, $old_url, $new_url );
$count = $this->sql_handle_column( $table, $column, $primary_keys, $old_url, $new_url );
}

return $count;
}

/**
* Get the URL without the file extension, so size and scaled variants match it too.
*
* @param string $url File URL.
*
* @return string Empty when the URL has no path.
*/
private function get_base_url( string $url ): string {
$parts = parse_url( $url );
if ( ! isset( $parts['path'] ) ) {
return '';
}

return $this->get_origin( $parts ) . dirname( $parts['path'] ) . '/' . pathinfo( $parts['path'], PATHINFO_FILENAME );
}

/**
* Get the scheme, host and port of a parsed URL.
*
* @param array{scheme?: string, host?: string, port?: int} $parts Result of parse_url().
*
* @return string Empty when the URL has no host.
*/
private function get_origin( array $parts ): string {
if ( ! isset( $parts['host'] ) ) {
return '';
}

return 'http' . ( isset( $parts['scheme'] ) && $parts['scheme'] === 'https' ? 's' : '' ) . '://' . $parts['host'] . ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' );
}

/**
* Get the primary key values that identify a row.
*
* @param stdClass $row Database row.
* @param list<string> $primary_keys Primary keys.
*
* @return array<string, string> Primary key values keyed by column.
*/
private function get_row_where( stdClass $row, $primary_keys ): array {
$where = [];
foreach ( $primary_keys as $key ) {
$where[ $key ] = $row->$key;
}

return $where;
}

/**
* Handle column using SQL replacement
*
* @param string $table Table name.
* @param string $column Column name.
* @param string $old_url Old URL.
* @param string $new_url New URL.
* @param string $table Table name.
* @param string $column Column name.
* @param list<string> $primary_keys Primary keys.
* @param string $old_url Old URL.
* @param string $new_url New URL.
*
* @return int Number of replacements
*/
private function sql_handle_column( $table, $column, $old_url, $new_url ) {
private function sql_handle_column( $table, $column, $primary_keys, $old_url, $new_url ) {
global $wpdb;
$count = 0;

// Get the filename components
$old_path_parts = parse_url( $old_url );
if ( ! isset( $old_path_parts['path'] ) ) {
$base_url = $this->get_base_url( $old_url );
if ( '' === $base_url ) {
return 0;
}

$old_path = $old_path_parts['path'];
$old_file_info = pathinfo( $old_path );

$old_base = $old_file_info['filename'];
$old_dir = dirname( $old_path );
$old_domain = isset( $old_path_parts['host'] ) ? 'http' . ( isset( $old_path_parts['scheme'] ) && $old_path_parts['scheme'] === 'https' ? 's' : '' ) . '://' . $old_path_parts['host'] : '';

// Build pattern to match any URL containing the base filename
$base_url = $old_domain . $old_dir . '/' . $old_base;

// Get rows with regular URLs
$rows = $wpdb->get_results(
$wpdb->prepare(
Expand Down Expand Up @@ -247,35 +285,19 @@ private function sql_handle_column( $table, $column, $old_url, $new_url ) {
}

foreach ( $all_rows as $row ) {
$id_field = $row->ID ?? $row->id ?? null;
if ( ! $id_field ) {
foreach ( $row as $field => $value ) {
if ( stripos( $field, 'id' ) !== false ) {
$id_field = $value;
break;
}
}
}

if ( ! $id_field ) {
continue;
}
$where = $this->get_row_where( $row, $primary_keys );
$row_id = implode( '|', $where );

// Skip if we've already processed this row
if ( isset( $processed_ids[ $id_field ] ) ) {
if ( isset( $processed_ids[ $row_id ] ) ) {
continue;
}
$processed_ids[ $id_field ] = true;
$processed_ids[ $row_id ] = true;

$content = $row->$column;
$new_content = $this->replace_image_urls( $content, $old_url, $new_url );

if ( $content !== $new_content ) {
$wpdb->update(
$table,
[ $column => $new_content ],
[ 'ID' => $id_field ]
);
if ( $content !== $new_content && $wpdb->update( $table, [ $column => $new_content ], $where ) ) {
++$count;
}
}
Expand All @@ -286,26 +308,31 @@ private function sql_handle_column( $table, $column, $old_url, $new_url ) {
/**
* Handle column using PHP for serialized data
*
* @param string $table Table name.
* @param string $column Column name.
* @param array $primary_keys Primary keys.
* @param string $old_url Old URL.
* @param string $new_url New URL.
* @param string $table Table name.
* @param string $column Column name.
* @param list<string> $primary_keys Primary keys.
* @param string $old_url Old URL.
* @param string $new_url New URL.
*
* @return int Number of replacements
*/
private function php_handle_column( $table, $column, $primary_keys, $old_url, $new_url ) {
global $wpdb;

$count = 0;
$json_old_url = str_replace( '/', '\/', $old_url );

$base_url = $this->get_base_url( $old_url );
if ( '' === $base_url ) {
return 0;
}
$json_base_url = str_replace( '/', '\/', $base_url );
Comment thread
pirate-bot marked this conversation as resolved.
Resolved

// Build the query and allow processing with multiple primary keys.
$query = 'SELECT ';
foreach ( $primary_keys as $key => $value ) {
$query .= $wpdb->prepare( '%i, ', $value );
}
$query .= '%i FROM %i WHERE %s LIKE %s LIMIT 100';
$query .= '%i FROM %i WHERE %i LIKE %s';

// Get the rows that need updating - first for regular URLs
$rows = $wpdb->get_results(
Expand All @@ -314,7 +341,7 @@ private function php_handle_column( $table, $column, $primary_keys, $old_url, $n
$column,
$table,
$column,
'%' . $wpdb->esc_like( $old_url ) . '%'
'%' . $wpdb->esc_like( $base_url ) . '%'
)
);

Expand All @@ -325,7 +352,7 @@ private function php_handle_column( $table, $column, $primary_keys, $old_url, $n
$column,
$table,
$column,
'%' . $wpdb->esc_like( $json_old_url ) . '%'
'%' . $wpdb->esc_like( $json_base_url ) . '%'
Comment thread
pirate-bot marked this conversation as resolved.
Resolved
)
);

Expand All @@ -334,11 +361,8 @@ private function php_handle_column( $table, $column, $primary_keys, $old_url, $n
$all_rows = array_merge( $rows, $json_rows );

foreach ( $all_rows as $row ) {
// Generate a unique identifier for this row based on primary keys
$row_id = '';
foreach ( $primary_keys as $key ) {
$row_id .= $row->$key . '|';
}
$where = $this->get_row_where( $row, $primary_keys );
$row_id = implode( '|', $where );

// Skip if we've already processed this row
if ( isset( $processed_ids[ $row_id ] ) ) {
Expand All @@ -361,17 +385,11 @@ private function php_handle_column( $table, $column, $primary_keys, $old_url, $n
continue;
}

// Build WHERE clause for this row
$where_conditions = [];
foreach ( $primary_keys as $key ) {
$where_conditions[ $key ] = $row->$key;
}

// Update the row
$updated = $wpdb->update(
$table,
[ $column => $new_value ],
$where_conditions
$where
);

if ( $updated ) {
Expand Down Expand Up @@ -403,13 +421,7 @@ private function replace_urls_in_value( $value, $old_url, $new_url ) {
}
}

// Handle image sizes for non-serialized content
if ( $this->handle_image_sizes ) {
return $this->replace_image_urls( $value, $old_url, $new_url );
}

// Simple string replacement for non-serialized data
return str_replace( $old_url, $new_url, $value );
return $this->replace_image_urls( $value, $old_url, $new_url );
}

/**
Expand Down Expand Up @@ -444,8 +456,8 @@ private function replace_image_urls( $content, $old_url, $new_url ) {
$new_ext = isset( $new_file_info['extension'] ) ? $new_file_info['extension'] : $old_ext;

// Define domain parts
$old_domain = isset( $old_path_parts['host'] ) ? 'http' . ( isset( $old_path_parts['scheme'] ) && $old_path_parts['scheme'] === 'https' ? 's' : '' ) . '://' . $old_path_parts['host'] : '';
$new_domain = isset( $new_path_parts['host'] ) ? 'http' . ( isset( $new_path_parts['scheme'] ) && $new_path_parts['scheme'] === 'https' ? 's' : '' ) . '://' . $new_path_parts['host'] : '';
$old_domain = $this->get_origin( $old_path_parts );
$new_domain = $this->get_origin( $new_path_parts );

// Replace original URLs
$content = str_replace( $old_url, $new_url, $content );
Expand Down Expand Up @@ -537,12 +549,7 @@ private function replace_in_data( $data, $old_url, $new_url ) {
$data->$key = $this->replace_in_data( $value, $old_url, $new_url );
}
} elseif ( is_string( $data ) ) {
// Replace URLs in strings
if ( $this->handle_image_sizes ) {
$data = $this->replace_image_urls( $data, $old_url, $new_url );
} else {
$data = str_replace( $old_url, $new_url, $data );
}
$data = $this->replace_image_urls( $data, $old_url, $new_url );
}

return $data;
Expand Down
12 changes: 0 additions & 12 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2418,18 +2418,6 @@ parameters:
count: 1
path: inc/media_rename/attachment_db_renamer.php

-
message: '#^Method Optml_Attachment_Db_Renamer\:\:php_handle_column\(\) has parameter \$primary_keys with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: inc/media_rename/attachment_db_renamer.php

-
message: '#^Method Optml_Attachment_Db_Renamer\:\:process_column\(\) has parameter \$primary_keys with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: inc/media_rename/attachment_db_renamer.php

-
message: '#^Property Optml_Attachment_Db_Renamer\:\:\$skip_columns type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
Expand Down
Loading
Loading