You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 line
6.1KB

  1. <?php
  2. /*
  3. * This file is part of the Fxp Composer Asset Plugin package.
  4. *
  5. * (c) François Pluchino <francois.pluchino@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Fxp\Composer\AssetPlugin\Repository\Vcs;
  11. use Composer\Cache;
  12. use Composer\Downloader\TransportException;
  13. use Composer\Json\JsonFile;
  14. use Composer\Repository\Vcs\GitHubDriver as BaseGitHubDriver;
  15. /**
  16. * Abstract class for GitHub vcs driver.
  17. *
  18. * @author François Pluchino <francois.pluchino@gmail.com>
  19. */
  20. abstract class AbstractGitHubDriver extends BaseGitHubDriver
  21. {
  22. /**
  23. * @var Cache
  24. */
  25. protected $cache;
  26. /**
  27. * @var string|null|false
  28. */
  29. protected $redirectApi;
  30. /**
  31. * Get the remote content.
  32. *
  33. * @param string $url The URL of content
  34. * @param bool $fetchingRepoData Fetching the repo data or not
  35. *
  36. * @return mixed The result
  37. */
  38. protected function getContents($url, $fetchingRepoData = false)
  39. {
  40. $url = $this->getValidContentUrl($url);
  41. if (null !== $this->redirectApi) {
  42. return parent::getContents($url, $fetchingRepoData);
  43. }
  44. try {
  45. $contents = $this->getRemoteContents($url);
  46. $this->redirectApi = false;
  47. return $contents;
  48. } catch (TransportException $e) {
  49. if ($this->hasRedirectUrl($url)) {
  50. $url = $this->getValidContentUrl($url);
  51. }
  52. return parent::getContents($url, $fetchingRepoData);
  53. }
  54. }
  55. /**
  56. * @param string $url The url
  57. *
  58. * @return string The url redirected
  59. */
  60. protected function getValidContentUrl($url)
  61. {
  62. if (null === $this->redirectApi && false !== $redirectApi = $this->cache->read('redirect-api')) {
  63. $this->redirectApi = $redirectApi;
  64. }
  65. if (is_string($this->redirectApi) && 0 === strpos($url, $this->getRepositoryApiUrl())) {
  66. $url = $this->redirectApi.substr($url, strlen($this->getRepositoryApiUrl()));
  67. }
  68. return $url;
  69. }
  70. /**
  71. * Check if the driver must find the new url.
  72. *
  73. * @param string $url The url
  74. *
  75. * @return bool
  76. */
  77. protected function hasRedirectUrl($url)
  78. {
  79. if (null === $this->redirectApi && 0 === strpos($url, $this->getRepositoryApiUrl())) {
  80. $this->redirectApi = $this->getNewRepositoryUrl();
  81. if (is_string($this->redirectApi)) {
  82. $this->cache->write('redirect-api', $this->redirectApi);
  83. }
  84. }
  85. return is_string($this->redirectApi);
  86. }
  87. /**
  88. * Get the new url of repository.
  89. *
  90. * @return string|false The new url or false if there is not a new url
  91. */
  92. protected function getNewRepositoryUrl()
  93. {
  94. try {
  95. $this->getRemoteContents($this->getRepositoryUrl());
  96. $headers = $this->remoteFilesystem->getLastHeaders();
  97. if (!empty($headers[0]) && preg_match('{^HTTP/\S+ (30[1278])}i', $headers[0], $match)) {
  98. array_shift($headers);
  99. return $this->findNewLocationInHeader($headers);
  100. }
  101. return false;
  102. } catch (\Exception $ex) {
  103. return false;
  104. }
  105. }
  106. /**
  107. * Find the new url api in the header.
  108. *
  109. * @param array $headers The http header
  110. *
  111. * @return string|false
  112. */
  113. protected function findNewLocationInHeader(array $headers)
  114. {
  115. $url = false;
  116. foreach ($headers as $header) {
  117. if (0 === strpos($header, 'Location:')) {
  118. $newUrl = trim(substr($header, 9));
  119. preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $newUrl, $match);
  120. $owner = $match[3];
  121. $repository = $match[4];
  122. $paramPos = strpos($repository, '?');
  123. $repository = is_int($paramPos) ? substr($match[4], 0, $paramPos) : $repository;
  124. $url = $this->getRepositoryApiUrl($owner, $repository);
  125. break;
  126. }
  127. }
  128. return $url;
  129. }
  130. /**
  131. * Get the url API of the repository.
  132. *
  133. * @param string $owner
  134. * @param string $repository
  135. *
  136. * @return string
  137. */
  138. protected function getRepositoryApiUrl($owner = null, $repository = null)
  139. {
  140. $owner = null !== $owner ? $owner : $this->owner;
  141. $repository = null !== $repository ? $repository : $this->repository;
  142. return $this->getApiUrl().'/repos/'.$owner.'/'.$repository;
  143. }
  144. /**
  145. * Get the remote content.
  146. *
  147. * @param string $url
  148. *
  149. * @return bool|string
  150. */
  151. protected function getRemoteContents($url)
  152. {
  153. return $this->remoteFilesystem->getContents($this->originUrl, $url, false);
  154. }
  155. /**
  156. * {@inheritdoc}
  157. */
  158. public function getBranches()
  159. {
  160. if ($this->gitDriver) {
  161. return $this->gitDriver->getBranches();
  162. }
  163. if (null === $this->branches) {
  164. $this->branches = array();
  165. $resource = $this->getApiUrl().'/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100';
  166. $branchBlacklist = 'gh-pages' === $this->getRootIdentifier() ? array() : array('gh-pages');
  167. $this->doAddBranches($resource, $branchBlacklist);
  168. }
  169. return $this->branches;
  170. }
  171. /**
  172. * Push the list of all branch.
  173. *
  174. * @param string $resource
  175. * @param array $branchBlacklist
  176. */
  177. protected function doAddBranches($resource, array $branchBlacklist)
  178. {
  179. do {
  180. $branchData = JsonFile::parseJson((string) $this->getContents($resource), $resource);
  181. foreach ($branchData as $branch) {
  182. $name = substr($branch['ref'], 11);
  183. if (!in_array($name, $branchBlacklist)) {
  184. $this->branches[$name] = $branch['object']['sha'];
  185. }
  186. }
  187. $resource = $this->getNextPage();
  188. } while ($resource);
  189. }
  190. }