Как стать автором
Обновить

Как я боролся с бесплатным хостингом и Composer

Время на прочтение4 мин
Количество просмотров9.5K
Никто не будет отрицать что Composer довольно удобный инструмент, и что есть бесплатные или дешевые хостинги которые не предоставляют какую либо консольку или встроенного инструмента для работы с Composer. Вот как раз с таким стеком я и столкнулся. Ну и как завещали джедаи, vendor сразу же добавляется в .gitignore чтоб не засорять им репозиторий и не гонять туда/сюда библиотеки.

Первое что пришло в голову это сделать скрипт доступный из Web, который можно дёрнуть в нужный момент и он обновит зависимости или установит их.

Для этого нам нужно провести некоторые манипуляции.

1. Для установки композера локально нам необходимо скачать composer.phar.

2. Создать папку куда он распакуется (пусть будет var).

3. Создать composer.json (ну про этот я думаю вы уже и так знаете если работали с composer).

4. Ну и создать сам скрипт для работы с композером из Web (пусть будет composer.php).

Итак мы имеем такую структуру нашего будущего сайта:

структура сайта

Сам же composer.phar будет следующим:

<?php

use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;

//Уберём лимиты, чтоб скрипт не отвалился раньше времени
ini_set("memory_limit", -1);
ini_set("max_execution_time", 0);

//Корень проекта
$root = __DIR__ . "/../";

//Папка для разархивирования
$dir = "{$root}/var";

//Смотрим если Phar архив еще не распакован, то распакуем его
if (file_exists("{$dir}/vendor/autoload.php") === false) {
	$composerPhar = new Phar("{$root}/composer.phar");
	$composerPhar->extractTo($dir);
}

//Подключим автолоадер для использования классов композера
require_once("{$dir}/vendor/autoload.php" . '');

//Обьявим переменную окружения чтоб обозначить где хранится сам композер
putenv("COMPOSER_HOME={$dir}/bin/composer");

//Изменим папку на корень чтоб vendor хранился на том же уровне что и WebRoot
chdir($root);

//Подготавливаем комманду установки
$input = new ArrayInput(['command' => 'install']);

//Создаем вывод в стрим
$stream = fopen('php://temp', 'w+');
$output = new StreamOutput($stream);

//Запускаем "консольное" приложение
$application = new Application();
$application->setAutoExit(false);
$application->run($input, $output);

//А тут должен быть вывод
echo stream_get_contents($stream);


И если вы счатливчик. То после вызова скрипта, он развернёт папку vendor.

Но я таким не оказался) Первое что сломало мои планы это настройка phar.readonly = On в php.ini, и как вы уже догадались на бесплатных хостингах править его обычно нельзя. Тогда я начал искать пути обхода.

Первое что попробовал это создать user.ini который переопределит настройки в php.ini, на локальной машине сработало) А вот на хостинге этот функционал зарезан был.

Тогда я попробовал использовать еще один трюк. Переименовать composer.phar в просто composer, результат то же. На локалке сработало, на хостинге — нет.

Тогда пришлось всё таки вместо скрипта распаковать в var файлы локально и залить их на сервер.

Также стоит закрыть скрипт авторизацией, чтоб всякие персоны не вызывали его. Также в конечную версию скрипта я добавил выбор команды через параметры.

<?php

use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;

//Уберём лимиты, чтоб скрипт не отвалился раньше времени
ini_set("memory_limit", -1);
ini_set("max_execution_time", 0);

//Негоже в Web хранить скрипты не закрытые паролем, чтоб кто-то посторонний мог их вызвать
if (isset($_SERVER['HTTP_AUTHORIZATION']) AND !empty($_SERVER['HTTP_AUTHORIZATION'])) {
	list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)), 2);
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) AND !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
	list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6)), 2);
}

//Детали для авторизации, не забудьте поменять на что-то более секьюрное
$config = [
	'user' => 'admin',
	'password' => 'admin',
];

//Пускаем или не пускаем пользователя дальше
if ((isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] == $config['user'] && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW'] == $config['password'])) {
	unset($_SERVER['PHP_AUTH_USER']);
	unset($_SERVER['PHP_AUTH_PW']);
} else {
	$uniqueID = uniqid();
	header("WWW-Authenticate: Basic realm='{$uniqueID}'");
	header('HTTP/1.0 401 Unauthorized');

	exit();
}

//Корень проекта
$root = realpath(__DIR__ . "/../");

//Папка для разархивирования
$dir = "{$root}/var";

//Смотрим если Phar архив еще не распакован, то распакуем его
if (file_exists("{$dir}/vendor/autoload.php") === false) {
	$composerPhar = new Phar("{$root}/composer.phar");
	$composerPhar->extractTo($dir);
}

//Подключим автолоадер для использования классов композера
require_once("{$dir}/vendor/autoload.php" . '');

//Обьявим переменную окружения чтоб обозначить где хранится сам композер
putenv("COMPOSER_HOME={$dir}/bin/composer");

//Изменим папку на корень чтоб vendor хранился на том же уровне что и WebRoot
chdir($root);

//Не очень то хорошо, но будем надеятся что пароль знает только человек который знает что он делает, и делает это во имя добра)
//По умолчанию composer update, так как он используется чаще чем ?command=install
$params = !empty($_GET) ? $_GET : ['command' => 'update'];

//Подготавливаем комманду установки.
$input = new ArrayInput($params);

//Создаем вывод в стрим
$output = new BufferedOutput(
	OutputInterface::VERBOSITY_NORMAL
//true
);

//Запускаем "консольное" приложение
$application = new Application();
$application->setAutoExit(false);
$application->run($input, $output);

$content = $output->fetch();

echo "<pre>";
//А тут должен быть вывод
echo $content;

echo "<br>";

echo (file_exists("{$root}/vendor/autoload.php")) ? 'Autoload <b>installed</b>' : 'Autoload <b>Missing</b>';

Также не помешает добавить в .htaccess правило для проброса заголовка авторизации (в случае с CGI) и редиректа на HTTPS, так как авторизация уходит в открытом виде.

RewriteEngine On

#Если у вас CGI, то заголовок авторизации потеряется, поэтому нужно его принудительно добавить
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

#Переадресация на HTTPS, так как авторизация уходит в незашифрованном виде
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:SSL} !=1 [NC]
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
Теги:
Хабы:
+4
Комментарии27

Публикации

Изменить настройки темы

Истории

Работа

PHP программист
162 вакансии

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн