1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/* |
3
|
|
|
* ***************************************************************************** |
4
|
|
|
* Contributions to this work were made on behalf of the GÉANT project, a |
5
|
|
|
* project that has received funding from the European Union’s Framework |
6
|
|
|
* Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus), |
7
|
|
|
* Horizon 2020 research and innovation programme under Grant Agreements No. |
8
|
|
|
* 691567 (GN4-1) and No. 731122 (GN4-2). |
9
|
|
|
* On behalf of the aforementioned projects, GEANT Association is the sole owner |
10
|
|
|
* of the copyright in all material which was developed by a member of the GÉANT |
11
|
|
|
* project. GÉANT Vereniging (Association) is registered with the Chamber of |
12
|
|
|
* Commerce in Amsterdam with registration number 40535155 and operates in the |
13
|
|
|
* UK as a branch of GÉANT Vereniging. |
14
|
|
|
* |
15
|
|
|
* Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. |
16
|
|
|
* UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK |
17
|
|
|
* |
18
|
|
|
* License: see the web/copyright.inc.php file in the file structure or |
19
|
|
|
* <base_url>/copyright.php after deploying the software |
20
|
|
|
*/ |
21
|
|
|
|
22
|
|
|
namespace core\diag; |
23
|
|
|
|
24
|
|
|
use \Exception; |
25
|
|
|
|
26
|
|
|
require_once dirname(dirname(__DIR__)) . "/config/_config.php"; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Test suite to verify that a given NAI realm has NAPTR records according to |
30
|
|
|
* consortium-agreed criteria |
31
|
|
|
* Can only be used if CONFIG_DIAGNOSTICS['RADIUSTESTS'] is configured. |
32
|
|
|
* |
33
|
|
|
* @author Stefan Winter <[email protected]> |
34
|
|
|
* @author Tomasz Wolniewicz <[email protected]> |
35
|
|
|
* |
36
|
|
|
* @license see LICENSE file in root directory |
37
|
|
|
* |
38
|
|
|
* @package Developer |
39
|
|
|
*/ |
40
|
|
|
class RFC6614Tests extends AbstractTest { |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* dictionary of translatable texts around the certificates we check |
44
|
|
|
* |
45
|
|
|
* @var array |
46
|
|
|
*/ |
47
|
|
|
private $TLS_certkeys = []; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* list of IP addresses which are candidates for dynamic discovery targets |
51
|
|
|
* |
52
|
|
|
* @var array |
53
|
|
|
*/ |
54
|
|
|
private $candidateIPs; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* the hostname which should show up in the certificate when establishing |
58
|
|
|
* a connection to the RADIUS/TLS server (hostname is an intermediary result |
59
|
|
|
* of the RFC7585 DNS resolution algorithm, in SRV response) |
60
|
|
|
* |
61
|
|
|
* @var string |
62
|
|
|
*/ |
63
|
|
|
private $expectedName; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* associative array holding the server-side cert test results for a given IP (IP is the key) |
67
|
|
|
* |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
public $TLS_CA_checks_result; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* associative array holding the client-side cert test results for a given IP (IP is the key) |
74
|
|
|
* |
75
|
|
|
* @var array |
76
|
|
|
*/ |
77
|
|
|
public $TLS_clients_checks_result; |
78
|
|
|
|
79
|
|
|
/** |
|
|
|
|
80
|
|
|
* Sets up the instance for testing of a number of candidate IPs |
81
|
|
|
* |
82
|
|
|
* @param array $listOfIPs candidates to test |
83
|
|
|
*/ |
84
|
|
|
public function __construct($listOfIPs, $expectedName) { |
85
|
|
|
parent::__construct(); |
86
|
|
|
\core\common\Entity::intoThePotatoes(); |
87
|
|
|
$this->TLS_certkeys = [ |
88
|
|
|
'eduPKI' => _('eduPKI'), |
89
|
|
|
'NCU' => _('Nicolaus Copernicus University'), |
90
|
|
|
'ACCREDITED' => _('accredited'), |
91
|
|
|
'NONACCREDITED' => _('non-accredited'), |
92
|
|
|
'CORRECT' => _('correct certificate'), |
93
|
|
|
'WRONGPOLICY' => _('certificate with wrong policy OID'), |
94
|
|
|
'EXPIRED' => _('expired certificate'), |
95
|
|
|
'REVOKED' => _('revoked certificate'), |
96
|
|
|
'PASS' => _('pass'), |
97
|
|
|
'FAIL' => _('fail'), |
98
|
|
|
'non-eduPKI-accredited' => _("eduroam-accredited CA (now only for tests)"), |
99
|
|
|
]; |
100
|
|
|
$this->TLS_CA_checks_result = []; |
101
|
|
|
$this->TLS_clients_checks_result = []; |
102
|
|
|
|
103
|
|
|
$this->candidateIPs = $listOfIPs; |
104
|
|
|
$this->expectedName = $expectedName; |
105
|
|
|
\core\common\Entity::outOfThePotatoes(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* run all checks on all candidates |
110
|
|
|
* |
111
|
|
|
* @return void |
112
|
|
|
*/ |
113
|
|
|
public function allChecks() { |
114
|
|
|
foreach ($this->candidateIPs as $oneIP) { |
115
|
|
|
$this->cApathCheck($oneIP); |
116
|
|
|
$this->tlsClientSideCheck($oneIP); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* This function executes openssl s_clientends command to check if a server accepts a CA |
122
|
|
|
* |
123
|
|
|
* @param string $host IP:port |
124
|
|
|
* @return int returncode |
125
|
|
|
*/ |
126
|
|
|
public function cApathCheck(string $host) { |
127
|
|
|
if (!isset($this->TLS_CA_checks_result[$host])) { |
128
|
|
|
$this->TLS_CA_checks_result[$host] = []; |
129
|
|
|
} |
130
|
|
|
$opensslbabble = $this->execOpensslClient($host, '', $this->TLS_CA_checks_result[$host]); |
131
|
|
|
$overallRetval = $this->opensslCAResult($host, $opensslbabble); |
132
|
|
|
if ($overallRetval == AbstractTest::RETVAL_OK) { |
133
|
|
|
$this->checkServerName($host); |
134
|
|
|
} |
135
|
|
|
return $overallRetval; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
|
|
|
|
139
|
|
|
* checks whether the received servername matches the expected server name |
140
|
|
|
* |
141
|
|
|
* @return bool yes or no |
142
|
|
|
*/ |
143
|
|
|
private function checkServerName($host) { |
144
|
|
|
// it could match CN or sAN:DNS, we don't care which |
145
|
|
|
if (isset($this->TLS_CA_checks_result[$host]['certdata']['subject']['CN'])) { |
146
|
|
|
$testNames = $this->TLS_CA_checks_result[$host]['certdata']['subject']['CN']; |
147
|
|
|
if (!is_array($testNames)) { |
148
|
|
|
$testNames = [$testNames]; |
149
|
|
|
} |
150
|
|
|
if (array_search($this->expectedName, $testNames) !== FALSE) { |
151
|
|
|
return TRUE; |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
if (isset($this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'])) { |
155
|
|
|
$testNames = $this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname']; |
156
|
|
|
if (!is_array($testNames)) { |
157
|
|
|
$testNames = [$testNames]; |
158
|
|
|
} |
159
|
|
|
foreach ($testNames as $oneName) { |
160
|
|
|
if (preg_match("/".$this->expectedName."/", $oneName) === 1) { |
161
|
|
|
return TRUE; |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
$this->TLS_CA_checks_result[$host]['cert_oddity'] = RADIUSTests::CERTPROB_DYN_SERVER_NAME_MISMATCH; |
166
|
|
|
return FALSE; |
167
|
|
|
|
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* This function executes openssl s_client command to check if a server accepts a client certificate |
172
|
|
|
* |
173
|
|
|
* @param string $host IP:port |
174
|
|
|
* @return int returncode |
175
|
|
|
*/ |
176
|
|
|
public function tlsClientSideCheck(string $host) { |
177
|
|
|
$res = RADIUSTests::RETVAL_OK; |
178
|
|
|
if (!is_array(CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts']) || count(CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts']) == 0) { |
179
|
|
|
return RADIUSTests::RETVAL_SKIPPED; |
180
|
|
|
} |
181
|
|
|
if (preg_match("/\[/", $host)) { |
182
|
|
|
return RADIUSTests::RETVAL_INVALID; |
183
|
|
|
} |
184
|
|
|
foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts'] as $type => $tlsclient) { |
185
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['from'] = $type; |
186
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['status'] = $tlsclient['status']; |
187
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['message'] = $this->TLS_certkeys[$tlsclient['status']]; |
188
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['issuer'] = $tlsclient['issuerCA']; |
189
|
|
|
foreach ($tlsclient['certificates'] as $k => $cert) { |
190
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['status'] = $cert['status']; |
191
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['message'] = $this->TLS_certkeys[$cert['status']]; |
192
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['expected'] = $cert['expected']; |
193
|
|
|
$add = ' -cert ' . ROOT . '/config/cli-certs/' . $cert['public'] . ' -key ' . ROOT . '/config/cli-certs/' . $cert['private']; |
194
|
|
|
if (!isset($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k])) { |
195
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k] = []; |
196
|
|
|
} |
197
|
|
|
$opensslbabble = $this->execOpensslClient($host, $add, $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]); |
198
|
|
|
$res = $this->opensslClientsResult($host, $opensslbabble, $this->TLS_clients_checks_result, $type, $k); |
199
|
|
|
if ($cert['expected'] == 'PASS') { |
200
|
|
|
if (!$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['connected']) { |
201
|
|
|
if (($tlsclient['status'] == 'ACCREDITED') && ($cert['status'] == 'CORRECT')) { |
202
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['returncode'] = RADIUSTests::CERTPROB_NOT_ACCEPTED; |
203
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['finalerror'] = 1; |
204
|
|
|
break; |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
} else { |
208
|
|
|
if ($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['connected']) { |
209
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['returncode'] = RADIUSTests::CERTPROB_WRONGLY_ACCEPTED; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
if (($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['reason'] == RADIUSTests::CERTPROB_UNKNOWN_CA) && ($tlsclient['status'] == 'ACCREDITED') && ($cert['status'] == 'CORRECT')) { |
213
|
|
|
$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['finalerror'] = 1; |
214
|
|
|
echo "koniec zabawy2<br>"; |
215
|
|
|
break; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
return $res; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* This function executes openssl s_client command |
225
|
|
|
* |
226
|
|
|
* @param string $host IP address |
227
|
|
|
* @param string $arg arguments to add to the openssl command |
228
|
|
|
* @param array $testresults by-reference: the testresults array we are writing into |
229
|
|
|
* @return array result of openssl s_client ... |
230
|
|
|
*/ |
231
|
|
|
private function execOpensslClient($host, $arg, &$testresults) { |
232
|
|
|
// we got the IP address either from DNS (guaranteeing well-formedness) |
233
|
|
|
// or from filter_var'ed user input. So it is always safe as an argument |
234
|
|
|
// but code analysers want this more explicit, so here is this extra |
235
|
|
|
// call to escapeshellarg() |
236
|
|
|
$escapedHost = escapeshellarg($host); |
237
|
|
|
$this->loggerInstance->debug(4, CONFIG['PATHS']['openssl'] . " s_client -connect " . $escapedHost . " -tls1 -CApath " . ROOT . "/config/ca-certs/ $arg 2>&1\n"); |
238
|
|
|
$time_start = microtime(true); |
239
|
|
|
$opensslbabble = []; |
240
|
|
|
$result = 999; // likely to become zero by openssl; don't want to initialise to zero, could cover up exec failures |
241
|
|
|
exec(CONFIG['PATHS']['openssl'] . " s_client -connect " . $escapedHost . " -no_ssl2 -no_ssl3 -CApath " . ROOT . "/config/ca-certs/ $arg 2>&1", $opensslbabble, $result); |
242
|
|
|
$time_stop = microtime(true); |
243
|
|
|
$testresults['time_millisec'] = floor(($time_stop - $time_start) * 1000); |
244
|
|
|
$testresults['returncode'] = $result; |
245
|
|
|
return $opensslbabble; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* This function parses openssl s_client result |
250
|
|
|
* |
251
|
|
|
* @param string $host IP:port |
252
|
|
|
* @param array $opensslbabble openssl command output |
253
|
|
|
* @return int return code |
254
|
|
|
*/ |
255
|
|
|
private function opensslCAResult($host, $opensslbabble) { |
256
|
|
|
$res = RADIUSTests::RETVAL_OK; |
257
|
|
|
if (preg_match('/connect: Connection refused/', implode($opensslbabble))) { |
258
|
|
|
$this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_CONNECTION_REFUSED; |
259
|
|
|
$res = RADIUSTests::RETVAL_INVALID; |
260
|
|
|
} |
261
|
|
|
if (preg_match('/verify error:num=19/', implode($opensslbabble))) { |
262
|
|
|
$this->TLS_CA_checks_result[$host]['cert_oddity'] = RADIUSTests::CERTPROB_UNKNOWN_CA; |
263
|
|
|
$this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_INVALID; |
264
|
|
|
$res = RADIUSTests::RETVAL_INVALID; |
265
|
|
|
} |
266
|
|
|
if (preg_match('/verify return:1/', implode($opensslbabble))) { |
267
|
|
|
$this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_OK; |
268
|
|
|
$servercertStage1 = implode("\n", $opensslbabble); |
269
|
|
|
$servercert = preg_replace("/.*(-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----\n).*/s", "$1", $servercertStage1); |
270
|
|
|
$data = openssl_x509_parse($servercert); |
271
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['subject'] = $data['name']; |
272
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['issuer'] = $this->getCertificateIssuer($data); |
273
|
|
|
if (($altname = $this->getCertificatePropertyField($data, 'subjectAltName'))) { |
274
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'] = $altname; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$oids = $this->propertyCheckPolicy($data); |
278
|
|
|
if (!empty($oids)) { |
279
|
|
|
foreach ($oids as $resultArrayKey => $o) { |
280
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['extensions']['policyoid'][] = " $o ($resultArrayKey)"; |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
if (($crl = $this->getCertificatePropertyField($data, 'crlDistributionPoints'))) { |
284
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['extensions']['crlDistributionPoint'] = $crl; |
285
|
|
|
} |
286
|
|
|
if (($ocsp = $this->getCertificatePropertyField($data, 'authorityInfoAccess'))) { |
287
|
|
|
$this->TLS_CA_checks_result[$host]['certdata']['extensions']['authorityInfoAccess'] = $ocsp; |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
return $res; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* This function parses openssl s_client result |
295
|
|
|
* |
296
|
|
|
* @param string $host IP:port |
297
|
|
|
* @param array $opensslbabble openssl command output |
298
|
|
|
* @param array $testresults by-reference: pointer to results array we write into |
299
|
|
|
* @param string $type type of certificate |
300
|
|
|
* @param int $resultArrayKey results array key |
301
|
|
|
* @return int return code |
302
|
|
|
*/ |
303
|
|
|
private function opensslClientsResult($host, $opensslbabble, &$testresults, $type = '', $resultArrayKey = 0) { |
304
|
|
|
\core\common\Entity::intoThePotatoes(); |
305
|
|
|
$res = RADIUSTests::RETVAL_OK; |
306
|
|
|
$ret = $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['returncode']; |
307
|
|
|
$output = implode($opensslbabble); |
308
|
|
|
if ($ret == 0) { |
309
|
|
|
$testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['connected'] = 1; |
310
|
|
|
} else { |
311
|
|
|
$testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['connected'] = 0; |
312
|
|
|
if (preg_match('/connect: Connection refused/', implode($opensslbabble))) { |
313
|
|
|
$testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['returncode'] = RADIUSTests::RETVAL_CONNECTION_REFUSED; |
314
|
|
|
$resComment = _("No TLS connection established: Connection refused"); |
315
|
|
|
} elseif (preg_match('/sslv3 alert certificate expired/', $output)) { |
316
|
|
|
$resComment = _("certificate expired"); |
317
|
|
|
} elseif (preg_match('/sslv3 alert certificate revoked/', $output)) { |
318
|
|
|
$resComment = _("certificate was revoked"); |
319
|
|
|
} elseif (preg_match('/SSL alert number 46/', $output)) { |
320
|
|
|
$resComment = _("bad policy"); |
321
|
|
|
} elseif (preg_match('/tlsv1 alert unknown ca/', $output)) { |
322
|
|
|
$resComment = _("unknown authority"); |
323
|
|
|
$testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['reason'] = RADIUSTests::CERTPROB_UNKNOWN_CA; |
324
|
|
|
} else { |
325
|
|
|
$resComment = _("unknown authority or no certificate policy or another problem"); |
326
|
|
|
} |
327
|
|
|
$testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['resultcomment'] = $resComment; |
328
|
|
|
} |
329
|
|
|
\core\common\Entity::outOfThePotatoes(); |
330
|
|
|
return $res; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* This function parses a X.509 cert and returns all certificatePolicies OIDs |
335
|
|
|
* |
336
|
|
|
* @param array $cert (returned from openssl_x509_parse) |
337
|
|
|
* @return array of OIDs |
338
|
|
|
*/ |
339
|
|
|
private function propertyCheckPolicy($cert) { |
340
|
|
|
$oids = []; |
341
|
|
|
if ($cert['extensions']['certificatePolicies']) { |
342
|
|
|
foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-acceptableOIDs'] as $key => $oid) { |
343
|
|
|
if (preg_match("/Policy: $oid/", $cert['extensions']['certificatePolicies'])) { |
344
|
|
|
$oids[$key] = $oid; |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
} |
348
|
|
|
return $oids; |
349
|
|
|
} |
350
|
|
|
/** |
351
|
|
|
* This function parses a X.509 cert and returns the value of $field |
352
|
|
|
* |
353
|
|
|
* @param array $cert (returned from openssl_x509_parse) |
354
|
|
|
* @return string value of the issuer field or '' |
355
|
|
|
*/ |
356
|
|
|
private function getCertificateIssuer($cert) { |
357
|
|
|
$issuer = ''; |
358
|
|
|
foreach ($cert['issuer'] as $key => $val) { |
359
|
|
|
if (is_array($val)) { |
360
|
|
|
foreach ($val as $v) { |
361
|
|
|
$issuer .= "/$key=$v"; |
362
|
|
|
} |
363
|
|
|
} else { |
364
|
|
|
$issuer .= "/$key=$val"; |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
return $issuer; |
368
|
|
|
} |
369
|
|
|
/** |
370
|
|
|
* This function parses a X.509 cert and returns the value of $field |
371
|
|
|
* |
372
|
|
|
* @param array $cert (returned from openssl_x509_parse) |
373
|
|
|
* @param string $field the field to search for |
374
|
|
|
* @return string value of the extention named $field or '' |
375
|
|
|
*/ |
376
|
|
|
private function getCertificatePropertyField($cert, $field) { |
377
|
|
|
if ($cert['extensions'][$field]) { |
378
|
|
|
return $cert['extensions'][$field]; |
379
|
|
|
} |
380
|
|
|
return ''; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
} |
384
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.