Issues (45)

src/HttpClient/HashUtil.php (3 issues)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (c) Ne-Lexa
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 *
11
 * @see https://github.com/Ne-Lexa/google-play-scraper
12
 */
13
14
namespace Nelexa\GPlay\HttpClient;
15
16
use Psr\Http\Message\RequestInterface;
17
18
final class HashUtil
19
{
20
    /**
21
     * Returns request hash.
22
     *
23
     * @param RequestInterface $request  Request
24
     * @param string           $hashAlgo Hash algo
25
     *
26
     * @return string hash
27
     */
28
    public static function getRequestHash(RequestInterface $request, string $hashAlgo = 'crc32b'): string
29
    {
30
        $ctx = hash_init($hashAlgo);
31
        hash_update($ctx, $request->getMethod());
32
        hash_update($ctx, (string) $request->getUri());
33
34
        foreach ($request->getHeaders() as $name => $header) {
35
            hash_update($ctx, $name . ': ' . implode(', ', $header));
36
        }
37
        hash_update($ctx, $request->getBody()->getContents());
38
39
        return hash_final($ctx);
40
    }
41
42
    /**
43
     * @param callable $func     Callable, function name, class name or object
44
     * @param string   $hashAlgo Hash algorithm
45
     *
46
     * @throws \ReflectionException
47
     *
48
     * @return string
49
     */
50
    public static function hashCallable(callable $func, string $hashAlgo = 'crc32b'): string
51
    {
52
        if (\is_object($func)) {
53
            return self::generateHashByCallableObject($func, $hashAlgo);
0 ignored issues
show
$func of type object is incompatible with the type callable expected by parameter $func of Nelexa\GPlay\HttpClient\...eHashByCallableObject(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
            return self::generateHashByCallableObject(/** @scrutinizer ignore-type */ $func, $hashAlgo);
Loading history...
54
        }
55
56
        if (\is_string($func) && strpos($func, '::') !== false) {
57
            $func = explode('::', $func, 2);
58
        }
59
60
        if (\is_array($func) && \count($func) === 2) {
61
            [$classRef, $method] = $func;
62
            $ref = (new \ReflectionClass($classRef))->getMethod($method);
63
        } elseif (\is_string($func)) {
64
            if (class_exists($func, false)) {
65
                $ref = new \ReflectionClass($func);
66
            } elseif (\function_exists($func)) {
67
                $ref = new \ReflectionFunction($func);
68
            }
69
        }
70
71
        if (!isset($ref)) {
72
            throw new \RuntimeException('Could not calculate hash for passed callable.');
73
        }
74
75
        return self::generateHashByReflection($ref, $hashAlgo);
76
    }
77
78
    /**
79
     * @param callable|object $func
80
     * @param string          $hashAlgo Hash algorithm
81
     *
82
     * @throws \ReflectionException
83
     *
84
     * @return string
85
     */
86
    private static function generateHashByCallableObject(callable $func, string $hashAlgo): string
87
    {
88
        static $hashes;
89
90
        if ($hashes === null) {
91
            $hashes = new \SplObjectStorage();
92
        }
93
94
        if (!isset($hashes[$func])) {
95
            $hashContents = false;
96
97
            if ($func instanceof \Closure) {
98
                $ref = new \ReflectionFunction($func);
99
                $hashContents = true;
100
            } else {
101
                $ref = new \ReflectionClass($func);
0 ignored issues
show
$func of type callable is incompatible with the type object|string expected by parameter $objectOrClass of ReflectionClass::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
                $ref = new \ReflectionClass(/** @scrutinizer ignore-type */ $func);
Loading history...
102
103
                if ($ref->isAnonymous()) {
104
                    $hashContents = true;
105
                }
106
            }
107
108
            $ctx = hash_init($hashAlgo);
109
110
            if ($ref->isUserDefined()) {
111
                if ($hashContents) {
112
                    $file = new \SplFileObject($ref->getFileName());
113
                    $file->seek($ref->getStartLine() - 1);
114
115
                    while ($file->key() < $ref->getEndLine()) {
116
                        hash_update($ctx, $file->current());
0 ignored issues
show
It seems like $file->current() can also be of type array; however, parameter $data of hash_update() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

116
                        hash_update($ctx, /** @scrutinizer ignore-type */ $file->current());
Loading history...
117
                        $file->next();
118
                    }
119
                } else {
120
                    hash_update(
121
                        $ctx,
122
                        $ref->getName() . \PHP_EOL
123
                        . $ref->getFileName() . \PHP_EOL
124
                        . filemtime($ref->getFileName())
125
                    );
126
                }
127
            } else {
128
                hash_update($ctx, $ref->getName());
129
            }
130
            $hashes[$func] = hash_final($ctx);
131
        }
132
133
        return (string) $hashes[$func];
134
    }
135
136
    /**
137
     * @param \ReflectionClass|\ReflectionFunction|\ReflectionMethod $ref
138
     * @param string                                                 $hashAlgo
139
     *
140
     * @return string
141
     */
142
    private static function generateHashByReflection($ref, string $hashAlgo): string
143
    {
144
        static $hashes = [];
145
146
        if (!isset($hashes[$ref->getName()])) {
147
            if ($ref->isUserDefined()) {
148
                $hashes[$ref->getName()] = hash(
149
                    $hashAlgo,
150
                    $ref->getName() . \PHP_EOL
151
                    . $ref->getFileName() . \PHP_EOL
152
                    . filemtime($ref->getFileName())
153
                );
154
            } else {
155
                $hashes[$ref->getName()] = hash($hashAlgo, $ref->getName());
156
            }
157
        }
158
159
        return $hashes[$ref->getName()];
160
    }
161
}
162