Что значит обновление скрипта на компьютере
Автоматическое обновление скриптов после деплоя
Добрый день, уважаемые читатели. Эта статья посвящена проблеме, с которой сталкиваются разработчики более-менее серьезных веб-сайтов, а именно – проблеме автоматического обновления скриптов в браузере пользователя после деплоя.
Суть проблемы заключается в том, что одностраничное приложение, загруженное в браузере пользователя, не знает о том, что скрипты были только что изменены, и, следовательно, необходимо обновиться. С точки зрения пользователя это выглядит как непонятные ошибки, возникающие на ровном месте, или как отказ приложения выполнять свои функции. Все это – следствие того, что версия скриптов на сервере изменилась, и приложение просто не может их найти и загрузить.
Конечно, все решается нажатием на F5, но в данной статье я покажу, как можно автоматизировать это действие, избавить пользователя от головной боли и сделать это красиво.
О статье
Данная статья представляет из себя туториал и предназначена как для опытных разработчиков, так и для новичков.
Решение, представленное здесь, разработано для VueJS версии 2.6.x, но сами алгоритмы легко могут быть портированы на любой js-фреймворк на основе webpack’а.
Статья имеет веб-интерфейс, где вы сможете пощупать решение своими руками, а также исходники на github.
Ссылки для ленивых:
Также, в качестве эксперимента, в статью будет встроено два бонуса. В бонусах я решил добавить полезную на мой взгляд информацию, которая сама по себе не достойна отдельной статьи, но о которой хотелось бы рассказать.
Идея решения
Идея решения заключается в введении версионности веб-приложения на уровне сборки.
Каждый раз при сборке мы сохраняем информацию о времени сборки, а во время работы приложения проверяем, изменилось ли это время. В случае изменения, обновляем страницу, попутно предупреждая пользователя об изменениях.
Реализация
Решение представлено в виде плагина для Vue, который находится в коде проекта по пути /src/plugins/AutoReload. Отдельного пакета для npm не делал.
Демонстрационное веб-приложение представляет коробочную заготовку vue-cli, в которую добавлены стандартные фичи: store, роутер, axios для веб-запросов и dayjs для работы с датами. Интерфейс построен на Element.
Версионность сборки
Первая задача – сохранение информации о версии в сборке. Данную задачу можно выполнять разными способами:
Итак, рассмотрим последний вариант в контексте VueJS и Webpack. Как известно, сборщик webpack выполняет серверные js-скрипты, которые собственно и выполняют сборку. В них мы можем встроить свой код, который будет формировать специальный файл, скажем version.json, который будет содержать полезную для нас информацию.
В контексте нашей задачи достаточно записать туда дату сборки, но мы пойдем чуть дальше и добавим информацию о типе сборки (development/production/etc.) и версию проекта из файла package.json. Получим примерно такой файл:
Чтобы сформировать данный файл, нужно встроить код в скрипт vue.config.js, вот так:
Далее код самого генератора version.json:
Теперь каждый раз при сборке приложения через команды serve, build или другие будет формироваться новая версия файла version.json, которая может быть прочитана приложением через обычный get-запрос, что нам и требуется.
Примечание №1. Файл version.json помещается в папку public, содержимое которой в VueJS копируется в выходной каталог «как есть». В других фреймворках может потребоваться другое целевое расположение.
Примечание №2. Файл version.json нужно исключить из git’а, чтобы его изменение не вызывало конфликтов при командной разработке, ведь изменяться он будет постоянно даже в процессе отладки.
Плагин AutoReload
Исходя из идеи решения плагин будет выполнять следующие функции:
Конфигурация плагина
Любой уважающий себя плагин должен иметь возможности для настройки. Наш плагин не является исключением, хотя его настройки ограничены рамками задачи, для которой он разрабатывался:
Код файла конфигурации можно найти в исходниках или под спойлером.
Использование плагина
Данный плагин имеет одну особенность, – он использует роутер и уведомления Element’а, поэтому его нужно включать в методе create главного компонента Vue:
Разбор кода плагина
Главной функцией является check с опциональным параметром href. Именно эта функция вызывается по таймеру или при переходе по маршруту, в этом случае передается адрес целевой страницы.
Функция получает текущую версию сборки из файла version.json и сравнивает дату сборки с ранее сохраненным значением. Если значение отличается, пользователю показывается уведомление, затем сохраняется новое значение версии сборки и происходит перезагрузка страницы.
Если обновление происходит по таймеру, эмулируется нажатие F5 (window.location.reload(true)). Если же обновление происходит при переходе по маршруту, пользователь направляется на целевую страницу маршрута. Это важно, т.к. исполнение кода никогда не дойдет до next() в хуке роутера после обновления страницы.
В принципе на этом статью можно было бы закончить, но мы пойдем чуть дальше и воспользуемся побочными эффектами нашего решения.
Бонус №1. Использование информации о версии сборки в целях отладки
Я не случайно добавил в файл version.json дополнительную информацию о версии проекта и типе сборки. В реальном проекте эта информация выводилась на специальной «скрытой» странице, в которую можно было попасть, указав ее путь в URL. В демонстрационном проекте страничка с информацией о сборке находится прямо в меню, и там можно увидеть содержимое файла version.json в удобном для человека виде.
Это может быть полезно, когда проект имеет кучу версий и веток, которые выкладываются на отдельные стенды. Посмотрев на эту страницу можно увидеть, когда проект был обновлен, и какой тип сборки там использовался.
Также туда можно добавить информацию о бэкенде, но в этом проекте его нет.
Прошу обратить внимание, что информация об интервале обновления формируется в соответствии с правилами русского языка: «60 секунд», а не «60 секунды». Программисты практически всегда пренебрегают такими мелочами, хотя решение лежит на поверхности и не требует глубоких знаний. Именно этому вопросу посвящен второй бонус статьи, под спойлером.
Исходное решение было написано на C# много лет назад, а затем портировано на JS почти в неизменном виде. Я приведу код обоих решений и примеры использования.
Анализ решения
У любого решения есть плюсы и минусы, а также свои особенности и, конечно, простор для дальнейших доработок. Здесь проведу краткий анализ своего решения в плане его ограничений и доработок.
Проблема распределенного приложения
Если приложение работает через балансер, и сборка происходит на каждом из веб-серверов отдельно, то, очевидно, версия сборок будет отличаться минутами или секундами. Поэтому, если пользователь по какой-то причине будет перекинут на другой веб-сервер, у него произойдет обновление скриптов из-за различий во времени сборки.
Является ли это проблемой? Скорее нет, чем да, потому что балансер, как правило, настроен таким образом, чтобы пользователь всегда направлялся на один из вебов, а не прыгал между ними.
Если все-таки такая проблема актуальна для вас, я вижу два варианта решения: сборка на одном сервере или введение проверки на разность дат сборок, чтобы она превышала некий лимит.
Проблема асинхронных компонентов
В плагине решена проблема подгрузки асинхронных маршрутов, но остается нерешенной проблема, когда пользователь производит действие, вызывающее подгрузку асинхронного компонента. С точки зрения браузера это загрузка обычного js-файла с сервера. Но после деплоя и до срабатывания автоматического обновления браузер будет пытаться загрузить несуществующий файл.
У данной проблемы нет нормального решения, хотя есть вариант с перехватом/оборачиванием кода подтягивания асинхронных компонентов, чтобы в этот момент также вызывался код проверки изменения версии сборки. В любом случае, максимум, что возможно будет сделать – перезагрузить страницу целиком.
Проблема потери данных
Возможна ситуация, когда пользователь заполняет форму, и в процессе заполнения происходит обновление страницы. Данные будут потеряны, равно как и всё содержимое store. С другой стороны, есть гарантия, что пользователь не попытается загрузить старую версию формы.
Как вариант, можно не инициировать обновление страницы, а давать пользователю выбор: продолжить работу или обновить страницу.
Проблема публикации списка изменений
Честно говоря, не припомню сайты, которые публикуют какие-либо changelog’и, но было бы интересно внедрить такой функционал в модуль автообновления. Хотя лично меня всегда бесит, когда тот же телеграм пишет мне в личку список новых возможностей. Каждый раз я удаляю тот чат, но он все равно оживает при следующем обновлении. А для веба это, наверное, и не нужно вовсе.
Плагин в действии
Если вы хотите увидеть плагин в действии, воспользуйтесь демонстрационным стендом.
Нажмите на кнопку, которая сбрасывает сохраненную в плагине версию, что приведет к срабатыванию автоматического обновления после истечения минутного таймера или при переходе в другой раздел меню.
Заключение
Надеюсь, что данная статья поможет вам решить проблему с автоматическим обновлением скриптом или хотя бы даст направление исследования. Также прошу оценить, насколько вам зашли «бонусы», и стоит ли их использовать в дальнейшем?
Что такое скрипт
Общее понимание скрипта
С английского языка слово «скрипт» переводится как сценарий, из чего уже можно сделать определенные выводы. Это набор команд, то есть строк кода, которые вкупе выполняют конкретную задачу. Для ее выполнения и создаются скрипты. Они могут быть как очень маленькими по объему и отвечать за запуск каких-то простых служб операционной системы, так и объемными, сравнивая переменные и выводя результат на сайте.
Скрипт хранится в текстовом файле, поэтому при желании его содержимое можно легко просмотреть и даже изменить. Этот текстовый файл запускает цепочку выполнения задачи, которая и запрограммирована в скрипте. Если все строки написаны правильно и целевые объекты удается найти, задача выполняется успешно и скрипт срабатывает.
Скрипты сейчас активно интегрируются на сайтах, в качестве примера можно привести популярный скриптовый язык – JavaScript. Однако изначально они работали в операционных системах и выполнялись при помощи внутреннего синтаксиса командной оболочки.
История появления скриптов
Для общего развития предлагаю немного окунуться в историю появления скриптов и взглянуть на то, какими они были раньше. Начали применять их под управлением семейства операционных систем Unix еще 50 лет назад. Одной из первых командных оболочек была sh, в ней использовались shell scripts, которые позволяли выполнять самые разнообразные задачи на компьютере.
Ниже вы видите небольшой код, предназначенный для конвертирования изображения из JPG в PNG:
Обозначения после знаков # являются комментариями и не относятся к скрипту, они только описывают для пользователя действия. Этот пример был взят из открытой библиотеки и отлично показывает, что всего несколько строк кода позволяют обработать изображение, сменив его формат на другой. Сейчас скрипты могут быть более массивными и выполнять задачи на уровень сложнее.
Сферы использования скриптов
Скрипты часто используются на веб-сайтах. Чаще всего они пишутся на языках PHP и JavaScript. Первый используется для написания той части сайта, которую не видит посетитель, то есть бэкенда, а второй в большинстве случаев отвечает за визуал, то есть разные анимации, плавные переходы и другие действия (фронтэнд).
Если с визуальными скриптами все понятно, то невидимые для глаза посетителя скрипты собирают информацию в базы данных, проверяют правильность заполнения форм и выполняют другие сложные задачи.
Соответственно, в операционной системе скрипты тоже выполняют серьезные операции. Скрипты, запущенные через консоль (командную строку), могут влиять на открытие служб и приложений, вносить изменения в системные файлы или даже устанавливать другие программы (вирусы так и попадают в систему).
Если говорить о Windows, то в ней вы можете найти встроенный инструмент CMD (PowerShell), который и предназначен для запуска скриптов, хранящихся в формате BAT.
Самостоятельное написание и применение скриптов
Разберем самостоятельное написание и применение скриптов на примере Windows. Допустим, у вас стоит задача проверить стабильность соединения с конкретным сайтом без запуска браузера. Для этого есть одна полезная команда, запускаемая через Командную строку. А если нужно еще сформировать и отчет о результатах проверки, не совсем удобно будет вводить несколько разных команд по очереди, особенно в тех случаях, когда задача выполняется раз в несколько дней или чаще. Тогда создается BAT-файл с таким содержимым:

Что такое скрипт простыми словами
Скрипт хранится в виде текстового файла на хостинге и доступен для запуска пользователями.
Рейтинг лучших виртуальных хостингов вы можете увидеть вот тут
На виртуальном хостинге автоматический запуск скрипта настраивается один раз (самим пользователем, либо системным администратором, либо техподдержкой по запросу пользователя), а затем скрипт срабатывает автоматически в нужный момент. Никаких манипуляций со скриптом вам вручную больше совершать не нужно, возможно лишь взаимодействие, в зависимости от задач скрипта.
Часто скрипты отвечают за различные динамические элементы сайта:
Скриптовые языки
Все сценарные языки программирования имеют разный синтаксис и области применения.
Что делают скрипты и где применяются
Скрипт может выполняться:
Например, в случае работы в консоли Linux-сервера скрипты используются для:
2. В браузере пользователя сайта применяются скрипты на языке JavaScript.
Если вы решите подключить на свой ресурс сервис чата онлайн-консультанта, например, JivoSite, то вы должны будете разместить на сайте скрипт на языке JavaScript, обеспечивающий работу этого инструмента.
Являются ли WEB-страницы скриптами?
В случае страницы на языке PHP с формой для ввода данных пользователя, можно сказать, что отрабатываются сразу два сценария:
Отправка формы
В форме обратной связи, которую применяет пользователь для контакта с владельцами сайта, человек указывает:
В HTML такая простая форма, в которой содержится три поля и кнопка “отправить”, выглядит так:
Обратите внимание, на атрибут action, содержащий адрес PHP-скрипта, который после отправки должен обработать эту форму.
Обработка формы
После того, как пользователь нажимает на кнопку “Отправить”, управление формой передается PHP-скрипту, который после получения данных, сохраняет их в базе данных и показывает вам сообщение об успешном выполнении задачи. Например, скрипт может показать вам сообщение “Отправлено” или “Ваши данные успешно отправлены”.
В WEB-страницу могут быть встроены сразу несколько скриптов JavaScript, каждый из которых несет свою конкретную функцию.
Сложно ли писать скрипты самостоятельно?
Системные администраторы постоянно пишут для себя разнообразные скрипты для автоматизации повседневных рутинных задач.
Написание скриптов требует знания конкретного языка программирования и общих принципов:
Обычному пользователю хостинга не обязательно учиться писать скрипты самостоятельно. Этот навык может понадобиться в случае, если человек решит заняться программированием профессионально.
Если же вам просто нужно добавить интерактивности на свой сайт, то можно воспользоваться готовыми скриптами на JavaScript. Наиболее полная библиотека скриптов находится здесь.
Пользователям виртуального хостинга также нужно изучить инструкции по применению скриптов, например, чтобы правильно настроить их выполнение по расписанию. В инструкции будет указано, в каком месте конкретного файла нужно разместить текст скрипта.
Например, простейший скрипт можно создать и запустить прямо из командной строки в консоли.
Достаточно просто набрать строку:
А после завершить набор команд нажатием Enter.
Данная строка содержит две команды, разделенные точкой с запятой.
Интерпретатор языка Shell выполнит команды одну за другой, и на экране появится результат их работы.
LiveReload — обновление javascript без полной перезагрузки страницы (на примере mithril)
Вступление
Не так давно я начал пользоваться такой полезной штукой как livereload (для тех, кто не знает, что это — есть статья на хабре). Livereload отслеживает изменения в коде веб-приложения и перегружает страницу в браузере при необходимости. При этом со стилями Livereload поступает умнее и заменяет их на лету без перезагрузки, что выглядит волшебно.
К сожалению с javascript такой номер не прокатывает — Livereload не знает как гуманно заменить только изменившиеся скрипты и перегружает всю страницу. Это особенно печалит если использовать инструмент вроде mithril, в котором представление (читай — html) задается так же в javascript. Если я меняю модель или контроллер, то тут все понятно, но если я меняю класс у дива в представлении (скажем, подбирая правильное сочетание bootstrap классов), то перезагрузка страницы кажется излишней — ну вот же, я поменял одну функцию, просто перерисуй view с ее помощью!
В целом не страшно, конечно (работали же как-то без этого раньше), но почему бы не сделать работу еще немного удобнее?
Для тех, кто торопится
Постановка
У нас есть чистые функции, отображающие модель на представление (читай — html). Нам нужно сделать так, что если меняется такая функция, то мы должны в браузере подгрузить новую версию, подменить исходную и, в случае с Mithril, вызвать m.redraw().
Самый простой способ подмены — запихать все такие функции в глобальный объект, а в местах их прежнего обитания расставить обращения к этому объекту. Что-то типа такого:
Теперь мы можем перезагружать один только file1.js и дергать m.redraw(), после чего представление будет перерисовано. При этом текущее состояние системы (в нашем случае хранящееся в контроллере Page) сохранится.
Однако вручную делать такое разделение не хочется — во-первых нарушается целостность компонента (довольно удобно при разработке менять только один файл, настраивая и поведение и внешний вид компонента), во-вторых есть уже написанный код, в-третьих пришлось бы больше объяснять новым разработчикам. Значит нужно парсить исходный код и извлекать нужные нам функции.
Извлечение view-функций
Для решения проблемы 1 парсинга кода регекспами будет явно недостаточно, нужен синтаксический разбор. К счастью, есть библиотека esprima, которая разбирает переданный ей js-код и выдает синтаксическое дерево (можете поиграться здесь) как обычный json. Обход такого дерева не должен составить труда, надо только разобраться со всеми возможными типами узлов дерева, чтобы не пропустить какие-то случаи. Например запись
совсем не то же самое что
(соответственно результаты парсинга вот и вот)
Теперь несложно и функцию найти, и все ее зависимости. Тела функций вынимаем и складируем отдельно, в исходном коде помещаем вызов глобальной функции, и собираем код обратно с помощью библиотеки escodegen.
Для решения задач 2 и 3 пригодилось бы что-то вроде аттрибутов C# или аннотаций Java — какой-то способ пометить нужные функции. Т.к. в javascript такого способа не предусмотрено, пришлось придумать — пусть аттрибутом будет строка, являющаяся первым выражением в теле функции. И если значение аттрибуты «__stateless», то функцию надо извлекать, а если «__ignore» — то не надо.
Все описанное выше я собрал в виде отдельной библиотеки st8less, никак не заточенной под mithril. В теории ее можно использовать для других похожих задач.
Плагин к LiveReload
Теперь, когда мы умеем доставать нужные нам функции и сохранять их отдельным js-файлом, надо научить LiveReload обновлять его без перезагрузки всей страницы.
Плагин к LiveReload написать несложно, причем он подхватится независимо от того как вы используете Livereload — вставляете сниппет на страницу или используете расширение браузера. Плагины имеют приоритет над стандартным поведением, и если мы напишем вот так:
то при любом изменении вызовется наш метод reload, и если мы возвращаем true, то стандартная обработка не происходит. Так что можем отслеживать обновление только нашего файла с вынесенными функциями (зная имя этого файла, конечно) и перезагружать только его.
Для перезагрузки мы будем удалять существующий элемент script и добавлять в DOM новый, причем к URL скрипта каждый раз будет добавляться текущее время — чтобы предотвратить кэширование.
Встраивание в процесс сборки
Если проект собирается с помощью gulp (у меня как раз так) или другой системы сборки, то логично было бы встроить извлечение view-функций в процесс сборки. В случае с gulp нужно было написать плагин, который обработает все проходящие через него js-скрипты, повыдергивает из них view-функции и запишет отдельным файлом, а потом оповестит LiveReload об изменениях.
Я не буду описывать создание плагина для gulp, все делалось строго по туториалам и примерам других плагинов (типа gulp-coffee и gulp-concat), ничего необычного. В итоге gulpfile.js выглядит примерно так:
Обратите внимание на использование gulp-changed. Если мы меняем только main.coffee, то на выходе получаем обновленный main.js и st8less.js, причем если мы меняли view-функцию, то main.js по факту будет точно таким же. Но время изменения у main.js все равно поменяется, и в результате LiveReload перезагрузит всю страницу. Чтобы этого не случилось, необходимо сравнить фактическое содержимое, что и делает плагин gulp-changed.
Плагины для gulp и LiveReload лежат в отдельном репозитории — gulp-livereload-mithril. Он, в свою очередь, ссылается на библиотеку st8less описанную выше.
Неявная подгрузка нового скрипта
Наш плагин создает новый js-файл (st8less.js), и надо сослаться на него из html-страницы. Можно было попросить пользователя сделать это самостоятельно, но я подумал: все равно я меняю пользовательские js-файлы, почему бы не добавить в один из них простой document.write?
Так и было сделано, но этого оказалось недостаточно. Если, скажем, мы добавим document.write в начало main.js, а main.js где-то в середине уже использует вынесенные в функции, то мы получим ошибку, т.к. свежедобавленный элемент script еще не начал подгружать наш скрипт.
Надо как-то подгрузить указанный скрипт здесь и сейчас, и другого способа, кроме как послать синхронный ajax-запрос, я не нашел. Добавляемая в начало одного из скриптов конструкция выглядит так:
При желании можно этот ужас отключить, передав плагину
Задача оказалась вполне разрешимой, причем приведенное решение можно применить не только для mithril, но и для других подобных случаев — на ум приходят react и angular.js 1.x (зачастую html-верстка для директив помещается прямо в js-код директивы).
PHP-скрипт, который обновляет сам себя
Задача: написать скрипт, который умеет загружать новую версию себя с удаленного сервера и самообновляться.
Практика автообновлений широко применяется среди десктоп-клиентов и операционных систем, но в вебе встречается редко. Однако, для скриптов, где одна установка обеспечивает работу одного ресурса (а это, фактически, все скрипты, которые ставишь себе на хостинг), автоматизированная возможность установки обновлений не менее важна, чем для десктопов. К веб-сервисам это, конечно, отношения не имеет.
Технически выполнить обновление скрипта не так уж и сложно — загрузить обновление с удаленного сервера и установить его поверх работающей версии — однако, есть некоторые нюансы. Мы в Вебасисте применяем практику установки удаленных обновлений уже давно и накопили большой опыт работы со всевозможными конфигурациями серверов хостинг-провайдеров. Поделимся опытом.
Это не туториал, поэтому вот сразу окончательный вариант скрипта: www.webasyst.com/etc/ru/selfupdate-1 (скрипт index.php; ≈20 КБ).
Скрипт содержит класс selfUpdate, который выполняет обновление самого себя (скачивает обновленную версию файла index.php и заменяет ей работающую в текущий момент).
Под катом рассмотрим как устроен процесс обновления и какие могут возникнуть проблемы при обновлении.
Как запустить скрипт?
Просто скачайте скрипт и загрузите в папку на сервере. Запустив скрипт в браузере, вы увидите заголовок Hello World и кнопки обновления скрипта.
Для того, чтобы обновление скрипта работало, нужно включить права на запись для корневой папки, куда установлен скрипт. Это необходимо, потому что скрипт будет создавать в этой папке поддиректорию для загрузки в нее обновленного скрипта с удаленного сервера. Если запускаете скрипт на локалхосте в Windows (например, на основе пакета «Денвер»), то права доступа предоставлять не надо — все будет работать само собой.
Скрипт index.php загружает с сервера свою обновленную версию как есть (не в архиве) и заменяет себя на нее. Обновление скриптов, в которых много файлов, в реальности отличается только тем, что с сервера загружается архив со скриптами. В остальном же все работает по такой же схеме: загрузил файлы — обновил файлы.
Загрузка файла с удаленного сервера
За обновлением скрипт идет по тому же адресу, который был представлен выше (http://www.webasyst.com/etc/ru/selfupdate-1/). Этот источник обновлений отдает «обновление» в виде вложения (для наглядности мы сделали две вариации: V1 выводит надпись Hello world, и V2 показывает сумму трех случайных чисел).
Загрузка файла с удаленного сервера из скрипта возможна двумя способами:
Через fopen(): Да, через обычный fopen(). Но для работы этой функции необходимо, чтобы в настройках PHP (php.ini) был установлен параметр allow_fopen_url = On и в списке поддерживаемых протоколов был HTTP.
Через cURL: Этот вариант предпочительнее, так как более гибкий. Например, при использовании cURL можно сделать возобновление загрузки в случае, если она была прервана.
Обновление работающего скрипта
Файл PHP-скрипта, работающего в текущий момент, не блокируется системой по записи, и поэтому его можно перезаписать. В связи с этим схема установки обновления в нашем примере следующая: файл загружается в отдельную подпапку (/updates/download/), проверяется правильно ли загрузился файл (соответствует ли размер скачанного файла размеру, заявленному в заголовках ответа, а также проверкой md5-хеша файла), и затем работающий скрипт index.php перезаписывается обновленным файлом.
В случае с одним файлом все тривиально: скачал и перезаписал. Интереснее, когда с сервера загружается архив со скриптами. Интереснее тем, что тут есть три варианта (стратегии) распаковки:
1) скачанный архив можно распаковывать поверх работающих скриптов: хороший вариант, но на момент распаковки это сломает работу системы, будет даунтайм;
2) скопировать текущую версию работающих скриптов во временную папку, распаковать поверх этой папки скачанный архив, рабочую версию переместить в директорию бекапов, а на её место обновленный код: вариант плох тем, что предполагает сохранение устаревших файлов;
3) распаковать архив в отдельную папку, а затем подменить работающую папку обновленной версией: этот вариант наиболее интересен, потому что предполагает наименьший даунтайм в работе скриптов.
Проблемы
Основные проблемы, возникающие при обновлении:
― max_input_time (ограничение на время операций ввода-вывода): скачивание файлов значительного объема может не уложиться в максимально допустимое время;
― max_execution_time: процесс обновления может упереться в ограничение на общее время работы скрипта. Особенность в том, что такого не может случиться при загрузке файла с удаленного сервера, т.к. PHP не считает время на операции с файлами (хотя, есть особенности в Windows-системах), однако, ограничение становится критичным при распаковке архивов значительных размеров;
― доступность сервера обновлений: помимо отсутствия подходящего транспорта для загрузки обновления (посредством fopen или curl) могут иметь место проблемы с сетью (DNS, роутинг, firewall);
― разрывы соединения с сервером во время скачивания файла: необходимо проверять целостность файла после загрузки, но для этого сервер, который выдает обновление, должен уметь отдавать и md5-хеши файлов.
Перечисленные проблемы характерны для загрузки больших архивов. Для обновления одного двадцатикилобайтного index.php это не актуальны (за исключением необходимости проверить целостность загруженного файл).
Загрузку больших архивов мы планируем рассмотреть в следующем посте, где представим вторую версию класса selfUpdate с поддержкой загрузки файлов (с прогрессбаром!), распаковку архива и возобновление загрузки в случае сбоя.



