' ) ) {
return new WP_Error( 'table_import_html_no_table_found' );
}
// Prepend XML declaration, for better encoding support.
$full_html = '' . $html;
if ( function_exists( 'libxml_disable_entity_loader' ) ) {
/*
* Don't expand external entities, see https://websec.io/2012/08/27/Preventing-XXE-in-PHP.html.
* Silence warnings as the function is deprecated in PHP 8, but can be necessary with LIBXML_NOENT being defined, see https://core.trac.wordpress.org/changeset/50714.
*/
@libxml_disable_entity_loader( true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
// No warnings/errors raised, but stored internally.
libxml_use_internal_errors( true );
$dom = new DOMDocument( '1.0', 'UTF-8' );
// No strict checking for invalid HTML.
$dom->strictErrorChecking = false; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$dom->loadHTML( $full_html );
if ( false === $dom ) {
return new WP_Error( 'table_import_html_dom_load_html_failed' );
}
$dom_tables = $dom->getElementsByTagName( 'table' );
if ( 0 === count( $dom_tables ) ) {
return new WP_Error( 'table_import_html_dom_get_tables' );
}
libxml_clear_errors(); // Clear errors so that we only catch those inside the table in the next line.
$table = simplexml_import_dom( $dom_tables->item( 0 ) );
if ( false === $table ) {
return new WP_Error( 'table_import_html_simplexml_import_dom_failed' );
}
$errors = libxml_get_errors();
libxml_clear_errors();
if ( ! empty( $errors ) ) {
$output = '' . __( 'The imported file contains errors:', 'tablepress' ) . '
';
foreach ( $errors as $error ) {
switch ( $error->level ) {
case LIBXML_ERR_WARNING:
$output .= "Warning {$error->code}: {$error->message} in line {$error->line}, column {$error->column}
";
break;
case LIBXML_ERR_ERROR:
$output .= "Error {$error->code}: {$error->message} in line {$error->line}, column {$error->column}
";
break;
case LIBXML_ERR_FATAL:
$output .= "Fatal Error {$error->code}: {$error->message} in line {$error->line}, column {$error->column}
";
break;
}
}
wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
}
$html_table = array(
'data' => array(),
'options' => array(),
);
if ( isset( $table->thead ) ) {
$html_table['data'] = array_merge( $html_table['data'], self::_import_html_rows( $table->thead[0]->tr ) );
$html_table['options']['table_head'] = true;
}
if ( isset( $table->tbody ) ) {
$html_table['data'] = array_merge( $html_table['data'], self::_import_html_rows( $table->tbody[0]->tr ) );
}
if ( isset( $table->tr ) ) {
$html_table['data'] = array_merge( $html_table['data'], self::_import_html_rows( $table->tr ) );
}
if ( isset( $table->tfoot ) ) {
$html_table['data'] = array_merge( $html_table['data'], self::_import_html_rows( $table->tfoot[0]->tr ) );
$html_table['options']['table_foot'] = true;
}
return $html_table;
}
/**
* Converts table HTML rows to an array.
*
* @since 2.0.0
*
* @param SimpleXMLElement $element XMLElement.
* @return array SimpleXMLElement exported to an array.
*/
protected static function _import_html_rows( $element ) {
$rows = array(); // Container for the table data.
$rowspans = array(); // Container for information about rowspans in rows that follow the currently processed row.
$row_idx = 0;
foreach ( $element as $row ) {
// If all cells in a row should be merged with the cells in the row above, add the trigger word to each of them (should be very rare).
while ( isset( $rowspans[ $row_idx ] ) && count( $rowspans[ $row_idx ] ) === count( $rows[ $row_idx - 1 ] ) ) {
$rows[] = $rowspans[ $row_idx ];
++$row_idx;
}
$new_row = array();
$column_idx = 0;
foreach ( $row as $cell ) {
// If a cell in a row should be merged with the cell above it, add the trigger word to it.
while ( isset( $rowspans[ $row_idx ][ $column_idx ] ) ) {
$new_row[] = $rowspans[ $row_idx ][ $column_idx ];
++$column_idx;
}
$cell_xml = $cell->asXml();
// Get content between