| @@ -7,8 +7,10 @@ | ||
| 7 | 7 | * @author Tobias Bäthge |
| 8 | 8 | * @since 2.0.0 |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +declare(strict_types=1); | |
| 12 | + | |
| 11 | 13 | // Prohibit direct script loading. |
| 12 | 14 | defined( 'ABSPATH' ) || die( 'No direct script access allowed!' ); |
| 13 | 15 | |
| 14 | 16 | /** |
| @@ -26,10 +28,10 @@ | ||
| 26 | 28 | * |
| 27 | 29 | * @since 2.0.0 |
| 28 | 30 | */ |
| 29 | 31 | public function __construct() { |
| 30 | - // Load PHPSpreadsheet via the Composer autoloading mechanism. | |
| 31 | - TablePress::load_file( 'autoload.php', 'libraries' ); | |
| 32 | + // Load PHPSpreadsheet via its autoloading mechanism. | |
| 33 | + TablePress::load_file( 'autoload.php', 'libraries/vendor' ); | |
| 32 | 34 | } |
| 33 | 35 | |
| 34 | 36 | /** |
| 35 | 37 | * Evaluates formulas in the passed table. |
| @@ -35,13 +37,13 @@ | ||
| 35 | 37 | * Evaluates formulas in the passed table. |
| 36 | 38 | * |
| 37 | 39 | * @since 1.0.0 |
| 38 | 40 | * |
| 39 | - * @param array $table_data Table data in which formulas shall be evaluated. | |
| 40 | - * @param string $table_id ID of the passed table. | |
| 41 | - * @return array Table data with evaluated formulas. | |
| 41 | + * @param array<int, array<int, string>> $table_data Table data in which formulas shall be evaluated. | |
| 42 | + * @param string $table_id ID of the passed table. | |
| 43 | + * @return array<int, array<int, string>> Table data with evaluated formulas. | |
| 42 | 44 | */ |
| 43 | - public function evaluate_table_data( array $table_data, $table_id ) { | |
| 45 | + public function evaluate_table_data( array $table_data, string $table_id ): array { | |
| 44 | 46 | $table_has_formulas = false; |
| 45 | 47 | |
| 46 | 48 | // Loop through all cells to check for formulas and convert notations. |
| 47 | 49 | foreach ( $table_data as &$row ) { |
| @@ -51,13 +53,13 @@ | ||
| 51 | 53 | } |
| 52 | 54 | |
| 53 | 55 | $table_has_formulas = true; |
| 54 | 56 | |
| 55 | - // Convert legacy "formulas in text" notation to standard Excel notation (`=Text {A3+B3} Text` => `="Text "&A3+B3&" Text"`). | |
| 56 | - $count = 0; | |
| 57 | - $cell_content = preg_replace( '#{(.+?)}#', '"&$1&"', $cell_content, -1, $count ); | |
| 58 | - if ( $count > 0 ) { | |
| 59 | - $cell_content = '="' . substr( $cell_content, 1 ) . '"'; | |
| 57 | + // Convert legacy "formulas in text" notation (`=Text {A3+B3} Text`) to standard Excel notation (`="Text "&A3+B3&" Text"`). | |
| 58 | + if ( 1 === preg_match( '#{(.+?)}#', $cell_content ) ) { | |
| 59 | + $cell_content = str_replace( '"', '""', $cell_content ); // Preserve existing quotation marks in text around formulas. | |
| 60 | + $cell_content = '="' . substr( $cell_content, 1 ) . '"'; // Wrap the whole cell content in quotation marks, as there will be text around formulas. | |
| 61 | + $cell_content = (string) preg_replace( '#{(.+?)}#', '"&$1&"', $cell_content, -1, $count ); // Convert all wrapped formulas to standard Excel notation. | |
| 60 | 62 | } |
| 61 | 63 | } |
| 62 | 64 | } |
| 63 | 65 | unset( $row, $cell_content ); // Unset use-by-reference parameters of foreach loops. |
| @@ -72,9 +74,9 @@ | ||
| 72 | 74 | $worksheet = $spreadsheet->setActiveSheetIndex( 0 ); |
| 73 | 75 | $worksheet->fromArray( /* $source */ $table_data, /* $nullValue */ '' ); |
| 74 | 76 | |
| 75 | 77 | // Don't allow cyclic references. |
| 76 | - TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance( $spreadsheet )->cyclicFormulaCount = 0; | |
| 78 | + \TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance( $spreadsheet )->cyclicFormulaCount = 0; | |
| 77 | 79 | |
| 78 | 80 | /* |
| 79 | 81 | * Register variables as Named Formulas. |
| 80 | 82 | * The variables `ROW`, `COLUMN`, `CELL`, `PI`, and `E` should be considered deprecated and only their formulas should be used. |
| @@ -100,9 +102,22 @@ | ||
| 100 | 102 | if ( $cell_collection->has( $cell_reference ) ) { |
| 101 | 103 | $cell = $cell_collection->get( $cell_reference ); |
| 102 | 104 | try { |
| 103 | 105 | $cell_content = (string) $cell->getCalculatedValue(); |
| 104 | - } catch ( \TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception $exception ) { | |
| 106 | + | |
| 107 | + // Convert hyperlinks, e.g. generated via `=HYPERLINK()` to HTML code. | |
| 108 | + $cell_has_hyperlink = $worksheet->hyperlinkExists( $cell_reference ) && ! $worksheet->getHyperlink( $cell_reference )->isInternal(); | |
| 109 | + if ( $cell_has_hyperlink ) { | |
| 110 | + $url = $worksheet->getHyperlink( $cell_reference )->getUrl(); | |
| 111 | + if ( '' !== $url ) { | |
| 112 | + $url = esc_url( $url ); | |
| 113 | + $cell_content = "<a href=\"{$url}\">{$cell_content}</a>"; | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + // Sanitize the output of the evaluated formula. | |
| 118 | + $cell_content = wp_kses_post( $cell_content ); // Equals wp_filter_post_kses(), but without the unnecessary slashes handling. | |
| 119 | + } catch ( \Throwable $exception ) { | |
| 105 | 120 | $message = str_replace( 'Worksheet!', '', $exception->getMessage() ); |
| 106 | 121 | $cell_content = "!ERROR! {$message}"; |
| 107 | 122 | } |
| 108 | 123 | } |
| @@ -113,9 +128,9 @@ | ||
| 113 | 128 | |
| 114 | 129 | // Save PHP memory. |
| 115 | 130 | $spreadsheet->disconnectWorksheets(); |
| 116 | 131 | unset( $cell_collection, $worksheet, $spreadsheet ); |
| 117 | - } catch ( \TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception $exception ) { | |
| 132 | + } catch ( \Throwable $exception ) { | |
| 118 | 133 | $message = str_replace( 'Worksheet!', '', $exception->getMessage() ); |
| 119 | 134 | $table_data = array( array( "!ERROR! {$message}" ) ); |
| 120 | 135 | } |
| 121 | 136 | |