Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

280 lines
9.1KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\composer;
  8. use Composer\Package\PackageInterface;
  9. use Composer\Installer\LibraryInstaller;
  10. use Composer\Repository\InstalledRepositoryInterface;
  11. use Composer\Script\CommandEvent;
  12. use Composer\Util\Filesystem;
  13. /**
  14. * @author Qiang Xue <qiang.xue@gmail.com>
  15. * @since 2.0
  16. */
  17. class Installer extends LibraryInstaller
  18. {
  19. const EXTRA_BOOTSTRAP = 'bootstrap';
  20. const EXTENSION_FILE = 'yiisoft/extensions.php';
  21. /**
  22. * @inheritdoc
  23. */
  24. public function supports($packageType)
  25. {
  26. return $packageType === 'yii2-extension';
  27. }
  28. /**
  29. * @inheritdoc
  30. */
  31. public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
  32. {
  33. // install the package the normal composer way
  34. parent::install($repo, $package);
  35. // add the package to yiisoft/extensions.php
  36. $this->addPackage($package);
  37. // ensure the yii2-dev package also provides Yii.php in the same place as yii2 does
  38. if ($package->getName() == 'yiisoft/yii2-dev') {
  39. $this->linkBaseYiiFiles();
  40. }
  41. }
  42. /**
  43. * @inheritdoc
  44. */
  45. public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
  46. {
  47. parent::update($repo, $initial, $target);
  48. $this->removePackage($initial);
  49. $this->addPackage($target);
  50. // ensure the yii2-dev package also provides Yii.php in the same place as yii2 does
  51. if ($initial->getName() == 'yiisoft/yii2-dev') {
  52. $this->linkBaseYiiFiles();
  53. }
  54. }
  55. /**
  56. * @inheritdoc
  57. */
  58. public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
  59. {
  60. // uninstall the package the normal composer way
  61. parent::uninstall($repo, $package);
  62. // remove the package from yiisoft/extensions.php
  63. $this->removePackage($package);
  64. // remove links for Yii.php
  65. if ($package->getName() == 'yiisoft/yii2-dev') {
  66. $this->removeBaseYiiFiles();
  67. }
  68. }
  69. protected function addPackage(PackageInterface $package)
  70. {
  71. $extension = [
  72. 'name' => $package->getName(),
  73. 'version' => $package->getVersion(),
  74. ];
  75. $alias = $this->generateDefaultAlias($package);
  76. if (!empty($alias)) {
  77. $extension['alias'] = $alias;
  78. }
  79. $extra = $package->getExtra();
  80. if (isset($extra[self::EXTRA_BOOTSTRAP])) {
  81. $extension['bootstrap'] = $extra[self::EXTRA_BOOTSTRAP];
  82. }
  83. $extensions = $this->loadExtensions();
  84. $extensions[$package->getName()] = $extension;
  85. $this->saveExtensions($extensions);
  86. }
  87. protected function generateDefaultAlias(PackageInterface $package)
  88. {
  89. $fs = new Filesystem;
  90. $vendorDir = $fs->normalizePath($this->vendorDir);
  91. $autoload = $package->getAutoload();
  92. $aliases = [];
  93. if (!empty($autoload['psr-0'])) {
  94. foreach ($autoload['psr-0'] as $name => $path) {
  95. $name = str_replace('\\', '/', trim($name, '\\'));
  96. if (!$fs->isAbsolutePath($path)) {
  97. $path = $this->vendorDir . '/' . $package->getPrettyName() . '/' . $path;
  98. }
  99. $path = $fs->normalizePath($path);
  100. if (strpos($path . '/', $vendorDir . '/') === 0) {
  101. $aliases["@$name"] = '<vendor-dir>' . substr($path, strlen($vendorDir)) . '/' . $name;
  102. } else {
  103. $aliases["@$name"] = $path . '/' . $name;
  104. }
  105. }
  106. }
  107. if (!empty($autoload['psr-4'])) {
  108. foreach ($autoload['psr-4'] as $name => $path) {
  109. $name = str_replace('\\', '/', trim($name, '\\'));
  110. if (!$fs->isAbsolutePath($path)) {
  111. $path = $this->vendorDir . '/' . $package->getPrettyName() . '/' . $path;
  112. }
  113. $path = $fs->normalizePath($path);
  114. if (strpos($path . '/', $vendorDir . '/') === 0) {
  115. $aliases["@$name"] = '<vendor-dir>' . substr($path, strlen($vendorDir));
  116. } else {
  117. $aliases["@$name"] = $path;
  118. }
  119. }
  120. }
  121. return $aliases;
  122. }
  123. protected function removePackage(PackageInterface $package)
  124. {
  125. $packages = $this->loadExtensions();
  126. unset($packages[$package->getName()]);
  127. $this->saveExtensions($packages);
  128. }
  129. protected function loadExtensions()
  130. {
  131. $file = $this->vendorDir . '/' . self::EXTENSION_FILE;
  132. if (!is_file($file)) {
  133. return [];
  134. }
  135. // invalidate opcache of extensions.php if exists
  136. if (function_exists('opcache_invalidate')) {
  137. opcache_invalidate($file, true);
  138. }
  139. $extensions = require($file);
  140. $vendorDir = str_replace('\\', '/', $this->vendorDir);
  141. $n = strlen($vendorDir);
  142. foreach ($extensions as &$extension) {
  143. if (isset($extension['alias'])) {
  144. foreach ($extension['alias'] as $alias => $path) {
  145. $path = str_replace('\\', '/', $path);
  146. if (strpos($path . '/', $vendorDir . '/') === 0) {
  147. $extension['alias'][$alias] = '<vendor-dir>' . substr($path, $n);
  148. }
  149. }
  150. }
  151. }
  152. return $extensions;
  153. }
  154. protected function saveExtensions(array $extensions)
  155. {
  156. $file = $this->vendorDir . '/' . self::EXTENSION_FILE;
  157. if (!file_exists(dirname($file))) {
  158. mkdir(dirname($file), 0777, true);
  159. }
  160. $array = str_replace("'<vendor-dir>", '$vendorDir . \'', var_export($extensions, true));
  161. file_put_contents($file, "<?php\n\n\$vendorDir = dirname(__DIR__);\n\nreturn $array;\n");
  162. // invalidate opcache of extensions.php if exists
  163. if (function_exists('opcache_invalidate')) {
  164. opcache_invalidate($file, true);
  165. }
  166. }
  167. protected function linkBaseYiiFiles()
  168. {
  169. $yiiDir = $this->vendorDir . '/yiisoft/yii2';
  170. if (!file_exists($yiiDir)) {
  171. mkdir($yiiDir, 0777, true);
  172. }
  173. foreach (['Yii.php', 'BaseYii.php', 'classes.php'] as $file) {
  174. file_put_contents($yiiDir . '/' . $file, <<<EOF
  175. <?php
  176. /**
  177. * This is a link provided by the yiisoft/yii2-dev package via yii2-composer plugin.
  178. *
  179. * @link http://www.yiiframework.com/
  180. * @copyright Copyright (c) 2008 Yii Software LLC
  181. * @license http://www.yiiframework.com/license/
  182. */
  183. return require(__DIR__ . '/../yii2-dev/framework/$file');
  184. EOF
  185. );
  186. }
  187. }
  188. protected function removeBaseYiiFiles()
  189. {
  190. $yiiDir = $this->vendorDir . '/yiisoft/yii2';
  191. foreach (['Yii.php', 'BaseYii.php', 'classes.php'] as $file) {
  192. if (file_exists($yiiDir . '/' . $file)) {
  193. unlink($yiiDir . '/' . $file);
  194. }
  195. }
  196. if (file_exists($yiiDir)) {
  197. rmdir($yiiDir);
  198. }
  199. }
  200. public static function postCreateProject($event)
  201. {
  202. $params = $event->getComposer()->getPackage()->getExtra();
  203. if (isset($params[__METHOD__]) && is_array($params[__METHOD__])) {
  204. foreach ($params[__METHOD__] as $method => $args) {
  205. call_user_func_array([__CLASS__, $method], (array) $args);
  206. }
  207. }
  208. }
  209. /**
  210. * Sets the correct permission for the files and directories listed in the extra section.
  211. * @param array $paths the paths (keys) and the corresponding permission octal strings (values)
  212. */
  213. public static function setPermission(array $paths)
  214. {
  215. foreach ($paths as $path => $permission) {
  216. echo "chmod('$path', $permission)...";
  217. if (is_dir($path) || is_file($path)) {
  218. chmod($path, octdec($permission));
  219. echo "done.\n";
  220. } else {
  221. echo "file not found.\n";
  222. }
  223. }
  224. }
  225. /**
  226. * Generates a cookie validation key for every app config listed in "config" in extra section.
  227. * You can provide one or multiple parameters as the configuration files which need to have validation key inserted.
  228. */
  229. public static function generateCookieValidationKey()
  230. {
  231. $configs = func_get_args();
  232. $key = self::generateRandomString();
  233. foreach ($configs as $config) {
  234. if (is_file($config)) {
  235. $content = preg_replace('/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/', "\\1'$key'", file_get_contents($config));
  236. file_put_contents($config, $content);
  237. }
  238. }
  239. }
  240. protected static function generateRandomString()
  241. {
  242. if (!extension_loaded('openssl')) {
  243. throw new \Exception('The OpenSSL PHP extension is required by Yii2.');
  244. }
  245. $length = 32;
  246. $bytes = openssl_random_pseudo_bytes($length);
  247. return strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.');
  248. }
  249. }