array('method' => strtoupper($method), 'header' => 'Content-type: application/json API-Key: ' . $apiKey, 'ignore_errors' => true)); if ($data) { $options['http']['content'] = json_encode($data); } $streamContext = stream_context_create($options); $result = file_get_contents($url, false, $streamContext); /** * @var array $http_response_header materializes out of thin air */ $status_line = $http_response_header[0]; preg_match('{HTTP\\/\\S*\\s(\\d{3})}', $status_line, $match); $status = $match[1]; if ($status !== '200') { if ($result = json_decode($result, true)) { die('Request failed: ' . $result['message']); } else { die('Request failed. Unknown error, please contact the support.'); } } return $result; } }; class CommandLine { const FILE_NAME_PREVIOUS_INPUT = '.php-download.com'; const YES_NO_SELECTION = array('Yes', 'No'); private $apiKey; private $projectId; private $scriptName; private $action; public function __construct() { $this->parseArguments(); switch ($this->action) { case 'install': $this->installAction(); break; case 'silent': $this->silentAction(); break; } } private function parseArguments() { $args = $_SERVER['argv']; $wrongParameterMessage = 'The first parameter should be one of the following commands: ' . PHP_EOL; foreach ($this->getActions() as $action => $description) { $wrongParameterMessage .= (((str_pad('', 4) . str_pad($action, 7)) . ' - ') . $description) . PHP_EOL; } foreach ($args as $index => $arg) { if ($index == 0) { $this->scriptName = $arg; } elseif ($index == 1 && !in_array($arg, array_keys($this->getActions()))) { $this->error($wrongParameterMessage, true); } elseif ($index == 1) { $this->action = $arg; } } if (!$this->action) { $this->error($wrongParameterMessage, true); } } public function deleteDir($dir) { if (substr($dir, 0, strlen('vendor')) !== 'vendor') { $this->error('Error on deleting the vendor folder', true); } if (is_dir($dir)) { $objects = scandir($dir); foreach ($objects as $object) { if ($object != '.' && $object != '..') { if (is_dir(($dir . DIRECTORY_SEPARATOR) . $object) && !is_link((($dir . '/') . $object))) { $this->deleteDir(($dir . DIRECTORY_SEPARATOR) . $object); } else { unlink(($dir . DIRECTORY_SEPARATOR) . $object); } } } rmdir($dir); } } private function selectInput($message, $possibilities, callable $handleInput, $defaultValueIndex = null) { if ($defaultValueIndex !== null) { $this->message(('Enter without input to choose [' . $possibilities[$defaultValueIndex]) . ']'); } $this->message($message); foreach ($possibilities as $index => $possibility) { echo ((($index + 1) . '. ') . $possibility) . PHP_EOL; } $input = readline('>> '); $this->lbr(); if (empty($input) && $defaultValueIndex !== null) { $handleInput($defaultValueIndex + 1); return; } if ($input === '' || !in_array(intval($input), array_keys($possibilities), true) && count($possibilities) != $input) { $this->error('Invalid input. Please try again!.'); $this->selectInput($message, $possibilities, $handleInput, $defaultValueIndex); return; } if ($handleInput($input)) { $this->error('Please try again!'); $this->selectInput($message, $possibilities, $handleInput, $defaultValueIndex); } } private function textInput($message, callable $handleInput, $defaultInput = null) { if (!is_null($defaultInput)) { $this->message(('Enter without input to choose [' . $defaultInput) . ']'); } $this->message($message); $input = readline('>> '); $this->lbr(); if (empty($input) && !is_null($defaultInput)) { $handleInput($defaultInput); return; } if ($handleInput($input)) { $this->error('Please try again!'); $this->textInput($message, $handleInput, $defaultInput); } } private function message($message) { echo $message . PHP_EOL; } private function lbr() { echo PHP_EOL; } private function error($message, $cancelScript = false) { //TODO add red color if ($cancelScript) { die($message); } else { echo $message . PHP_EOL; } } private function handleApiResponse($response) { $response = json_decode($response, true); if (!isset($response['success']) || !$response['success']) { if (isset($response['message'])) { $this->error($response['message']); } return false; } return $response['data']; } private function saveInput() { return file_put_contents(self::FILE_NAME_PREVIOUS_INPUT, json_encode(array('apiKey' => $this->apiKey, 'projectId' => $this->projectId))); } private function silentAction() { if (!file_exists(self::FILE_NAME_PREVIOUS_INPUT)) { $this->error(('The file ' . self::FILE_NAME_PREVIOUS_INPUT) . ' does not exist.', true); } $data = json_decode(file_get_contents(self::FILE_NAME_PREVIOUS_INPUT), true); if (!$data) { $this->error(('Corrupt ' . self::FILE_NAME_PREVIOUS_INPUT) . ' file.', true); } if (!isset($data['apiKey']) || !isset($data['projectId'])) { $this->error(('Error on loading the data from the ' . self::FILE_NAME_PREVIOUS_INPUT) . ' file.', true); } $this->apiKey = $data['apiKey']; $this->projectId = $data['projectId']; $this->downloadVendor(class_exists('ZipArchive')); } private function installAction() { $this->message('**************************************************'); $this->message(' Welcome to the php-download.com download wizard! '); $this->message('**************************************************'); $this->lbr(); $this->selectInput('Is this PHP script exactly in the folder where the vendor folder should be created?', self::YES_NO_SELECTION, function ($input) { if ($input == 2) { $this->error('Please move this PHP script to another location.', true); } }, 0); $projects = null; $this->textInput('Please insert your API Key from php-download.com.', function ($input) { $this->apiKey = $input; try { $response = Http::call(PHP_DOWNLOAD_URL . '/api/v1/users/login', 'post', $this->apiKey, array()); if ($this->handleApiResponse($response) === false) { return true; } } catch (Exception $e) { $this->error($e->getMessage(), true); } return false; }); $response = Http::call(PHP_DOWNLOAD_URL . '/api/v1/projects/index', 'get', $this->apiKey); $projects = $this->handleApiResponse($response); $possibilities = array(); foreach ($projects as $project) { $possibilities[] = $project['name']; } $this->selectInput('Please select a project.', $possibilities, function ($input) use($projects) { $this->projectId = $projects[$input - 1]['id']; }, 0); $this->selectInput(('Save your input (API key etc.) in a file called ' . self::FILE_NAME_PREVIOUS_INPUT) . ' to skip this dialog in the future?', self::YES_NO_SELECTION, function ($input) { if ($input == 1) { if (!$this->saveInput()) { $this->error('Error on save input. Do you have enough permissions?'); return true; } } }, 1); $this->selectInput('Delete the old vendor folder and replace it by a new one from php-download.com?', self::YES_NO_SELECTION, function ($input) { $this->downloadVendor($input == 1 && class_exists('ZipArchive')); }, 1); } private function downloadVendor($replace) { $counter = 0; while (true) { $counter++; if ($counter > 100) { $this->error('Timout. Please check for error messages on the website.', true); } $response = Http::call((PHP_DOWNLOAD_URL . '/api/v1/projects/start-download?projectId=') . $this->projectId, 'get', $this->apiKey); if (!$response) { $this->error('Error on the HTTP call. Please contact the support.', true); } $response = json_decode($response, true); if (!$response || !isset($response['status'])) { $this->error('Error on JSON decode. Please contact the support.', true); } if ($response['status'] !== 'complete') { sleep(2); continue; } $downloadResponse = Http::call((PHP_DOWNLOAD_URL . '/api/v1/projects/download?projectId=') . $this->projectId, 'get', $this->apiKey); $downloadResponse = $this->handleApiResponse($downloadResponse); $tmpVendorZipFile = ('vendor_' . date('Y_m_d_H_i_s')) . '.zip'; file_put_contents($tmpVendorZipFile, base64_decode($downloadResponse['file'])); if (!$replace) { $this->message("The new vendor folder was downloaded. Please extract the file {$tmpVendorZipFile} manually."); break; } $this->deleteDir('vendor'); $zip = new ZipArchive(); $res = $zip->open($tmpVendorZipFile); if ($res === true) { $zip->extractTo('.'); $zip->close(); } else { $this->error('Error on extracting the zip file.', true); } unlink($tmpVendorZipFile); break; } } private function getActions() { return array('install' => ('Downloads the vendor folder and creates a ' . self::FILE_NAME_PREVIOUS_INPUT) . ' file, to download the vendor folder in silent mode.', 'silent' => ('Starts the vendor download without any interaction. A ' . self::FILE_NAME_PREVIOUS_INPUT) . ' file is necessary.'); } }; if (php_sapi_name() !== 'cli') { die('This script is only callable from the command line.'); } $commandLine = new CommandLine();