friendica-addons/advancedcontentfilter/vendor/fxp/composer-asset-plugin/Repository/Vcs/AbstractGitHubDriver.php

258 lines
7.1 KiB
PHP

<?php
/*
* This file is part of the Fxp Composer Asset Plugin package.
*
* (c) François Pluchino <francois.pluchino@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Fxp\Composer\AssetPlugin\Repository\Vcs;
use Composer\Cache;
use Composer\Downloader\TransportException;
use Composer\Json\JsonFile;
use Composer\Repository\Vcs\GitHubDriver as BaseGitHubDriver;
use Fxp\Composer\AssetPlugin\Repository\Util as RepoUtil;
/**
* Abstract class for GitHub vcs driver.
*
* @author François Pluchino <francois.pluchino@gmail.com>
*/
abstract class AbstractGitHubDriver extends BaseGitHubDriver
{
/**
* @var Cache
*/
protected $cache;
/**
* @var string|null|false
*/
protected $redirectApi;
public function initialize()
{
if (!isset($this->repoConfig['no-api'])) {
$this->repoConfig['no-api'] = $this->getNoApiOption();
}
parent::initialize();
}
/**
* Get the no-api repository option.
*
* @return bool
*/
protected function getNoApiOption()
{
$packageName = $this->repoConfig['package-name'];
$opts = RepoUtil::getArrayValue($this->repoConfig, 'vcs-driver-options', array());
$noApiOpt = RepoUtil::getArrayValue($opts, 'github-no-api', array());
$defaultValue = false;
if (is_bool($noApiOpt)) {
$defaultValue = $noApiOpt;
$noApiOpt = array();
}
$noApiOpt['default'] = (bool) RepoUtil::getArrayValue($noApiOpt, 'default', $defaultValue);
$noApiOpt['packages'] = (array) RepoUtil::getArrayValue($noApiOpt, 'packages', array());
return (bool) RepoUtil::getArrayValue($noApiOpt['packages'], $packageName, $defaultValue);
}
/**
* Get the remote content.
*
* @param string $url The URL of content
* @param bool $fetchingRepoData Fetching the repo data or not
*
* @return mixed The result
*/
protected function getContents($url, $fetchingRepoData = false)
{
$url = $this->getValidContentUrl($url);
if (null !== $this->redirectApi) {
return parent::getContents($url, $fetchingRepoData);
}
try {
$contents = $this->getRemoteContents($url);
$this->redirectApi = false;
return $contents;
} catch (TransportException $e) {
if ($this->hasRedirectUrl($url)) {
$url = $this->getValidContentUrl($url);
}
return parent::getContents($url, $fetchingRepoData);
}
}
/**
* @param string $url The url
*
* @return string The url redirected
*/
protected function getValidContentUrl($url)
{
if (null === $this->redirectApi && false !== $redirectApi = $this->cache->read('redirect-api')) {
$this->redirectApi = $redirectApi;
}
if (is_string($this->redirectApi) && 0 === strpos($url, $this->getRepositoryApiUrl())) {
$url = $this->redirectApi.substr($url, strlen($this->getRepositoryApiUrl()));
}
return $url;
}
/**
* Check if the driver must find the new url.
*
* @param string $url The url
*
* @return bool
*/
protected function hasRedirectUrl($url)
{
if (null === $this->redirectApi && 0 === strpos($url, $this->getRepositoryApiUrl())) {
$this->redirectApi = $this->getNewRepositoryUrl();
if (is_string($this->redirectApi)) {
$this->cache->write('redirect-api', $this->redirectApi);
}
}
return is_string($this->redirectApi);
}
/**
* Get the new url of repository.
*
* @return string|false The new url or false if there is not a new url
*/
protected function getNewRepositoryUrl()
{
try {
$this->getRemoteContents($this->getRepositoryUrl());
$headers = $this->remoteFilesystem->getLastHeaders();
if (!empty($headers[0]) && preg_match('{^HTTP/\S+ (30[1278])}i', $headers[0], $match)) {
array_shift($headers);
return $this->findNewLocationInHeader($headers);
}
return false;
} catch (\Exception $ex) {
return false;
}
}
/**
* Find the new url api in the header.
*
* @param array $headers The http header
*
* @return string|false
*/
protected function findNewLocationInHeader(array $headers)
{
$url = false;
foreach ($headers as $header) {
if (0 === strpos($header, 'Location:')) {
$newUrl = trim(substr($header, 9));
preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $newUrl, $match);
$owner = $match[3];
$repository = $match[4];
$paramPos = strpos($repository, '?');
$repository = is_int($paramPos) ? substr($match[4], 0, $paramPos) : $repository;
$url = $this->getRepositoryApiUrl($owner, $repository);
break;
}
}
return $url;
}
/**
* Get the url API of the repository.
*
* @param string $owner
* @param string $repository
*
* @return string
*/
protected function getRepositoryApiUrl($owner = null, $repository = null)
{
$owner = null !== $owner ? $owner : $this->owner;
$repository = null !== $repository ? $repository : $this->repository;
return $this->getApiUrl().'/repos/'.$owner.'/'.$repository;
}
/**
* Get the remote content.
*
* @param string $url
*
* @return bool|string
*/
protected function getRemoteContents($url)
{
return $this->remoteFilesystem->getContents($this->originUrl, $url, false);
}
/**
* {@inheritdoc}
*/
public function getBranches()
{
if ($this->gitDriver) {
return $this->gitDriver->getBranches();
}
if (null === $this->branches) {
$this->branches = array();
$resource = $this->getApiUrl().'/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100';
$branchBlacklist = 'gh-pages' === $this->getRootIdentifier() ? array() : array('gh-pages');
$this->doAddBranches($resource, $branchBlacklist);
}
return $this->branches;
}
/**
* Push the list of all branch.
*
* @param string $resource
* @param array $branchBlacklist
*/
protected function doAddBranches($resource, array $branchBlacklist)
{
do {
$branchData = JsonFile::parseJson((string) $this->getContents($resource), $resource);
foreach ($branchData as $branch) {
$name = substr($branch['ref'], 11);
if (!in_array($name, $branchBlacklist)) {
$this->branches[$name] = $branch['object']['sha'];
}
}
$resource = $this->getNextPage();
} while ($resource);
}
}