PageRenderTime 52ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/downloader/lib/Magento/Connect/Command/Install.php

https://github.com/jonathanselander/magento2
PHP | 548 lines | 422 code | 45 blank | 81 comment | 109 complexity | 238447f6e1817c467f1c14c87fbffa8b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Unlicense
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Magento
  22. * @package Magento_Connect
  23. * @copyright Copyright (c) 2013 X.commerce, Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. namespace Magento\Connect\Command;
  27. final class Install extends \Magento\Connect\Command
  28. {
  29. /**
  30. * Install action callback
  31. *
  32. * @throws \Exception
  33. * @param string $command
  34. * @param array $options
  35. * @param array $params
  36. * @param array $objects
  37. * @return array|null
  38. */
  39. public function doInstall($command, $options, $params, $objects = array())
  40. {
  41. $this->cleanupParams($params);
  42. $installFileMode = $command === 'install-file';
  43. /** @var $ftpObj \Magento\Connect\Ftp */
  44. $ftpObj=null;
  45. $ftp = empty($options['ftp']) ? false : $options['ftp'];
  46. /** @var $packager \Magento\Connect\Packager */
  47. $packager = $this->getPackager();
  48. /** @var $cache \Magento\Connect\Singleconfig */
  49. /** @var $config \Magento\Connect\Config */
  50. if ($ftp) {
  51. list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
  52. } else {
  53. $cache = $this->getSconfig();
  54. $config = $this->config();
  55. }
  56. try {
  57. $forceMode = isset($options['force']);
  58. $upgradeAllMode = $command == 'upgrade-all';
  59. $upgradeMode = $command == 'upgrade' || $command == 'upgrade-all';
  60. $noFilesInstall = isset($options['nofiles']);
  61. $withDepsMode = !isset($options['nodeps']);
  62. $ignoreModifiedMode = true || !isset($options['ignorelocalmodification']);
  63. $clearInstallMode = $command == 'install' && !$forceMode;
  64. $installAll = isset($options['install_all']);
  65. $channelAuth = isset($options['auth'])?$options['auth']:array();
  66. $rest = $this->rest();
  67. if (empty($config->magento_root)) {
  68. $config->magento_root=dirname(dirname($_SERVER['SCRIPT_FILENAME']));
  69. }
  70. chdir($config->magento_root);
  71. $dirCache = '/' . $config->downloader_path . '/'
  72. . \Magento\Connect\Config::DEFAULT_CACHE_PATH;
  73. $dirTmp = '/' . \Magento\Connect\Package\Reader::PATH_TO_TEMPORARY_DIRECTORY;
  74. $dirMedia = '/media';
  75. $isWritable = true;
  76. if ($ftp) {
  77. $cwd=$ftpObj->getcwd();
  78. $ftpObj->mkdirRecursive($cwd . $dirCache,0777);
  79. $ftpObj->chdir($cwd);
  80. $ftpObj->mkdirRecursive($cwd . $dirTmp,0777);
  81. $ftpObj->chdir($cwd);
  82. $ftpObj->mkdirRecursive($cwd . $dirMedia,0777);
  83. $ftpObj->chdir($cwd);
  84. $err = "Please check for sufficient ftp write file permissions.";
  85. } else {
  86. @mkdir($config->magento_root . $dirCache,0777,true);
  87. @mkdir($config->magento_root . $dirTmp,0777,true);
  88. @mkdir($config->magento_root . $dirMedia,0777,true);
  89. $isWritable = is_writable($config->magento_root)
  90. && is_writable($config->magento_root . '/' . $config->downloader_path)
  91. && is_writable($config->magento_root . $dirCache)
  92. && is_writable($config->magento_root . $dirTmp)
  93. && is_writable($config->magento_root . $dirMedia);
  94. $err = "Please check for sufficient write file permissions.";
  95. }
  96. $isWritable = $isWritable && is_writable($config->magento_root . $dirMedia)
  97. && is_writable($config->magento_root . $dirCache)
  98. && is_writable($config->magento_root . $dirTmp);
  99. if (!$isWritable) {
  100. $this->doError($command, $err);
  101. throw new \Exception(
  102. 'Your Magento folder does not have sufficient write permissions, which downloader requires.'
  103. );
  104. }
  105. if (!empty($channelAuth)) {
  106. $rest->getLoader()->setCredentials($channelAuth['username'], $channelAuth['password']);
  107. }
  108. if ($installFileMode) {
  109. if (count($params) < 1) {
  110. throw new \Exception("Argument should be: filename");
  111. }
  112. $filename = $params[0];
  113. if (!@file_exists($filename)) {
  114. throw new \Exception("File '{$filename}' not found");
  115. }
  116. if (!@is_readable($filename)) {
  117. throw new \Exception("File '{$filename}' is not readable");
  118. }
  119. $package = new \Magento\Connect\Package($filename);
  120. $package->setConfig($config);
  121. $package->validate();
  122. $errors = $package->getErrors();
  123. if (count($errors)) {
  124. throw new \Exception("Package file is invalid\n" . implode("\n", $errors));
  125. }
  126. $pChan = $package->getChannel();
  127. $pName = $package->getName();
  128. $pVer = $package->getVersion();
  129. if (!($cache->isChannelName($pChan) || $cache->isChannelAlias($pChan))) {
  130. throw new \Exception("The '{$pChan}' channel is not installed. Please use the MAGE shell "
  131. . "script to install the '{$pChan}' channel.");
  132. }
  133. $conflicts = $cache->hasConflicts($pChan, $pName, $pVer);
  134. if (false !== $conflicts) {
  135. $conflicts = implode(", ",$conflicts);
  136. if ($forceMode) {
  137. $this->doError($command, "Package {$pChan}/{$pName} {$pVer} conflicts with: " . $conflicts);
  138. } else {
  139. throw new \Exception("Package {$pChan}/{$pName} {$pVer} conflicts with: " . $conflicts);
  140. }
  141. }
  142. $conflicts = $package->checkPhpDependencies();
  143. if (true !== $conflicts) {
  144. $conflicts = implode(",",$conflicts);
  145. $err = "Package {$pChan}/{$pName} {$pVer} depends on PHP extensions: " . $conflicts;
  146. if ($forceMode) {
  147. $this->doError($command, $err);
  148. } else {
  149. throw new \Exception($err);
  150. }
  151. }
  152. $conflicts = $package->checkPhpVersion();
  153. if (true !== $conflicts) {
  154. $err = "Package {$pChan}/{$pName} {$pVer}: " . $conflicts;
  155. if ($forceMode) {
  156. $this->doError($command, $err);
  157. } else {
  158. throw new \Exception($err);
  159. }
  160. }
  161. if (!$noFilesInstall) {
  162. if ($ftp) {
  163. $packager->processInstallPackageFtp($package, $filename, $config, $ftpObj);
  164. } else {
  165. $packager->processInstallPackage($package, $filename, $config);
  166. }
  167. }
  168. $cache->addPackage($package);
  169. $installedDeps = array();
  170. $installedDepsAssoc = array();
  171. $installedDepsAssoc[] = array('channel'=>$pChan, 'name'=>$pName, 'version'=>$pVer);
  172. $installedDeps[] = array($pChan, $pName, $pVer);
  173. $title = isset($options['title']) ? $options['title'] : "Package installed: ";
  174. $out = array($command => array('data'=>$installedDeps, 'assoc'=>$installedDepsAssoc, 'title'=>$title));
  175. if ($ftp) {
  176. $packager->writeToRemoteCache($cache, $ftpObj);
  177. @unlink($config->getFilename());
  178. }
  179. $this->ui()->output($out);
  180. return $out[$command]['data'];
  181. }
  182. if (!$upgradeAllMode) {
  183. if (count($params) < 2) {
  184. throw new \Exception("Argument should be: channelName packageName");
  185. }
  186. $channel = $params[0];
  187. $package = $params[1];
  188. $argVersionMax = isset($params[2]) ? $params[2]: false;
  189. $argVersionMin = isset($params[3]) ? $params[3]: false;
  190. $cache->checkChannel($channel, $config, $rest);
  191. $channelName = $cache->chanName($channel);
  192. $this->ui()->output("Checking dependencies of packages");
  193. $packagesToInstall = $packager->getDependenciesList($channelName, $package, $cache, $config,
  194. $argVersionMax, $argVersionMin, $withDepsMode, false, $rest
  195. );
  196. /*
  197. * process 'failed' results
  198. */
  199. if (count($packagesToInstall['failed'])) {
  200. $showError=!count($packagesToInstall['result']);
  201. foreach ($packagesToInstall['failed'] as $failed) {
  202. $msg="Package {$failed['channel']}/{$failed['name']} failed: " . $failed['reason'];
  203. if ($showError) {
  204. $this->doError($command, $msg);
  205. } else {
  206. $this->ui()->output($msg);
  207. }
  208. }
  209. }
  210. $packagesToInstall = $packagesToInstall['result'];
  211. } else {
  212. if (empty($params[0])) {
  213. $channels = $cache->getChannelNames();
  214. } else {
  215. $channel = $params[0];
  216. if (!$cache->isChannel($channel)) {
  217. throw new \Exception("'{$channel}' is not existant channel name / valid uri");
  218. }
  219. $channels = $cache->chanName($channel);
  220. }
  221. $packagesToInstall = array();
  222. $neededToUpgrade = $packager->getUpgradesList($channels, $cache, $config, $rest);
  223. foreach ($neededToUpgrade as $chan=>$packages) {
  224. foreach ($packages as $name=>$data) {
  225. $versionTo = $data['to'];
  226. $tmp = $packager->getDependenciesList($chan, $name, $cache, $config, $versionTo, $versionTo,
  227. $withDepsMode, false, $rest
  228. );
  229. if (count($tmp['result'])) {
  230. $packagesToInstall = array_merge($packagesToInstall, $tmp['result']);
  231. }
  232. }
  233. }
  234. }
  235. /**
  236. * Make installation
  237. */
  238. $installedDeps = array();
  239. $installedDepsAssoc = array();
  240. foreach ($packagesToInstall as $package) {
  241. try {
  242. $pName = $package['name'];
  243. $pChan = $package['channel'];
  244. $pVer = $package['downloaded_version'];
  245. $pInstallState = $package['install_state'];
  246. $rest->setChannel($cache->chanUrl($pChan));
  247. /**
  248. * Skip existing packages
  249. */
  250. if ($upgradeMode && $cache->hasPackage($pChan, $pName, $pVer, $pVer)
  251. || ('already_installed' == $pInstallState && !$forceMode)
  252. ) {
  253. $this->ui()->output("Already installed: {$pChan}/{$pName} {$pVer}, skipping");
  254. continue;
  255. }
  256. if ('incompartible' == $pInstallState) {
  257. $this->ui()->output(
  258. "Package incompartible with installed Magento: {$pChan}/{$pName} {$pVer}, skipping"
  259. );
  260. continue;
  261. }
  262. $conflicts = $cache->hasConflicts($pChan, $pName, $pVer);
  263. if (false !== $conflicts) {
  264. $conflicts = implode(", ",$conflicts);
  265. if ($forceMode) {
  266. $this->doError($command, "Package {$pChan}/{$pName} {$pVer} conflicts with: " . $conflicts);
  267. } else {
  268. throw new \Exception("Package {$pChan}/{$pName} {$pVer} conflicts with: " . $conflicts);
  269. }
  270. }
  271. /**
  272. * Modifications
  273. */
  274. if (($upgradeMode || ($pInstallState == 'upgrade')) && !$ignoreModifiedMode) {
  275. if ($ftp) {
  276. $modifications = $packager->getRemoteModifiedFiles($pChan, $pName, $cache, $config, $ftp);
  277. } else {
  278. $modifications = $packager->getLocalModifiedFiles($pChan, $pName, $cache, $config);
  279. }
  280. if (count($modifications) > 0) {
  281. $this->ui()->output('Changed locally: ');
  282. foreach ($modifications as $row) {
  283. if (!$ftp) {
  284. $this->ui()->output($config->magento_root . '/' . $row);
  285. } else {
  286. $this->ui()->output($row);
  287. }
  288. }
  289. }
  290. }
  291. if ($ftp) {
  292. $cwd=$ftpObj->getcwd();
  293. $dir=$cwd . '/' .$config->downloader_path . '/'
  294. . \Magento\Connect\Config::DEFAULT_CACHE_PATH . '/' . trim( $pChan, "\\/");
  295. $ftpObj->mkdirRecursive($dir,0777);
  296. $ftpObj->chdir($cwd);
  297. } else {
  298. $dir = $config->getChannelCacheDir($pChan);
  299. @mkdir($dir, 0777, true);
  300. }
  301. $dir = $config->getChannelCacheDir($pChan);
  302. $packageFileName = $pName . "-" . $pVer . ".tgz";
  303. $file = $dir . '/' . $packageFileName;
  304. if (!@file_exists($file)) {
  305. $this->ui()->output("Starting to download $packageFileName ...");
  306. $rest->downloadPackageFileOfRelease($pName, $pVer, $file);
  307. $this->ui()->output(sprintf("...done: %s bytes", number_format(filesize($file))));
  308. }
  309. /**
  310. * Remove old version package before install new
  311. */
  312. if ($cache->hasPackage($pChan, $pName)) {
  313. if ($ftp) {
  314. $packager->processUninstallPackageFtp($pChan, $pName, $cache, $ftpObj);
  315. } else {
  316. $packager->processUninstallPackage($pChan, $pName, $cache, $config);
  317. }
  318. $cache->deletePackage($pChan, $pName);
  319. }
  320. $package = new \Magento\Connect\Package($file);
  321. if ($clearInstallMode && $pInstallState != 'upgrade' && !$installAll) {
  322. $this->validator()->validateContents($package->getContents(), $config);
  323. $errors = $this->validator()->getErrors();
  324. if (count($errors)) {
  325. throw new \Exception("Package '{$pName}' is invalid\n" . implode("\n", $errors));
  326. }
  327. }
  328. $conflicts = $package->checkPhpDependencies();
  329. if (true !== $conflicts) {
  330. $conflicts = implode(",",$conflicts);
  331. $err = "Package {$pChan}/{$pName} {$pVer} depends on PHP extensions: " . $conflicts;
  332. if ($forceMode) {
  333. $this->doError($command, $err);
  334. } else {
  335. throw new \Exception($err);
  336. }
  337. }
  338. $conflicts = $package->checkPhpVersion();
  339. if (true !== $conflicts) {
  340. $err = "Package {$pChan}/{$pName} {$pVer}: " . $conflicts;
  341. if ($forceMode) {
  342. $this->doError($command, $err);
  343. } else {
  344. throw new \Exception($err);
  345. }
  346. }
  347. if (!$noFilesInstall) {
  348. $this->ui()->output("Installing package {$pChan}/{$pName} {$pVer}");
  349. if ($ftp) {
  350. $packager->processInstallPackageFtp($package, $file, $config, $ftpObj);
  351. } else {
  352. $packager->processInstallPackage($package, $file, $config);
  353. }
  354. $this->ui()->output("Package {$pChan}/{$pName} {$pVer} installed successfully");
  355. }
  356. $cache->addPackage($package);
  357. $installedDepsAssoc[] = array('channel'=>$pChan, 'name'=>$pName, 'version'=>$pVer);
  358. $installedDeps[] = array($pChan, $pName, $pVer);
  359. } catch(\Exception $e) {
  360. $this->doError($command, $e->getMessage());
  361. }
  362. }
  363. $title = isset($options['title']) ? $options['title'] : "Package installed: ";
  364. $out = array($command => array('data'=>$installedDeps, 'assoc'=>$installedDepsAssoc, 'title'=>$title));
  365. if ($ftp) {
  366. $packager->writeToRemoteCache($cache, $ftpObj);
  367. @unlink($config->getFilename());
  368. }
  369. $this->ui()->output($out);
  370. return $out[$command]['data'];
  371. } catch (\Exception $e) {
  372. if ($ftp) {
  373. $packager->writeToRemoteCache($cache, $ftpObj);
  374. @unlink($config->getFilename());
  375. }
  376. return $this->doError($command, $e->getMessage());
  377. }
  378. }
  379. /**
  380. * Upgrade action callback
  381. *
  382. * @param string $command
  383. * @param array $options
  384. * @param array $params
  385. * @return array|null
  386. */
  387. public function doUpgrade($command, $options, $params)
  388. {
  389. $options['title'] = "Package upgraded: ";
  390. return $this->doInstall($command, $options, $params);
  391. }
  392. /**
  393. * Updgrade action callback
  394. *
  395. * @param string $command
  396. * @param array $options
  397. * @param array $params
  398. * @return array|null
  399. */
  400. public function doUpgradeAll($command, $options, $params)
  401. {
  402. $options['title'] = "Package upgraded: ";
  403. return $this->doInstall($command, $options, $params);
  404. }
  405. /**
  406. * Uninstall package callback
  407. *
  408. * @param string $command
  409. * @param array $options
  410. * @param array $params
  411. * @return array|null
  412. */
  413. public function doUninstall($command, $options, $params)
  414. {
  415. $this->cleanupParams($params);
  416. try {
  417. if (count($params) != 2) {
  418. throw new \Exception("Argument count should be = 2");
  419. }
  420. $channel = $params[0];
  421. $package = $params[1];
  422. /** @var $packager \Magento\Connect\Packager */
  423. $packager = $this->getPackager();
  424. $withDepsMode = !isset($options['nodeps'])? false : (boolean)$options['nodeps'];
  425. $forceMode = isset($options['force']);
  426. $ftp = empty($options['ftp']) ? false : $options['ftp'];
  427. /** @var $cache \Magento\Connect\Singleconfig */
  428. /** @var $config \Magento\Connect\Config */
  429. /** @var $ftpObj \Magento\Connect\Ftp */
  430. if ($ftp) {
  431. list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
  432. } else {
  433. $cache = $this->getSconfig();
  434. $config = $this->config();
  435. }
  436. $channel = $cache->chanName($channel);
  437. if (!$cache->hasPackage($channel, $package)) {
  438. throw new \Exception("Package is not installed");
  439. }
  440. $deletedPackages = array();
  441. $list = $packager->getUninstallList($channel, $package, $cache, $config, $withDepsMode);
  442. foreach ($list['list'] as $packageData) {
  443. try {
  444. $reqd = $cache->requiredByOtherPackages(
  445. $packageData['channel'],
  446. $packageData['name'],
  447. $list['list']
  448. );
  449. if (count($reqd)) {
  450. $errMessage = "{$packageData['channel']}/{$packageData['name']} "
  451. . "{$packageData['version']} is required by: ";
  452. $t = array();
  453. foreach ($reqd as $r) {
  454. $t[] = $r['channel'] . "/" . $r['name'] . " " . $r['version'];
  455. }
  456. $errMessage .= implode(", ", $t);
  457. if ($forceMode) {
  458. $this->ui()->output("Warning: " . $errMessage);
  459. } else {
  460. throw new \Exception($errMessage);
  461. }
  462. }
  463. } catch(\Exception $e) {
  464. if ($forceMode) {
  465. $this->doError($command, $e->getMessage());
  466. } else {
  467. throw new \Exception($e->getMessage());
  468. }
  469. }
  470. }
  471. foreach ($list['list'] as $packageData) {
  472. try {
  473. list($chan, $pack) = array($packageData['channel'], $packageData['name']);
  474. $packageName = $packageData['channel'] . "/" . $packageData['name'];
  475. $this->ui()->output("Starting to uninstall $packageName ");
  476. if ($ftp) {
  477. $packager->processUninstallPackageFtp($chan, $pack, $cache, $ftpObj);
  478. } else {
  479. $packager->processUninstallPackage($chan, $pack, $cache, $config);
  480. }
  481. $cache->deletePackage($chan, $pack);
  482. $deletedPackages[] = array($chan, $pack);
  483. $this->ui()->output("Package {$packageName} uninstalled");
  484. } catch(\Exception $e) {
  485. if ($forceMode) {
  486. $this->doError($command, $e->getMessage());
  487. } else {
  488. throw new \Exception($e->getMessage());
  489. }
  490. }
  491. }
  492. if ($ftp) {
  493. $packager->writeToRemoteCache($cache, $ftpObj);
  494. @unlink($config->getFilename());
  495. }
  496. $out = array($command=>array('data'=>$deletedPackages, 'title'=>'Package deleted: '));
  497. $this->ui()->output($out);
  498. return $out[$command]['data'];
  499. } catch (\Exception $e) {
  500. return $this->doError($command, $e->getMessage());
  501. }
  502. }
  503. }