- PVSM.RU - https://www.pvsm.ru -
Не секрет, что зачастую PHP-программистам приходится решать задачи, весьма далёкие от бытового представления о «веб-разработке». Развитие языка в последние годы привело к тому, что PHP всё чаще считают языком общего назначения, пригодным не только для сайтов, но и для других задач.
Одну из таких «других» задач мне с коллективом пришлось решать совсем недавно. Мы решили поделиться «картой граблей» с теми, кто, возможно, пойдет по этому же пути.
Некоторое время нам хватало возможностей популярной библиотеки PHPExcel. Но когда от бизнеса поступило очередное требование «нужно, чтобы работали макросы, и еще бы хорошо всё это сохранять в PDF», стало понятно, что выбранный путь — тупиковый. Нужно не парсить файлы xlsx, не имитировать просчёт, и даже не использовать Open Office, а научиться взаимодействовать с «настоящим» Microsoft Excel.
В результате недолгих изысканий было решено создать внутренний микро-веб-сервис, который умел бы принять данные, открыть указанный файл Excel, вставить в него принятые данные, просчитать результат и выдать его в качестве ответа клиенту. Заинтересованность в таком веб-сервисе выразили несколько внутренних проектов и работа закипела.
Первым под удар попал отдел dev-ops. Им предстояло подготовить сервер для будущего сервиса. Дело было необычным, поскольку актуального опыта работы с Windows ни у кого нет…
В качестве серверной платформы был выбран Windows Server 2012 R2 standart. Нужно сразу отметить, что «из коробки» Windows совершенно не приспособлена к
Для начала был установлен PowerShellServer [2]. Это позволило нам подключаться к windows-серверу по привычному всем протоколу ssh, не изобретая велосипедов. Поддерживается авторизация по ключам, работает rsync (это важно). Жаль, что в Personal Edition ограничение только на одно одновременное подключение, но для нас это некритично.
Nginx был установлен штатным образом. Взят со страницы nginx.org/ru/download.html [3] Имейте в виду — под windows есть существенные ограничения: только один рабочий процесс, который держит не более 1024 соединений. Впрочем, это опять же было некритично для внутреннего микро-сервиса.
PHP 7.0.9 взят с windows.php.net/download [4], установлен штатным образом.
Для упрощения перезапуска всего этого «добра» был написан несложный cmd-файл:
cd C:nginx
taskkill /f /IM nginx.exe
taskkill /f /IM php-cgi.exe
rm C:nginxlogs*
start nginx
start -WindowStyle Hidden phpphp-cgi -A "-b 0.0.0.0:9000 -c C:serverphpphp.ini"
Первоначальная настройка сервера закончилась успешным выводом страницы с phpinfo(). Однако это было еще только самое начало…
Мы внутри компании используем Continuous Integration. Всегда. Для любого, сколь угодно малого проекта. Примерный план развертывания выглядит так:
Что потребовалось далее? ssh-сервер уже установлен, rsync выполняется корректно. Установим phing:
Git for Windows берем с git-scm.com [7], устанавливаем, проверяем корректную работу.
Точно по такой же схеме поступаем с composer, только bat-файл пишем сами и он будет значительно проще:
@echo off
if "%PHPBIN%" == "" set PHPBIN=C:serverphpphp.exe
"%PHPBIN%" "C:nginxphpcomposer.phar" %*
Вроде бы всё готово. Запускаем сборку… fail!
Причина 1. Нужно установить расширение php_openssl.dll, иначе Phing не сможет работать с репозиториями через SSL. Проблем не доставило.
Причина 2. Более серьезная. В нашем сценарии сборки используется техника переключения симлинка на папку со свежей сборкой на последнем шаге. Примерно так:
<symlink target="${current.dir}" link="${home.dir}/${build.branch}/current" overwrite="true" />
В результате получается что-то вроде
symlink: "c:serverdomainsthis.servicemastercurrent" => "c:serverdomainsthis.servicemaster2016-04-01-12-34-56"
Оказалось, что создать символическую ссылку на NTFS — не проблема. Проблема ее удалить… Отчего-то операция удаления симлинка требует прав администратора, которых у обычного PHP нет и быть не может.
Нам помогла утилита junction ( technet.microsoft.com/en-us/sysinternals/bb896768 [8] ). С ней вышеуказанный кусок сценария стал выглядеть примерно так:
<exec command="junction -d ${home.dir}/${build.branch}/current" checkreturn="true" passthru="true" />
<symlink target="${current.dir}" link="${home.dir}/${build.branch}/current" overwrite="true" />
Итак, всё встало на свои места, сборка заработала, как ей и положено. Настала пора писать код!
Надо отметить, что собственно код сервиса не доставил никаких проблем.
Как запустить приложение Microsoft Excel и загрузить в приложение существующий файл?
namespace AppComponents;
class Excel
{
protected $xls;
public function __construct($filename = null)
{
$this->xls = new COM("Excel.Application");
// @todo: выключить, если не требуется видеть работу приложения
$this->xls->Application->Visible = 1;
$this->xls->DisplayAlerts = 0;
if (empty($filename)) {
$this->xls->Workbooks->Add();
} else {
$this->xls->Workbooks->Open($filename);
}
$this->xls->Workbooks[1]->Activate();
}
}
Как закрыть приложение после окончания работы скрипта?
public function __destruct()
{
$this->xls->Workbooks[1]->Close(false);
$this->xls->Quit();
}
Установить значение ячейки или диапазона?
public function setValue($range, $value)
{
$this->xls->Range($range)->Value = iconv('UTF-8', 'Windows-1251', $value);
}
Прочесть значение из ячейки или диапазона?
public function getValue($range)
{
return iconv('Windows-1251', 'UTF-8', $this->xls->Range($range)->Value);
}
Экспортировать книгу в PDF?
const FORMATS = [
'PDF' => 0
];
public function saveAs($filename, $format = self::FORMATS['PDF'])
{
// Будь проклят тот день, когда разработчики MS-DOS придумали обратные слэши!
$this->xls->Workbooks[1]->ExportAsFixedFormat($format, str_replace('/', '\', $filename));
}
Что надо сделать, чтобы вся эта безумная магия заработала?
Добавить расширение php_com_dotnet.dll
Довольно феерично наблюдать за сервером: при приходе запроса мгновенно открывается Excel, запускается экспорт и потом также мгновенно всё это хозяйство закрывается.
Удачи и не наступайте на те же грабли!
Автор: AlexLeonov
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/162615
Ссылки в тексте:
[1] хостингу: https://www.reg.ru/?rlink=reflink-717
[2] PowerShellServer: http://www.powershellserver.com
[3] nginx.org/ru/download.html: http://nginx.org/ru/download.html
[4] windows.php.net/download: http://windows.php.net/download
[5] www.phing.info/trac/wiki/Users/Installation: https://www.phing.info/trac/wiki/Users/Installation
[6] www.phing.info/trac/browser/bin/phing.bat: https://www.phing.info/trac/browser/bin/phing.bat
[7] git-scm.com: https://git-scm.com/
[8] technet.microsoft.com/en-us/sysinternals/bb896768: https://technet.microsoft.com/en-us/sysinternals/bb896768
[9] php.net/manual/en/book.com.php: http://php.net/manual/en/book.com.php
[10] msdn.microsoft.com/ru-ru/library/wss56bz7.aspx: https://msdn.microsoft.com/ru-ru/library/wss56bz7.aspx
[11] geektimes.ru/post/50878: https://geektimes.ru/post/50878/
[12] Источник: https://habrahabr.ru/post/306408/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.