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

HTTPOptionsTrait::setCA()   F

Complexity

Conditions 23
Paths 15

Size

Total Lines 105

Duplication

Lines 8
Ratio 7.62 %

Importance

Changes 0
Metric Value
cc 23
nc 15
nop 0
dl 8
loc 105
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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