class-mysqldumpwriter.php
2 weeks ago
class-staticpostfileswriter.php
2 weeks ago
class-wxrwriter.php
2 weeks ago
interface-entity-writer.php
2 weeks ago
class-mysqldumpwriter.php
108 lines
| 1 | <?php |
| 2 | |
| 3 | namespace WordPress\DataLiberation\EntityWriter; |
| 4 | |
| 5 | use WordPress\ByteStream\WriteStream\ByteWriteStream; |
| 6 | use WordPress\DataLiberation\DataLiberationException; |
| 7 | use WordPress\DataLiberation\ImportEntity; |
| 8 | |
| 9 | class MySQLDumpWriter implements EntityWriter { |
| 10 | |
| 11 | private $write_stream; |
| 12 | private $is_closed = false; |
| 13 | |
| 14 | public function __construct( ByteWriteStream $write_stream ) { |
| 15 | $this->write_stream = $write_stream; |
| 16 | } |
| 17 | |
| 18 | public function append_entity( ImportEntity $entity ) { |
| 19 | if ( $this->is_closed ) { |
| 20 | throw new DataLiberationException( 'Cannot write to a closed writer' ); |
| 21 | } |
| 22 | |
| 23 | switch ( $entity->get_type() ) { |
| 24 | case 'sql_query': |
| 25 | $this->write_stream->append_bytes( $entity->get_data() . "\n" ); |
| 26 | break; |
| 27 | case 'database_row': |
| 28 | $data = $entity->get_data(); |
| 29 | $table = $data['table']; |
| 30 | $record = $data['record']; |
| 31 | |
| 32 | $columns = implode( ', ', array_keys( $record ) ); |
| 33 | $values = implode( ', ', array_map( array( $this, 'quote_value' ), array_values( $record ) ) ); |
| 34 | |
| 35 | $this->write_stream->append_bytes( "INSERT INTO $table ($columns) VALUES ($values);\n" ); |
| 36 | break; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | private function quote_value( $value ) { |
| 41 | if ( is_null( $value ) ) { |
| 42 | return 'NULL'; |
| 43 | } |
| 44 | if ( is_string( $value ) ) { |
| 45 | return "'" . $this->escape_string( $value ) . "'"; |
| 46 | } |
| 47 | if ( is_bool( $value ) ) { |
| 48 | return $value ? '1' : '0'; |
| 49 | } |
| 50 | |
| 51 | return $value; |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * @TODO: This is AI-generated. It's okay for the demo, but before |
| 56 | * any production application we should review this with @dmsnell |
| 57 | * and reach mysql_real_escape_string() parity without any server |
| 58 | * round-trips. |
| 59 | * |
| 60 | * Escapes special characters in a string for use in a MySQL query. |
| 61 | * |
| 62 | * Only escapes characters that need escaping when using single quotes: |
| 63 | * - Backslash (\) -> \\ |
| 64 | * - Single quote (') -> \' |
| 65 | * - Control chars like null byte, newline etc are escaped as hex |
| 66 | */ |
| 67 | private function escape_string( $value ) { |
| 68 | $result = ''; |
| 69 | $len = strlen( $value ); |
| 70 | |
| 71 | for ( $i = 0; $i < $len; $i++ ) { |
| 72 | $char = $value[ $i ]; |
| 73 | $ord = ord( $char ); |
| 74 | |
| 75 | // Control characters need hex escaping. |
| 76 | if ( $ord < 32 || 0x7F === $ord ) { |
| 77 | $result .= sprintf( '\\x%02x', $ord ); |
| 78 | continue; |
| 79 | } |
| 80 | |
| 81 | // Only need to escape backslash and single quote. |
| 82 | switch ( $char ) { |
| 83 | case '\\': |
| 84 | $result .= '\\\\'; |
| 85 | break; |
| 86 | case "'": |
| 87 | $result .= "\\'"; |
| 88 | break; |
| 89 | default: |
| 90 | $result .= $char; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | return $result; |
| 95 | } |
| 96 | |
| 97 | public function close_writing() { |
| 98 | if ( ! $this->is_closed ) { |
| 99 | $this->write_stream->close_writing(); |
| 100 | $this->is_closed = true; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | public function get_reentrancy_cursor(): string { |
| 105 | return ''; // Not needed for this writer. |
| 106 | } |
| 107 | } |
| 108 |