Passed
Branch master (bfd702)
by Tony Karavasilev (Тони
02:24
created

AbstractUnkeyedHashFunction   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 39
c 1
b 0
f 0
dl 0
loc 135
ccs 41
cts 41
cp 1
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A hashFile() 0 27 3
A hashObject() 0 9 2
A validateFileNamePath() 0 13 6
A hashData() 0 13 2
A __debugInfo() 0 7 1
A __construct() 0 2 1
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\StringBuilder as StringBuilder;
13
14
/**
15
 * Class AbstractUnkeyedHashFunction - Abstraction for unkeyed hash classes.
16
 *
17
 * @package CryptoManana\Core\Abstractions\MessageDigestion
18
 */
19
abstract class AbstractUnkeyedHashFunction extends HashAlgorithm implements ObjectHashing, FileHashing
20
{
21
    /**
22
     * The internal name of the algorithm.
23
     */
24
    const ALGORITHM_NAME = 'none';
25
26
    /**
27
     * Flag to force native code polyfill realizations, if available.
28
     *
29
     * @var bool Flag to force native realizations.
30
     */
31
    protected $useNative = false;
32
33
    /**
34
     * Internal method for location and filename validation.
35
     *
36
     * @param string $filename The filename and location.
37
     *
38
     * @throws \Exception Validation errors.
39
     */
40 44
    protected function validateFileNamePath($filename)
41
    {
42 44
        $filename = StringBuilder::stringReplace("\0", '', $filename); // (ASCII 0 (0x00))
43 44
        $filename = realpath($filename); // Path traversal escape and absolute path fetching
44
45
        // Clear path cache
46 44
        if (!empty($filename)) {
47 22
            clearstatcache(true, $filename);
48
        }
49
50
        // Check if path is valid and the file is readable
51 44
        if ($filename === false || !file_exists($filename) || !is_readable($filename) || !is_file($filename)) {
52 22
            throw new \RuntimeException('File is not found or can not be accessed.');
53
        }
54 22
    }
55
56
    /**
57
     * Unkeyed hash algorithm constructor.
58
     */
59 352
    public function __construct()
60
    {
61 352
    }
62
63
    /**
64
     * Calculates a hash value for the given data.
65
     *
66
     * @param string $data The input string.
67
     *
68
     * @return string The digest.
69
     * @throws \Exception Validation errors.
70
     */
71 126
    public function hashData($data)
72
    {
73 126
        if (!is_string($data)) {
74 14
            throw new \InvalidArgumentException('The data for hashing must be a string or a binary string.');
75
        }
76
77 112
        $data = $this->addSaltString($data);
78
79 112
        $digest = hash(static::ALGORITHM_NAME, $data, ($this->digestFormat === self::DIGEST_OUTPUT_RAW));
80
81 112
        $digest = $this->changeOutputFormat($digest);
82
83 112
        return $digest;
84
    }
85
86
    /**
87
     * Calculates a hash value for the content of the given filename and location.
88
     *
89
     * @param string $filename The full path and name of the file for hashing.
90
     *
91
     * @return string The digest.
92
     * @throws \Exception Validation errors.
93
     */
94 66
    public function hashFile($filename)
95
    {
96
        // Validate input type
97 66
        if (!is_string($filename)) {
98 22
            throw new \InvalidArgumentException('The file path must be of type string.');
99
        }
100
101 44
        $this->validateFileNamePath($filename);
102
103 22
        if ($this->useNative) {
104 22
            $data = file_get_contents($filename);
105
106 22
            $oldSalt = $this->getSalt();
107 22
            $oldMode = $this->getSaltingMode();
108
109 22
            $this->setSalt('')->setSaltingMode(self::SALTING_MODE_NONE);
110
111 22
            $digest = $this->hashData($data);
112
113 22
            $this->setSalt($oldSalt)->setSaltingMode($oldMode);
114
        } else {
115 22
            $digest = hash_file(static::ALGORITHM_NAME, $filename, ($this->digestFormat === self::DIGEST_OUTPUT_RAW));
116
117 22
            $digest = $this->changeOutputFormat($digest);
118
        }
119
120 22
        return $digest;
121
    }
122
123
    /**
124
     * Calculates a hash value for the serialized value of the given object.
125
     *
126
     * @param object|\stdClass $object The full path and name of the file for hashing.
127
     *
128
     * @return string The digest.
129
     * @throws \Exception Validation errors.
130
     */
131 44
    public function hashObject($object)
132
    {
133 44
        if (is_object($object)) {
134 22
            $object = serialize($object);
135
        } else {
136 22
            throw new \InvalidArgumentException('The data for hashing must be an object instance.');
137
        }
138
139 22
        return $this->hashData($object);
140
    }
141
142
    /**
143
     * Get debug information for the class instance.
144
     *
145
     * @return array Debug information.
146
     */
147 22
    public function __debugInfo()
148
    {
149
        return [
150 22
            'standard' => static::ALGORITHM_NAME,
151 22
            'type' => 'unkeyed digestion or checksum',
152 22
            'salt' => $this->salt,
153 22
            'mode' => $this->saltingMode,
154
        ];
155
    }
156
}
157