• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mborne / remote-git / 23622402787

26 Mar 2026 11:05PM UTC coverage: 91.473% (-1.4%) from 92.857%
23622402787

push

github

web-flow
Merge pull request #46 from mborne/upgrade_test

Improve tests

1 of 1 new or added line in 1 file covered. (100.0%)

10 existing lines in 3 files now uncovered.

354 of 387 relevant lines covered (91.47%)

2.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.36
/src/ClientFactory.php
1
<?php
2

3
namespace MBO\RemoteGit;
4

5
use GuzzleHttp\Client as GuzzleHttpClient;
6
use MBO\RemoteGit\Exception\ClientNotFoundException;
7
use MBO\RemoteGit\Exception\ProtocolNotSupportedException;
8
use MBO\RemoteGit\Github\GithubClient;
9
use MBO\RemoteGit\Gitlab\GitlabClient;
10
use MBO\RemoteGit\Gogs\GogsClient;
11
use MBO\RemoteGit\Helper\ClientHelper;
12
use MBO\RemoteGit\Helper\LoggerHelper;
13
use MBO\RemoteGit\Http\TokenType;
14
use Psr\Log\LoggerInterface;
15

16
/**
17
 * Helper to create clients according to URL.
18
 *
19
 * Note that it rely on a static interface on clients (TYPE and TOKEN_TYPE)
20
 *
21
 * @author mborne
22
 */
23
class ClientFactory
24
{
25
    /**
26
     * @var ClientFactory
27
     */
28
    private static $instance;
29

30
    /**
31
     * Associates client type to metadata ('className','tokenType').
32
     *
33
     * @var array<string,array<string,string>>
34
     */
35
    private $types = [];
36

37
    private function __construct()
38
    {
39
        $this->register(GitlabClient::class);
1✔
40
        $this->register(GithubClient::class);
1✔
41
        $this->register(GogsClient::class);
1✔
42
    }
43

44
    /**
45
     * True if type is registred.
46
     *
47
     * @param string $type
48
     */
49
    public function hasType($type): bool
50
    {
51
        return isset($this->types[$type]);
9✔
52
    }
53

54
    /**
55
     * Get supported types.
56
     *
57
     * @return string[]
58
     */
59
    public function getTypes(): array
60
    {
61
        return array_keys($this->types);
2✔
62
    }
63

64
    /**
65
     * Create a client with options.
66
     */
67
    public static function createClient(
68
        ClientOptions $options,
69
        ?LoggerInterface $logger = null,
70
    ): ClientInterface {
71
        return self::getInstance()->createGitClient($options, $logger);
7✔
72
    }
73

74
    /**
75
     * Create a client with options.
76
     */
77
    public function createGitClient(
78
        ClientOptions $options,
79
        ?LoggerInterface $logger = null,
80
    ): ClientInterface {
81
        $logger = LoggerHelper::handleNull($logger);
8✔
82

83
        /* Detect client type from URL if not specified */
84
        if (!$options->hasType()) {
8✔
85
            $className = self::detectClientClass($options->getUrl());
5✔
86
            $options->setType($className::TYPE);
5✔
87
            $logger->debug(sprintf(
5✔
88
                'Type %s found for %s',
5✔
89
                $options->getType(),
5✔
90
                $options->getUrl()
5✔
91
            ));
5✔
92
        }
93

94
        /* Ensure that type exists */
95
        if (!$this->hasType($options->getType())) {
8✔
96
            throw new ClientNotFoundException($options->getType(), $this->getTypes());
1✔
97
        }
98

99
        /* handle removed LocalClient */
100
        if ('local' === $options->getType()) {
7✔
UNCOV
101
            throw new ClientNotFoundException($options->getType(), $this->getTypes());
×
102
        }
103

104
        /* Force github API URL */
105
        if (GithubClient::TYPE === $options->getType()) {
7✔
106
            $options->setUrl('https://api.github.com');
2✔
107
        }
108

109
        /* Retrieve type metadata */
110
        $metadata = $this->types[$options->getType()];
7✔
111
        $clientClass = $metadata['className'];
7✔
112
        $tokenType = $metadata['tokenType'];
7✔
113

114
        /* common http options */
115
        $guzzleOptions = [
7✔
116
            'base_uri' => $options->getUrl(),
7✔
117
            'timeout' => 60.0,
7✔
118
            'headers' => TokenType::createHttpHeaders(
7✔
119
                $tokenType,
7✔
120
                $options->getToken()
7✔
121
            ),
7✔
122
        ];
7✔
123
        /* disable SSL checks */
124
        if ($options->isUnsafeSsl()) {
7✔
UNCOV
125
            $guzzleOptions['verify'] = false;
×
126
        }
127

128
        /* create http client */
129
        $httpClient = new GuzzleHttpClient($guzzleOptions);
7✔
130
        /* create git client */
131
        $result = new $clientClass($httpClient, $logger);
7✔
132
        assert($result instanceof ClientInterface);
133

134
        return $result;
7✔
135
    }
136

137
    /**
138
     * Get client class according to URL content.
139
     */
140
    public static function detectClientClass(string $url): string
141
    {
142
        $scheme = parse_url($url, PHP_URL_SCHEME);
8✔
143
        if (!is_string($scheme)) {
8✔
144
            $scheme = 'file';
1✔
145
        }
146
        if (!in_array($scheme, ['http', 'https'])) {
8✔
147
            throw new ProtocolNotSupportedException($scheme, $url);
2✔
148
        }
149

150
        $hostname = parse_url($url, PHP_URL_HOST);
6✔
151
        assert('string' === gettype($hostname));
152
        if ('api.github.com' === $hostname || 'github.com' === $hostname) {
6✔
153
            return GithubClient::class;
3✔
154
        } elseif (str_contains($hostname, 'gogs')) {
4✔
155
            return GogsClient::class;
1✔
156
        } elseif (str_contains($hostname, 'gitea')) {
4✔
157
            return GogsClient::class;
1✔
158
        }
159

160
        /*
161
         * fallback to gitlab to ensure comptability with original version
162
         * of satis-gitlab
163
         */
164
        return GitlabClient::class;
4✔
165
    }
166

167
    /**
168
     * @return ClientFactory
169
     */
170
    public static function getInstance()
171
    {
172
        if (null == self::$instance) {
10✔
173
            self::$instance = new ClientFactory();
1✔
174
        }
175

176
        return self::$instance;
10✔
177
    }
178

179
    /**
180
     * Register client type.
181
     *
182
     * @param class-string $className
183
     */
184
    private function register(string $className): void
185
    {
186
        $clientProperties = ClientHelper::getStaticProperties($className);
1✔
187
        $this->types[$clientProperties['typeName']] = $clientProperties;
1✔
188
    }
189
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc