Completed
Push — master ( ab2a6d...da3f44 )
by Tony Karavasilev (Тони
08:35
created

AbstractUnkeyedHashFunction::hashFile()   B

Complexity

Conditions 10
Paths 26

Size

Total Lines 50
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
c 1
b 0
f 0
dl 0
loc 50
ccs 23
cts 23
cp 1
rs 7.6666
cc 10
nc 26
nop 1
crap 10

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
 * Abstraction for unkeyed hash objects like checksums and plain cryptographic hash functions.
5
 */
6
7
namespace CryptoManana\Core\Abstractions\MessageDigestion;
8
9
use \CryptoManana\Core\Abstractions\MessageDigestion\AbstractHashAlgorithm as HashAlgorithm;
10
use \CryptoManana\Core\Interfaces\MessageDigestion\ObjectHashingInterface as ObjectHashing;
11
use \CryptoManana\Core\Interfaces\MessageDigestion\FileHashingInterface as FileHashing;
12
use \CryptoManana\Core\Traits\MessageDigestion\ObjectHashingTrait as HashObjects;
13
use \CryptoManana\Core\StringBuilder as StringBuilder;
14
15
/**
16
 * Class AbstractUnkeyedHashFunction - Abstraction for unkeyed hash classes.
17
 *
18
 * @package CryptoManana\Core\Abstractions\MessageDigestion
19
 *
20
 * @mixin HashObjects
21
 */
22
abstract class AbstractUnkeyedHashFunction extends HashAlgorithm implements ObjectHashing, FileHashing
23
{
24
    /**
25
     * Object hashing capabilities.
26
     *
27
     * {@internal Reusable implementation of `ObjectHashingInterface`. }}
28
     */
29
    use HashObjects;
30
31
    /**
32
     * The internal name of the algorithm.
33
     */
34
    const ALGORITHM_NAME = 'none';
35
36
    /**
37
     * Flag to force native code polyfill realizations, if available.
38
     *
39
     * @var bool Flag to force native realizations.
40
     */
41
    protected $useNative = false;
42
43
    /**
44
     * Internal method for location and filename validation.
45
     *
46
     * @param string $filename The filename and location.
47
     *
48
     * @throws \Exception Validation errors.
49
     */
50 44
    protected function validateFileNamePath($filename)
51
    {
52 44
        $filename = StringBuilder::stringReplace("\0", '', $filename); // (ASCII 0 (0x00))
53 44
        $filename = realpath($filename); // Path traversal escape and absolute path fetching
54
55
        // Clear path cache
56 44
        if (!empty($filename)) {
57 22
            clearstatcache(true, $filename);
58
        }
59
60
        // Check if path is valid and the file is readable
61 44
        if ($filename === false || !file_exists($filename) || !is_readable($filename) || !is_file($filename)) {
62 22
            throw new \RuntimeException('File is not found or can not be accessed.');
63
        }
64 22
    }
65
66
    /**
67
     * Unkeyed hash algorithm constructor.
68
     */
69 374
    public function __construct()
70
    {
71 374
    }
72
73
    /**
74
     * Calculates a hash value for the given data.
75
     *
76
     * @param string $data The input string.
77
     *
78
     * @return string The digest.
79
     * @throws \Exception Validation errors.
80
     */
81 140
    public function hashData($data)
82
    {
83 140
        if (!is_string($data)) {
84 14
            throw new \InvalidArgumentException('The data for hashing must be a string or a binary string.');
85
        }
86
87 126
        $data = $this->addSaltString($data);
88
89 126
        $digest = hash(
90 126
            static::ALGORITHM_NAME,
91
            $data,
92 126
            ($this->digestFormat === self::DIGEST_OUTPUT_RAW)
93
        );
94
95 126
        $digest = $this->changeOutputFormat($digest);
96
97 126
        return $digest;
98
    }
99
100
    /**
101
     * Calculates a hash value for the content of the given filename and location.
102
     *
103
     * @param string $filename The full path and name of the file for hashing.
104
     *
105
     * @return string The digest.
106
     * @throws \Exception Validation errors.
107
     */
108 66
    public function hashFile($filename)
109
    {
110
        // Validate input type
111 66
        if (!is_string($filename)) {
112 22
            throw new \InvalidArgumentException('The file path must be of type string.');
113
        }
114
115 44
        $this->validateFileNamePath($filename);
116
117
        $useFileSalting = (
118
            (
119
                // If there is an non-empty salt string set and salting is enabled
120 22
                $this->salt !== '' &&
121 22
                $this->saltingMode !== self::SALTING_MODE_NONE
122
            ) || (
123
                // If there is an empty salt string set and the salting mode duplicates/manipulates the input
124 22
                $this->salt === '' &&
125 22
                in_array($this->saltingMode, [self::SALTING_MODE_INFIX_SALT, self::SALTING_MODE_PALINDROME_MIRRORING])
126
            )
127
        );
128
129 22
        if ($this->useNative || $useFileSalting) {
130
            /**
131
             * {@internal An optimization for native performance that spears string manipulations and function calls. }}
132
             */
133 22
            if (!$useFileSalting) {
134 22
                $oldSalt = $this->salt;
135 22
                $oldMode = $this->saltingMode;
136
137 22
                $this->salt = '';
138 22
                $this->saltingMode = self::SALTING_MODE_NONE;
139
            }
140
141 22
            $digest = $this->hashData(file_get_contents($filename));
142
143 22
            if (!$useFileSalting && isset($oldSalt, $oldMode)) {
144 22
                $this->salt = $oldSalt;
145 22
                $this->saltingMode = $oldMode;
146
            }
147
        } else {
148 22
            $digest = hash_file(
149 22
                static::ALGORITHM_NAME,
150
                $filename,
151 22
                ($this->digestFormat === self::DIGEST_OUTPUT_RAW)
152
            );
153
154 22
            $digest = $this->changeOutputFormat($digest);
155
        }
156
157 22
        return $digest;
158
    }
159
160
    /**
161
     * Get debug information for the class instance.
162
     *
163
     * @return array Debug information.
164
     */
165 22
    public function __debugInfo()
166
    {
167
        return [
168 22
            'standard' => static::ALGORITHM_NAME,
169 22
            'type' => 'unkeyed digestion or checksum',
170 22
            'salt' => $this->salt,
171 22
            'mode' => $this->saltingMode,
172
        ];
173
    }
174
}
175