FastExcelWriter — избавление от проклятия PhpSpreadsheet

Наверняка каждый php-разработчик, хоть раз сталкивавшийся с чтением или записью Excel-файлов, знает про библиотеку PhpSpreadsheet (в прошлом - PHPExcel). Это мощная библиотека на чистом php, которая позволяет читать, а, главное, создавать Excel-таблицы. И все с ней хорошо, пока работаешь с небольшим набором данных. Но при работе с большими файлами PhpSpreadsheet начинает чудовищно отжирать память, да и производительность резко падает, и php-скрипт, использующий библиотеку, часто просто отваливается по таймауту. И проблема - в архитектуре библиотеки.

Библиотека PhpSpreadsheet, казалось бы, спроектирована очень правильно: листы таблицы, строки, ячейки, прочие сущности - это все классы, стили, форматы и всевозможные свойства тоже состоят из классов. И когда создается таблица, то в памяти создается огромное количество связанных между собой объектов, им задаются свойства, выполняются всевозможные манипуляции, ячейки заполняются данными, всё-всё это держится в памяти, и пишется в файл только в момент сохранения.

И вот однажды, когда ресурсы на генерацию большого Excel-файла превысили все разумные значения, я начал искать альтернативу известной библиотеке. Альтернатива нашлась. В отличие от PhpSpreadsheet, тут все было упаковано в один класс, возможности по форматированию и стилям - скромнее некуда, но зато XLSX-файл создавался в разы быстрее, а потребление памяти было вообще копеечное. А все потому, что тут был реализован совсем иной принцип - данные не накапливались в памяти, а построчно сразу выводились в файл. Вдохновившись этой идеей, я и создал свою библиотеку FastExcelWriter.

Я сохранил тот же принцип - данные пишутся, практически, сразу во временный XML-файл, который потом добавляется в итоговый XLSX-файл (на всякий случай напомню, что XLSX - это ZIP-архив с XML-файлами), активно использую массивы вместо объектов (что тоже дает прирост скорости). Разумеется, я использую классы, но без фанатизма, по минимуму.

На сегодняшний день библиотека FastExcelWriter - одна из самых быстрых (и экономных по потреблению памяти) для создания XLSX-файлов на php, скорость генерации файлов с ее помощью у меня получалась в 7-9 раз выше, чем с PhpSpreadsheet.

При этом она поддерживает почти все возможности, включая стили оформления, условное форматирование, вставку изображений, диаграмм, примечаний, валидацию данных. Кроме того, интерфейс взаимодействия с библиотекой получился гораздо более лаконичным. Ниже приведены для сравнения два фрагмента добавления условного форматирования

Скрытый текст

Код с использованием PhpSpreadsheet

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();
$conditional = new Conditional();
$conditional->setConditionType(Conditional::CONDITION_CELLIS);
$conditional->setOperatorType(Conditional::OPERATOR_GREATERTHAN);
$conditional->addCondition(80);
$conditional->getStyle()->getFont()->getColor()->setARGB(Color::COLOR_DARKGREEN);
$conditional->getStyle()->getFill()->setFillType(Fill::FILL_SOLID);
$conditional->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_GREEN);

$conditionalStyles = $spreadsheet->getActiveSheet()->getStyle('A1:A10')->getConditionalStyles();
$conditionalStyles[] = $conditional;

$spreadsheet->getActiveSheet()->getStyle('A1:A10')->setConditionalStyles($conditionalStyles);

$writer = new Xlsx($spreadsheet);
$writer->save('demo.xlsx');

Код с использованием FastExcelWriter

use avadim\FastExcelWriter\Conditional\Conditional;
use avadim\FastExcelWriter\Excel;

$excel = Excel::create();
$style = Excel::newStyle()->setFontColor('#008000')->setFillColor('#00ff00');
$conditional = Conditional::greaterThan(80, $style->toArray());
$excel->sheet()->addConditionalFormatting('A1:A10', [$conditional]);

$excel->save('demo.xlsx');

Конечно, с помощью библиотеки можно записывать в ячейки и формулы, но есть нюанс: значения формул не высчитываются и не записываются в файл. Но когда созданный файл открывается потом в соответствующей программе (MS Excel, Libre Office и пр.), то эти программы все рассчитают и отобразят значения налету.

А что насчет чтения XLSX-файлов? - спросите вы. А для этого есть аналогичная библиотека - FastExcelReader, и она тоже в разы быстрее читает файлы, чем та же PhpSpreadsheet.

А еще есть FastExcelLaravel - обертка, которая включает в себя сразу обе библиотеки (FastExcelWriter и FastExcelReader) и содержит дополнительные методы для импорта и экспорта моделей Laravel.

И все это — оупенсорс. Берите, пользуйтесь, наслаждайтесь!

@vshemarov
26.04.2025 22:55 UTC
Первоисточник

Комментарии

@aleksandr-s-zelenin
27.04.2025 09:18 UTC
+2

Пользовался вашей либой. И для чтения и для записи, всё отлично работает, спасибо! )

@SamDark
27.04.2025 13:40 UTC
+2

Интересно, а с https://github.com/box/spout сравнивали? Я на неё спрыгнул с FastExcelWriter как-то.

@vshemarov
27.04.2025 14:45 UTC
+3

Сравнивал с его форком https://github.com/openspout/openspout (оригинальный spout давно же не поддерживается), и у меня получалось и по фичам богаче, и по скорости выше (хотя, давно не делал бенчмарков, вдруг что-то изменилось).

Интересно было бы узнать, что не устроило в FastExcelWriter.

28.04.2025 08:21 UTC
+1

А его тогда не было, наверное, когда я перебирался. Попробую обязательно. Спасибо.

@StunIsLove
30.04.2025 15:24 UTC
+1

Надо на досуге подербанить наш проект, посравнивать перфомансы)

Если результат окажется кратно лучше, буду подбивать команду и тех дира на переход. Однако тут же возникает другая проблема. Думаю, не мало проектов напрямую в своих отчётах на уровне контроллеров и сервисов зависят от PhpSpreadsheet. И переход, как минимум, будет болезненный. А в худшем случае - не целесообразным по затратам для бизнеса)

@vshemarov
30.04.2025 16:13 UTC
0

Да, генерацию отчетов придется переписывать. Но затраты будут зависеть от того, как много фич используется в отчете, если какие-то сложные стили, условия, графики, то переписывать, конечно, много придется. А если просто объемные таблицы без особых наворотов, то переход будет не такой уж и затратный.

@GitVerse
14.05.2025 06:38 UTC
0

Привет, это команда GitVerse! Рады видеть тебя в числе участников сезона open source! Ставим лайк твоей статье :)