Completed
Push — master ( 221a44...71b082 )
by smiley
01:38
created

HTTPOptionsTrait   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 166
Duplicated Lines 4.82 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 8
loc 166
rs 10
c 0
b 0
f 0
wmc 27
lcom 1
cbo 1

2 Methods

Rating   Name   Duplication   Size   Complexity  
A HTTPOptionsTrait() 0 12 4
F setCA() 8 105 23

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Trait HTTPOptionsTrait
4
 *
5
 * @filesource   HTTPOptionsTrait.php
6
 * @created      28.08.2018
7
 * @package      chillerlan\HTTP
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2018 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\HTTP;
14
15
trait HTTPOptionsTrait{
16
17
	/**
18
	 * @var string
19
	 */
20
	protected $user_agent = 'chillerlanHttpInterface/2.0 +https://github.com/chillerlan/php-httpinterface';
21
22
	/**
23
	 * options for each curl instance
24
	 *
25
	 * this array is being merged into the default options as the last thing before curl_exec().
26
	 * none of the values (except existence of the CA file) will be checked - that's up to the implementation.
27
	 *
28
	 * @var array
29
	 */
30
	protected $curl_options = [];
31
32
	/**
33
	 * CA Root Certificates for use with CURL/SSL (if not configured in php.ini or available in a default path)
34
	 *
35
	 * @var string
36
	 *
37
	 * @link https://curl.haxx.se/docs/caextract.html
38
	 * @link https://curl.haxx.se/ca/cacert.pem
39
	 * @link https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt
40
	 */
41
	protected $ca_info = null;
42
43
	/**
44
	 * see CURLOPT_SSL_VERIFYPEER
45
	 * requires either HTTPOptions::$ca_info or a properly working system CA file
46
	 *
47
	 * @var bool
48
	 * @link http://php.net/manual/en/function.curl-setopt.php
49
	 */
50
	protected $ssl_verifypeer = true;
51
52
	/**
53
	 * HTTPOptionsTrait constructor
54
	 *
55
	 * @throws \Psr\Http\Client\ClientExceptionInterface
56
	 */
57
	protected function HTTPOptionsTrait():void{
58
59
		if(!is_array($this->curl_options)){
60
			$this->curl_options = [];
61
		}
62
63
		if(!is_string($this->user_agent) || empty(trim($this->user_agent))){
64
			throw new ClientException('invalid user agent');
65
		}
66
67
		$this->setCA();
68
	}
69
70
	/**
71
	 * @return void
72
	 * @throws \Psr\Http\Client\ClientExceptionInterface
73
	 */
74
	protected function setCA():void{
75
76
		// disable verification if wanted so
77
		if($this->ssl_verifypeer !== true || (isset($this->curl_options[CURLOPT_SSL_VERIFYPEER]) && !$this->curl_options[CURLOPT_SSL_VERIFYPEER])){
78
			unset($this->curl_options[CURLOPT_CAINFO], $this->curl_options[CURLOPT_CAPATH]);
79
80
			$this->curl_options[CURLOPT_SSL_VERIFYHOST] = 0;
81
			$this->curl_options[CURLOPT_SSL_VERIFYPEER] = false;
82
83
			return;
84
		}
85
86
		$this->curl_options[CURLOPT_SSL_VERIFYHOST] = 2;
87
		$this->curl_options[CURLOPT_SSL_VERIFYPEER] = true;
88
89
		// a path/dir/link to a CA bundle is given, let's check that
90
		if(is_string($this->ca_info)){
91
92
			// if you - for whatever obscure reason - need to check Windows .lnk links,
93
			// see http://php.net/manual/en/function.is-link.php#91249
94
			switch(true){
95
				case is_dir($this->ca_info):
96 View Code Duplication
				case is_link($this->ca_info) && is_dir(readlink($this->ca_info)): // @codeCoverageIgnore
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
97
					$this->curl_options[CURLOPT_CAPATH] = $this->ca_info;
98
					unset($this->curl_options[CURLOPT_CAINFO]);
99
					return;
100
101
				case is_file($this->ca_info):
102 View Code Duplication
				case is_link($this->ca_info) && is_file(readlink($this->ca_info)): // @codeCoverageIgnore
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
					$this->curl_options[CURLOPT_CAINFO] = $this->ca_info;
104
					unset($this->curl_options[CURLOPT_CAPATH]);
105
					return;
106
			}
107
108
			throw new ClientException('invalid path to SSL CA bundle (HTTPOptions::$ca_info): '.$this->ca_info);
109
		}
110
111
		// we somehow landed here, so let's check if there's a CA bundle given via the cURL options
112
		$ca = $this->curl_options[CURLOPT_CAPATH] ?? $this->curl_options[CURLOPT_CAINFO] ?? false;
113
114
		if($ca){
115
116
			// just check if the file/path exists
117
			switch(true){
118
				case is_dir($ca):
119
				case is_link($ca) && is_dir(readlink($ca)): // @codeCoverageIgnore
120
					unset($this->curl_options[CURLOPT_CAINFO]);
121
					return;
122
123
				case is_file($ca):
124
				case is_link($ca) && is_file(readlink($ca)): // @codeCoverageIgnore
125
					return;
126
			}
127
128
			throw new ClientException('invalid path to SSL CA bundle (CURLOPT_CAPATH/CURLOPT_CAINFO): '.$ca);
129
		}
130
131
		// check php.ini options - PHP should find the file by itself
132
		if(file_exists(ini_get('curl.cainfo'))){
133
			return; // @codeCoverageIgnore
134
		}
135
136
		// this is getting weird. as a last resort, we're going to check some default paths for a CA bundle file
137
		$cafiles = [
138
			// check other php.ini settings
139
			ini_get('openssl.cafile'),
140
			// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
141
			'/etc/pki/tls/certs/ca-bundle.crt',
142
			// Ubuntu, Debian (provided by the ca-certificates package)
143
			'/etc/ssl/certs/ca-certificates.crt',
144
			// FreeBSD (provided by the ca_root_nss package)
145
			'/usr/local/share/certs/ca-root-nss.crt',
146
			// SLES 12 (provided by the ca-certificates package)
147
			'/var/lib/ca-certificates/ca-bundle.pem',
148
			// OS X provided by homebrew (using the default path)
149
			'/usr/local/etc/openssl/cert.pem',
150
			// Google app engine
151
			'/etc/ca-certificates.crt',
152
			// Windows?
153
			// http://php.net/manual/en/function.curl-setopt.php#110457
154
			'C:\\Windows\\system32\\curl-ca-bundle.crt',
155
			'C:\\Windows\\curl-ca-bundle.crt',
156
			'C:\\Windows\\system32\\cacert.pem',
157
			'C:\\Windows\\cacert.pem',
158
			// working path
159
			__DIR__.'/cacert.pem',
160
		];
161
162
		foreach($cafiles as $file){
163
			if(is_file($file) || (is_link($file) && is_file(readlink($file)))){
164
				$this->curl_options[CURLOPT_CAINFO] = $file;
165
				return;
166
			}
167
		}
168
169
		$msg = 'No system CA bundle could be found in any of the the common system locations. '
170
			.'In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle via  '
171
			.'HTTPOptions::$ca_info or HTTPOptions::$curl_options. If you do not need a specific certificate bundle, '
172
			.'then you can download a CA bundle over here: https://curl.haxx.se/docs/caextract.html. '
173
			.'Once you have a CA bundle available on disk, you can set the "curl.cainfo" php.ini setting to point '
174
			.'to the path to the file, allowing you to omit the $ca_info or $curl_options setting. '
175
			.'See http://curl.haxx.se/docs/sslcerts.html for more information.';
176
177
		throw new ClientException($msg); // @codeCoverageIgnore
178
	}
179
180
}
181