Completed
Push — master ( ba30b6...904d28 )
by Christian
13:26
created

UriSigner::buildUrl()   C

Complexity

Conditions 12
Paths 2048

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 17
rs 5.3132
c 1
b 0
f 0
cc 12
eloc 13
nc 2048
nop 2

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of tenside/core.
5
 *
6
 * (c) Christian Schiffler <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * This project is provided in good faith and hope to be usable by anyone.
12
 *
13
 * @package    tenside/core
14
 * @author     Christian Schiffler <[email protected]>
15
 * @copyright  2015 Christian Schiffler <[email protected]>
16
 * @license    https://github.com/tenside/core/blob/master/LICENSE MIT
17
 * @link       https://github.com/tenside/core
18
 * @filesource
19
 */
20
21
namespace Tenside\CoreBundle;
22
23
/**
24
 * Signs URIs.
25
 *
26
 * This service behaves exactly like the one in the symfony framework but is ready for phar use.
27
 * As the kernel.secret is not available during phar compile time, we need it lazy loaded.
28
 *
29
 * @author Fabien Potencier <[email protected]>
30
 */
31
class UriSigner
32
{
33
    /**
34
     * The resolved secret.
35
     *
36
     * @var string
37
     */
38
    private $secret;
39
40
    /**
41
     * The name of the secret parameter in the kernel.
42
     *
43
     * @var TensideJsonConfig
44
     */
45
    private $tensideConfig;
46
47
    /**
48
     * Constructor.
49
     *
50
     * @param TensideJsonConfig $tensideConfig The tenside config.
51
     */
52
    public function __construct(TensideJsonConfig $tensideConfig)
53
    {
54
        $this->tensideConfig = $tensideConfig;
55
    }
56
57
    /**
58
     * Signs a URI.
59
     *
60
     * The given URI is signed by adding a _hash query string parameter
61
     * which value depends on the URI and the secret.
62
     *
63
     * @param string $uri A URI to sign.
64
     *
65
     * @return string The signed URI
66
     */
67
    public function sign($uri)
68
    {
69
        $url = parse_url($uri);
70
        if (isset($url['query'])) {
71
            parse_str($url['query'], $params);
72
        } else {
73
            $params = array();
74
        }
75
76
        $uri = $this->buildUrl($url, $params);
0 ignored issues
show
Security Bug introduced by
It seems like $url defined by parse_url($uri) on line 69 can also be of type false; however, Tenside\CoreBundle\UriSigner::buildUrl() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Bug introduced by
It seems like $params can also be of type null; however, Tenside\CoreBundle\UriSigner::buildUrl() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
77
78
        return $uri.(false === (strpos($uri, '?')) ? '?' : '&').'_hash='.$this->computeHash($uri);
79
    }
80
81
    /**
82
     * Checks that a URI contains the correct hash.
83
     *
84
     * The _hash query string parameter must be the last one
85
     * (as it is generated that way by the sign() method, it should
86
     * never be a problem).
87
     *
88
     * @param string $uri A signed URI.
89
     *
90
     * @return bool True if the URI is signed correctly, false otherwise
91
     */
92
    public function check($uri)
1 ignored issue
show
Coding Style introduced by
function check() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
93
    {
94
        $url = parse_url($uri);
95
        if (isset($url['query'])) {
96
            parse_str($url['query'], $params);
97
        } else {
98
            $params = array();
99
        }
100
101
        if (empty($params['_hash'])) {
102
            return false;
103
        }
104
105
        $hash = urlencode($params['_hash']);
106
        unset($params['_hash']);
107
108
        return $this->computeHash($this->buildUrl($url, $params)) === $hash;
0 ignored issues
show
Security Bug introduced by
It seems like $url defined by parse_url($uri) on line 94 can also be of type false; however, Tenside\CoreBundle\UriSigner::buildUrl() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
109
    }
110
111
    /**
112
     * Calculate the hash.
113
     *
114
     * @param string $uri The uri to calculate the hash for.
115
     *
116
     * @return string
117
     */
118
    private function computeHash($uri)
119
    {
120
        if (!isset($this->secret)) {
121
            $this->secret = $this->tensideConfig->getSecret();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->tensideConfig->getSecret() can also be of type array or integer. However, the property $secret is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
122
        }
123
124
        return urlencode(base64_encode(hash_hmac('sha256', $uri, $this->secret, true)));
125
    }
126
127
    /**
128
     * Build the url.
129
     *
130
     * @param array $url    The url values.
131
     *
132
     * @param array $params The url parameters.
133
     *
134
     * @return string
135
     *
136
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
137
     * @SuppressWarnings(PHPMD.NPathComplexity)
138
     */
139
    private function buildUrl(array $url, array $params = array())
140
    {
141
        ksort($params);
142
        $url['query'] = http_build_query($params, '', '&');
143
144
        $scheme   = isset($url['scheme']) ? $url['scheme'].'://' : '';
145
        $host     = isset($url['host']) ? $url['host'] : '';
146
        $port     = isset($url['port']) ? ':'.$url['port'] : '';
147
        $user     = isset($url['user']) ? $url['user'] : '';
148
        $pass     = isset($url['pass']) ? ':'.$url['pass']  : '';
149
        $pass     = ($user || $pass) ? $pass . '@' : '';
150
        $path     = isset($url['path']) ? $url['path'] : '';
151
        $query    = isset($url['query']) && $url['query'] ? '?'.$url['query'] : '';
152
        $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : '';
153
154
        return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
155
    }
156
}
157