LightOpenID::authUrl_v1()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 6
nop 1
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
     * This class provides a simple interface for OpenID (1.1 and 2.0) authentication.
4
     * Supports Yadis discovery.
5
     * The authentication process is stateless/dumb.
6
     *
7
     * Usage:
8
     * Sign-on with OpenID is a two step process:
9
     * Step one is authentication with the provider:
10
     * <code>
11
     * $openid = new LightOpenID('my-host.example.org');
12
     * $openid->identity = 'ID supplied by user';
13
     * header('Location: ' . $openid->authUrl());
14
     * </code>
15
     * The provider then sends various parameters via GET, one of them is openid_mode.
16
     * Step two is verification:
17
     * <code>
18
     * $openid = new LightOpenID('my-host.example.org');
19
     * if ($openid->mode) {
20
     *     echo $openid->validate() ? 'Logged in.' : 'Failed';
21
     * }
22
     * </code>
23
     *
24
     * Change the 'my-host.example.org' to your domain name. Do NOT use $_SERVER['HTTP_HOST']
25
     * for that, unless you know what you are doing.
26
     *
27
     * Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias).
28
     * The default values for those are:
29
     * $openid->realm     = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
30
     * $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI'];
31
     * If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess.
32
     *
33
     * AX and SREG extensions are supported.
34
     * To use them, specify $openid->required and/or $openid->optional before calling $openid->authUrl().
35
     * These are arrays, with values being AX schema paths (the 'path' part of the URL).
36
     * For example:
37
     *   $openid->required = array('namePerson/friendly', 'contact/email');
38
     *   $openid->optional = array('namePerson/first');
39
     * If the server supports only SREG or OpenID 1.1, these are automaticaly
40
     * mapped to SREG names, so that user doesn't have to know anything about the server.
41
     *
42
     * To get the values, use $openid->getAttributes().
43
     *
44
     *
45
     * The library requires PHP >= 5.1.2 with curl or http/https stream wrappers enabled.
46
     * @author Mewp
47
     * @copyright Copyright (c) 2010, Mewp
48
     * @license http://www.opensource.org/licenses/mit-license.php MIT
49
     */
50
class LightOpenID
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
51
{
52
    public $returnUrl
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
53
            , $required = array()
54
            , $optional = array()
55
            , $verify_peer = null
56
            , $capath = null
57
            , $cainfo = null
58
            , $data;
59
    private $identity, $claimed_id;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
60
    protected $server, $version, $trustRoot, $aliases, $identifier_select = false
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
61
            , $ax = false, $sreg = false, $setup_url = null, $headers = array();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ax. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
62
    static protected $ax_to_sreg = array(
63
        'namePerson/friendly'     => 'nickname',
64
        'contact/email'           => 'email',
65
        'namePerson'              => 'fullname',
66
        'birthDate'               => 'dob',
67
        'person/gender'           => 'gender',
68
        'contact/postalCode/home' => 'postcode',
69
        'contact/country/home'    => 'country',
70
        'pref/language'           => 'language',
71
        'pref/timezone'           => 'timezone',
72
        );
73
74
    function __construct($host)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
75
    {
76
        $this->trustRoot = (strpos($host, '://') ? $host : 'http://' . $host);
77
        if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
78
            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
79
            && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
80
        ) {
81
            $this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host);
82
        }
83
84
        if (($host_end = strpos($this->trustRoot, '/', 8)) !== false) {
85
            $this->trustRoot = substr($this->trustRoot, 0, $host_end);
86
        }
87
88
        $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
89
        $this->returnUrl = $this->trustRoot . $uri;
90
91
        $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
92
93
        if (!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
94
            throw new ErrorException('You must have either https wrappers or curl enabled.');
95
        }
96
    }
97
98
    function __set($name, $value)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
99
    {
100
        switch ($name) {
101
            case 'identity':
102
                if (strlen($value = trim((String) $value))) {
103
                    if (preg_match('#^xri:/*#i', $value, $m)) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
104
                        $value = substr($value, strlen($m[0]));
105
                    } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
106
                        $value = "http://$value";
107
                    }
108
                    if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
109
                        $value .= '/';
110
                    }
111
                }
112
                $this->$name = $this->claimed_id = $value;
113
                break;
114
            case 'trustRoot':
115
            case 'realm':
116
                $this->trustRoot = trim($value);
117
        }
118
    }
119
120
    function __get($name)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
121
    {
122
        switch ($name) {
123
            case 'identity':
124
                # We return claimed_id instead of identity,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
125
                # because the developer should see the claimed identifier,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
126
                # i.e. what he set as identity, not the op-local identifier (which is what we verify)
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
127
                return $this->claimed_id;
128
            case 'trustRoot':
129
            case 'realm':
130
                return $this->trustRoot;
131
            case 'mode':
132
                return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
133
        }
134
    }
135
136
    /**
137
     * Checks if the server specified in the url exists.
138
     *
139
     * @param $url url to check
140
     * @return true, if the server exists; false otherwise
0 ignored issues
show
Documentation introduced by
The doc-type true, could not be parsed: Expected "|" or "end of type", but got "," at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
141
     */
142
    function hostExists($url)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
143
    {
144
        if (strpos($url, '/') === false) {
145
            $server = $url;
146
        } else {
147
            $server = @parse_url($url, PHP_URL_HOST);
148
        }
149
150
        if (!$server) {
151
            return false;
152
        }
153
154
        return !!gethostbynamel($server);
155
    }
156
157
    protected function request_curl($url, $method = 'GET', $params = array(), $update_claimed_id)
158
    {
159
        $params = http_build_query($params, '', '&');
160
        $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
161
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
162
        curl_setopt($curl, CURLOPT_HEADER, false);
163
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
164
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
165
        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
166
167
        if ($this->verify_peer !== null) {
168
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
169
            if ($this->capath) {
170
                curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
171
            }
172
173
            if ($this->cainfo) {
174
                curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
175
            }
176
        }
177
178
        if ($method == 'POST') {
179
            curl_setopt($curl, CURLOPT_POST, true);
180
            curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
181
        } elseif ($method == 'HEAD') {
182
            curl_setopt($curl, CURLOPT_HEADER, true);
183
            curl_setopt($curl, CURLOPT_NOBODY, true);
184
        } else {
185
            curl_setopt($curl, CURLOPT_HEADER, true);
186
            curl_setopt($curl, CURLOPT_HTTPGET, true);
187
        }
188
        $response = curl_exec($curl);
189
190
        if ($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) {
191
            curl_setopt($curl, CURLOPT_HTTPGET, true);
192
            $response = curl_exec($curl);
193
            $response = substr($response, 0, strpos($response, "\r\n\r\n"));
194
        }
195
196
        if ($method == 'HEAD' || $method == 'GET') {
197
            $header_response = $response;
198
199
            # If it's a GET request, we want to only parse the header part.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
200
            if ($method == 'GET') {
201
                $header_response = substr($response, 0, strpos($response, "\r\n\r\n"));
202
            }
203
204
            $headers = array();
205
            foreach (explode("\n", $header_response) as $header) {
206
                $pos = strpos($header, ':');
207
                if ($pos !== false) {
208
                    $name = strtolower(trim(substr($header, 0, $pos)));
209
                    $headers[$name] = trim(substr($header, $pos + 1));
210
                }
211
            }
212
213
            if ($update_claimed_id) {
214
                # Updating claimed_id in case of redirections.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
215
                $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
216
                if ($effective_url != $url) {
217
                    $this->identity = $this->claimed_id = $effective_url;
218
                }
219
            }
220
221
            if ($method == 'HEAD') {
222
                return $headers;
223
            } else {
224
                $this->headers = $headers;
225
            }
226
        }
227
228
        if (curl_errno($curl)) {
229
            throw new ErrorException(curl_error($curl), curl_errno($curl));
230
        }
231
232
        return $response;
233
    }
234
235
    protected function parse_header_array($array, $update_claimed_id)
236
    {
237
        $headers = array();
238
        foreach ($array as $header) {
239
            $pos = strpos($header, ':');
240
            if ($pos !== false) {
241
                $name = strtolower(trim(substr($header, 0, $pos)));
242
                $headers[$name] = trim(substr($header, $pos + 1));
243
244
                # Following possible redirections. The point is just to have
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
245
                # claimed_id change with them, because the redirections
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
246
                # are followed automatically.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
247
                # We ignore redirections with relative paths.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
248
                # If any known provider uses them, file a bug report.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
249
                if ($name == 'location' && $update_claimed_id) {
250
                    if (strpos($headers[$name], 'http') === 0) {
251
                        $this->identity = $this->claimed_id = $headers[$name];
252
                    } elseif ($headers[$name][0] == '/') {
253
                        $parsed_url = parse_url($this->claimed_id);
254
                        $this->identity =
255
                        $this->claimed_id = $parsed_url['scheme'] . '://'
256
                                            . $parsed_url['host']
257
                                            . $headers[$name];
258
                    }
259
                }
260
            }
261
        }
262
        return $headers;
263
    }
264
265
    protected function request_streams($url, $method = 'GET', $params = array(), $update_claimed_id)
266
    {
267
        if (!$this->hostExists($url)) {
268
            throw new ErrorException("Could not connect to $url.", 404);
269
        }
270
271
        $params = http_build_query($params, '', '&');
272
        switch ($method) {
273
            case 'GET':
274
                $opts = array(
275
                    'http' => array(
276
                        'method' => 'GET',
277
                        'header' => 'Accept: application/xrds+xml, */*',
278
                        'ignore_errors' => true,
279
                    ), 'ssl' => array(
280
                        'CN_match' => parse_url($url, PHP_URL_HOST),
281
                    ),
282
                );
283
                $url = $url . ($params ? '?' . $params : '');
284
                break;
285
            case 'POST':
286
                $opts = array(
287
                    'http' => array(
288
                        'method' => 'POST',
289
                        'header'  => 'Content-type: application/x-www-form-urlencoded',
290
                        'content' => $params,
291
                        'ignore_errors' => true,
292
                    ), 'ssl' => array(
293
                        'CN_match' => parse_url($url, PHP_URL_HOST),
294
                    ),
295
                );
296
                break;
297
            case 'HEAD':
298
                # We want to send a HEAD request,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
299
                # but since get_headers doesn't accept $context parameter,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
300
                # we have to change the defaults.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
301
                $default = stream_context_get_options(stream_context_get_default());
302
                stream_context_get_default(
303
                    array(
304
                        'http' => array(
305
                            'method' => 'HEAD',
306
                            'header' => 'Accept: application/xrds+xml, */*',
307
                            'ignore_errors' => true,
308
                        ), 'ssl' => array(
309
                            'CN_match' => parse_url($url, PHP_URL_HOST),
310
                        ),
311
                    )
312
                );
313
314
                $url = $url . ($params ? '?' . $params : '');
315
                $headers = get_headers($url);
316
                if (!$headers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $headers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
317
                    return array();
318
                }
319
320
                if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) {
321
                    # The server doesn't support HEAD, so let's emulate it with
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
322
                    # a GET.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
323
                    $args = func_get_args();
324
                    $args[1] = 'GET';
325
                    call_user_func_array(array($this, 'request_streams'), $args);
326
                    return $this->headers;
327
                }
328
329
                $headers = $this->parse_header_array($headers, $update_claimed_id);
330
331
                # And restore them.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
332
                stream_context_get_default($default);
333
                return $headers;
334
        }
335
336
        if ($this->verify_peer) {
337
            $opts['ssl'] += array(
0 ignored issues
show
Bug introduced by
The variable $opts does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
338
                'verify_peer' => true,
339
                'capath'      => $this->capath,
340
                'cafile'      => $this->cainfo,
341
            );
342
        }
343
344
        $context = stream_context_create($opts);
345
        $data = file_get_contents($url, false, $context);
346
        # This is a hack for providers who don't support HEAD requests.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
347
        # It just creates the headers array for the last request in $this->headers.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
348
        if (isset($http_response_header)) {
349
            $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id);
350
        }
351
352
        return $data;
353
    }
354
355
    protected function request($url, $method = 'GET', $params = array(), $update_claimed_id = false)
356
    {
357
        if (function_exists('curl_init')
358
            && (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir'))
359
        ) {
360
            return $this->request_curl($url, $method, $params, $update_claimed_id);
361
        }
362
        return $this->request_streams($url, $method, $params, $update_claimed_id);
363
    }
364
365
    protected function build_url($url, $parts)
366
    {
367
        if (isset($url['query'], $parts['query'])) {
368
            $parts['query'] = $url['query'] . '&' . $parts['query'];
369
        }
370
371
        $url = $parts + $url;
372
        $url = $url['scheme'] . '://'
373
                . (empty($url['username']) ? ''
374
                 :(empty($url['password']) ? "{$url['username']}@"
375
                 :"{$url['username']}:{$url['password']}@"))
376
                . $url['host']
377
                . (empty($url['port']) ? '' : ":{$url['port']}")
378
                . (empty($url['path']) ? '' : $url['path'])
379
                . (empty($url['query']) ? '' : "?{$url['query']}")
380
                . (empty($url['fragment']) ? '' : "#{$url['fragment']}");
381
        return $url;
382
    }
383
384
    /**
385
     * Helper function used to scan for <meta>/<link> tags and extract information
386
     * from them
387
     */
388
    protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
389
    {
390
        preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
391
        preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
392
393
        $result = array_merge($matches1[1], $matches2[1]);
394
        return empty($result) ? false : $result[0];
395
    }
396
397
    /**
398
     * Performs Yadis and HTML discovery. Normally not used.
399
     * @param $url Identity URL.
400
     * @return String OP Endpoint (i.e. OpenID provider address).
401
     * @throws ErrorException
402
     */
403
    function discover($url)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
404
    {
405
        if (!$url) {
406
            throw new ErrorException('No identity supplied.');
407
        }
408
        # Use xri.net proxy to resolve i-name identities
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
409
        if (!preg_match('#^https?:#', $url)) {
410
            $url = "https://xri.net/$url";
411
        }
412
413
        # We save the original url in case of Yadis discovery failure.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
414
        # It can happen when we'll be lead to an XRDS document
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
415
        # which does not have any OpenID2 services.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
416
        $originalUrl = $url;
417
418
        # A flag to disable yadis discovery in case of failure in headers.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
419
        $yadis = true;
420
421
        # We'll jump a maximum of 5 times, to avoid endless redirections.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
422
        for ($i = 0; $i < 5; $i++) {
423
            if ($yadis) {
424
                $headers = $this->request($url, 'HEAD', array(), true);
425
426
                $next = false;
427
                if (isset($headers['x-xrds-location'])) {
428
                    $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
429
                    $next = true;
430
                }
431
432
                if (isset($headers['content-type'])
433
                    && (strpos($headers['content-type'], 'application/xrds+xml') !== false
434
                        || strpos($headers['content-type'], 'text/xml') !== false)
435
                ) {
436
                    # Apparently, some providers return XRDS documents as text/html.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
437
                    # While it is against the spec, allowing this here shouldn't break
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
438
                    # compatibility with anything.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
439
                    # ---
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
440
                    # Found an XRDS document, now let's find the server, and optionally delegate.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
441
                    $content = $this->request($url, 'GET');
442
443
                    preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
444
                    foreach ($m[1] as $content) {
445
                        $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
446
447
                        # OpenID 2
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
448
                        $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#');
449
                        if (preg_match('#<Type>\s*' . $ns . '(server|signon)\s*</Type>#s', $content, $type)) {
450
                            if ($type[1] == 'server') {
451
                                $this->identifier_select = true;
452
                            }
453
454
                            preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
455
                            preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate);
456
                            if (empty($server)) {
457
                                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by LightOpenID::discover of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
458
                            }
459
                            # Does the server advertise support for either AX or SREG?
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
460
                            $this->ax   = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
461
                            $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
462
                                       || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
463
464
                            $server = $server[1];
465
                            if (isset($delegate[2])) {
466
                                $this->identity = trim($delegate[2]);
467
                            }
468
                            $this->version = 2;
469
470
                            $this->server = $server;
471
                            return $server;
472
                        }
473
474
                        # OpenID 1.1
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
475
                        $ns = preg_quote('http://openid.net/signon/1.1', '#');
476
                        if (preg_match('#<Type>\s*' . $ns . '\s*</Type>#s', $content)) {
477
478
                            preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
479
                            preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
480
                            if (empty($server)) {
481
                                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by LightOpenID::discover of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
482
                            }
483
                            # AX can be used only with OpenID 2.0, so checking only SREG
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
484
                            $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
485
                                       || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
486
487
                            $server = $server[1];
488
                            if (isset($delegate[1])) {
489
                                $this->identity = $delegate[1];
490
                            }
491
                            $this->version = 1;
492
493
                            $this->server = $server;
494
                            return $server;
495
                        }
496
                    }
497
498
                    $next = true;
0 ignored issues
show
Unused Code introduced by
$next is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
499
                    $yadis = false;
0 ignored issues
show
Unused Code introduced by
$yadis is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
500
                    $url = $originalUrl;
0 ignored issues
show
Unused Code introduced by
$url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
501
                    $content = null;
0 ignored issues
show
Unused Code introduced by
$content is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
502
                    break;
503
                }
504
                if ($next) {
505
                    continue;
506
                }
507
508
                # There are no relevant information in headers, so we search the body.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
509
                $content = $this->request($url, 'GET', array(), true);
510
511
                if (isset($this->headers['x-xrds-location'])) {
512
                    $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location'])));
513
                    continue;
514
                }
515
516
                $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
517
                if ($location) {
518
                    $url = $this->build_url(parse_url($url), parse_url($location));
519
                    continue;
520
                }
521
            }
522
523
            if (!$content) {
0 ignored issues
show
Bug introduced by
The variable $content does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
524
                $content = $this->request($url, 'GET');
525
            }
526
527
            # At this point, the YADIS Discovery has failed, so we'll switch
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
528
            # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
529
            $server   = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
530
            $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
531
            $this->version = 2;
532
533
            if (!$server) {
534
                # The same with openid 1.1
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
535
                $server   = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
536
                $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
537
                $this->version = 1;
538
            }
539
540
            if ($server) {
541
                # We found an OpenID2 OP Endpoint
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
542
                if ($delegate) {
543
                    # We have also found an OP-Local ID.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
544
                    $this->identity = $delegate;
545
                }
546
                $this->server = $server;
547
                return $server;
548
            }
549
550
            throw new ErrorException("No OpenID Server found at $url", 404);
551
        }
552
        throw new ErrorException('Endless redirection!', 500);
553
    }
554
555
    protected function sregParams()
556
    {
557
        $params = array();
558
        # We always use SREG 1.1, even if the server is advertising only support for 1.0.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
559
        # That's because it's fully backwards compatibile with 1.0, and some providers
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
560
        # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
561
        $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
562 View Code Duplication
        if ($this->required) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->required of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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...
563
            $params['openid.sreg.required'] = array();
564
            foreach ($this->required as $required) {
565
                if (!isset(self::$ax_to_sreg[$required])) {
566
                    continue;
567
                }
568
                $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
569
            }
570
            $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
571
        }
572
573 View Code Duplication
        if ($this->optional) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->optional of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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...
574
            $params['openid.sreg.optional'] = array();
575
            foreach ($this->optional as $optional) {
576
                if (!isset(self::$ax_to_sreg[$optional])) {
577
                    continue;
578
                }
579
                $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
580
            }
581
            $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
582
        }
583
        return $params;
584
    }
585
586
    protected function axParams()
587
    {
588
        $params = array();
589
        if ($this->required || $this->optional) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->required of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->optional of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
590
            $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
591
            $params['openid.ax.mode'] = 'fetch_request';
592
            $this->aliases = array();
593
            $counts   = array();
594
            $required = array();
595
            $optional = array();
596
            foreach (array('required', 'optional') as $type) {
597
                foreach ($this->$type as $alias => $field) {
598
                    if (is_int($alias)) {
599
                        $alias = strtr($field, '/', '_');
600
                    }
601
                    $this->aliases[$alias] = 'http://axschema.org/' . $field;
602
                    if (empty($counts[$alias])) {
603
                        $counts[$alias] = 0;
604
                    }
605
                    $counts[$alias] += 1;
606
                    ${$type}[] = $alias;
607
                }
608
            }
609
            foreach ($this->aliases as $alias => $ns) {
610
                $params['openid.ax.type.' . $alias] = $ns;
611
            }
612
            foreach ($counts as $alias => $count) {
613
                if ($count == 1) {
614
                    continue;
615
                }
616
                $params['openid.ax.count.' . $alias] = $count;
617
            }
618
619
            # Don't send empty ax.requied and ax.if_available.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
620
            # Google and possibly other providers refuse to support ax when one of these is empty.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
621
            if ($required) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $required of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
622
                $params['openid.ax.required'] = implode(',', $required);
623
            }
624
            if ($optional) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $optional of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
625
                $params['openid.ax.if_available'] = implode(',', $optional);
626
            }
627
        }
628
        return $params;
629
    }
630
631
    protected function authUrl_v1($immediate)
632
    {
633
        $returnUrl = $this->returnUrl;
634
        # If we have an openid.delegate that is different from our claimed id,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
635
        # we need to somehow preserve the claimed id between requests.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
636
        # The simplest way is to just send it along with the return_to url.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
637
        if ($this->identity != $this->claimed_id) {
638
            $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
639
        }
640
641
        $params = array(
642
            'openid.return_to'  => $returnUrl,
643
            'openid.mode'       => $immediate ? 'checkid_immediate' : 'checkid_setup',
644
            'openid.identity'   => $this->identity,
645
            'openid.trust_root' => $this->trustRoot,
646
            ) + $this->sregParams();
647
648
        return $this->build_url(parse_url($this->server)
649
                                , array('query' => http_build_query($params, '', '&')));
650
    }
651
652
    protected function authUrl_v2($immediate)
653
    {
654
        $params = array(
655
            'openid.ns'          => 'http://specs.openid.net/auth/2.0',
656
            'openid.mode'        => $immediate ? 'checkid_immediate' : 'checkid_setup',
657
            'openid.return_to'   => $this->returnUrl,
658
            'openid.realm'       => $this->trustRoot,
659
        );
660
        if ($this->ax) {
661
            $params += $this->axParams();
662
        }
663
        if ($this->sreg) {
664
            $params += $this->sregParams();
665
        }
666
        if (!$this->ax && !$this->sreg) {
667
            # If OP doesn't advertise either SREG, nor AX, let's send them both
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
668
            # in worst case we don't get anything in return.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
669
            $params += $this->axParams() + $this->sregParams();
670
        }
671
672
        if ($this->identifier_select) {
673
            $params['openid.identity'] = $params['openid.claimed_id']
674
                    = 'http://specs.openid.net/auth/2.0/identifier_select';
675
        } else {
676
            $params['openid.identity'] = $this->identity;
677
            $params['openid.claimed_id'] = $this->claimed_id;
678
        }
679
680
        return $this->build_url(parse_url($this->server)
681
                                , array('query' => http_build_query($params, '', '&')));
682
    }
683
684
    /**
685
     * Returns authentication url. Usually, you want to redirect your user to it.
686
     * @return String The authentication url.
687
     * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
0 ignored issues
show
Bug introduced by
There is no parameter named $select_identifier. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
688
     * @throws ErrorException
689
     */
690
    function authUrl($immediate = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
691
    {
692
        if ($this->setup_url && !$immediate) {
693
            return $this->setup_url;
694
        }
695
        if (!$this->server) {
696
            $this->discover($this->identity);
697
        }
698
699
        if ($this->version == 2) {
700
            return $this->authUrl_v2($immediate);
701
        }
702
        return $this->authUrl_v1($immediate);
703
    }
704
705
    /**
706
     * Performs OpenID verification with the OP.
707
     * @return Bool Whether the verification was successful.
708
     * @throws ErrorException
709
     */
710
    function validate()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
711
    {
712
        # If the request was using immediate mode, a failure may be reported
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
713
        # by presenting user_setup_url (for 1.1) or reporting
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
714
        # mode 'setup_needed' (for 2.0). Also catching all modes other than
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
715
        # id_res, in order to avoid throwing errors.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
716
        if (isset($this->data['openid_user_setup_url'])) {
717
            $this->setup_url = $this->data['openid_user_setup_url'];
718
            return false;
719
        }
720
        if ($this->mode != 'id_res') {
0 ignored issues
show
Documentation introduced by
The property mode does not exist on object<LightOpenID>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
721
            return false;
722
        }
723
724
        $this->claimed_id = isset($this->data['openid_claimed_id']) ? $this->data['openid_claimed_id'] : $this->data['openid_identity'];
725
        $params = array(
726
            'openid.assoc_handle' => $this->data['openid_assoc_handle'],
727
            'openid.signed'       => $this->data['openid_signed'],
728
            'openid.sig'          => $this->data['openid_sig'],
729
            );
730
731
        if (isset($this->data['openid_ns'])) {
732
            # We're dealing with an OpenID 2.0 server, so let's set an ns
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
733
            # Even though we should know location of the endpoint,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
734
            # we still need to verify it by discovery, so $server is not set here
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
735
            $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
736
        } elseif (isset($this->data['openid_claimed_id'])
737
            && $this->data['openid_claimed_id'] != $this->data['openid_identity']
738
        ) {
739
            # If it's an OpenID 1 provider, and we've got claimed_id,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
740
            # we have to append it to the returnUrl, like authUrl_v1 does.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
741
            $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
742
                                .  'openid.claimed_id=' . $this->claimed_id;
743
        }
744
745
        if ($this->data['openid_return_to'] != $this->returnUrl) {
746
            # The return_to url must match the url of current request.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
747
            # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
748
            return false;
749
        }
750
751
        $server = $this->discover($this->claimed_id);
752
753
        foreach (explode(',', $this->data['openid_signed']) as $item) {
754
            # Checking whether magic_quotes_gpc is turned on, because
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
755
            # the function may fail if it is. For example, when fetching
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
756
            # AX namePerson, it might containg an apostrophe, which will be escaped.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
757
            # In such case, validation would fail, since we'd send different data than OP
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
758
            # wants to verify. stripslashes() should solve that problem, but we can't
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
759
            # use it when magic_quotes is off.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
760
            $value = $this->data['openid_' . str_replace('.', '_', $item)];
761
            $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value;
762
763
        }
764
765
        $params['openid.mode'] = 'check_authentication';
766
767
        $response = $this->request($server, 'POST', $params);
768
769
        return preg_match('/is_valid\s*:\s*true/i', $response);
770
    }
771
772
    protected function getAxAttributes()
773
    {
774
        $alias = null;
775
        if (isset($this->data['openid_ns_ax'])
776
            && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0'
777
        ) { # It's the most likely case, so we'll check it before
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
778
            $alias = 'ax';
779
        } else {
780
            # 'ax' prefix is either undefined, or points to another extension,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
781
            # so we search for another prefix
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
782
            foreach ($this->data as $key => $val) {
783
                if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_'
784
                    && $val == 'http://openid.net/srv/ax/1.0'
785
                ) {
786
                    $alias = substr($key, strlen('openid_ns_'));
787
                    break;
788
                }
789
            }
790
        }
791
        if (!$alias) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alias of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
792
            # An alias for AX schema has not been found,
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
793
            # so there is no AX data in the OP's response
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
794
            return array();
795
        }
796
797
        $attributes = array();
798
        foreach (explode(',', $this->data['openid_signed']) as $key) {
799
            $keyMatch = $alias . '.value.';
800
            if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
801
                continue;
802
            }
803
            $key = substr($key, strlen($keyMatch));
804
            if (!isset($this->data['openid_' . $alias . '_type_' . $key])) {
805
                # OP is breaking the spec by returning a field without
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
806
                # associated ns. This shouldn't happen, but it's better
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
807
                # to check, than cause an E_NOTICE.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
808
                continue;
809
            }
810
            $value = $this->data['openid_' . $alias . '_value_' . $key];
811
            $key = substr($this->data['openid_' . $alias . '_type_' . $key],
812
                            strlen('http://axschema.org/'));
813
814
            $attributes[$key] = $value;
815
        }
816
        return $attributes;
817
    }
818
819
    protected function getSregAttributes()
820
    {
821
        $attributes = array();
822
        $sreg_to_ax = array_flip(self::$ax_to_sreg);
823
        foreach (explode(',', $this->data['openid_signed']) as $key) {
824
            $keyMatch = 'sreg.';
825
            if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
826
                continue;
827
            }
828
            $key = substr($key, strlen($keyMatch));
829
            if (!isset($sreg_to_ax[$key])) {
830
                # The field name isn't part of the SREG spec, so we ignore it.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
831
                continue;
832
            }
833
            $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
834
        }
835
        return $attributes;
836
    }
837
838
    /**
839
     * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
840
     * Note that it does not guarantee that any of the required/optional parameters will be present,
841
     * or that there will be no other attributes besides those specified.
842
     * In other words. OP may provide whatever information it wants to.
843
     *     * SREG names will be mapped to AX names.
844
     *     * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
845
     * @see http://www.axschema.org/types/
846
     */
847
    function getAttributes()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
848
    {
849
        if (isset($this->data['openid_ns'])
850
            && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
851
        ) { # OpenID 2.0
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
852
            # We search for both AX and SREG attributes, with AX taking precedence.
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
853
            return $this->getAxAttributes() + $this->getSregAttributes();
854
        }
855
        return $this->getSregAttributes();
856
    }
857
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
858