Issues (31)

src/config/Alias.php (2 issues)

Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace tkanstantsin\fileupload\config;
5
6
use tkanstantsin\fileupload\model\ExternalFile;
7
use tkanstantsin\fileupload\model\IFile;
8
use tkanstantsin\fileupload\model\Type;
9
10
/**
11
 * Class Alias represent config for model
12
 * @todo: add `accept` type.
13
 */
14
class Alias
15
{
16
    public const DEFAULT_MAX_SIZE = 5 * 1000 * 1000; // 5 MB
17
    public const DEFAULT_MAX_COUNT = 5;
18
    public const DEFAULT_HASH_LENGTH = 3;
19
    /**
20
     * @see hash_algos()
21
     */
22
    public const DEFAULT_HASH_METHOD = 'crc32'; // not crypto-strong, but fast (as needed)
23
24
    /**
25
     * Alias name
26
     * @var string
27
     */
28
    public $name;
29
30
    /**
31
     * Owner model class
32
     * @var string
33
     */
34
    public $class;
35
    /**
36
     * @var string
37
     */
38
    public $directory;
39
40
    /**
41
     * Max file size in bytes
42
     * @var int
43
     */
44
    public $maxSize;
45
    /**
46
     * Max file count
47
     * @var int
48
     */
49
    public $maxCount;
50
    /**
51
     * Whether it is allowed multiple files for one model
52
     * @var bool
53
     */
54
    public $multiple;
55
56
    /**
57
     * Hash method like crc32 or md5
58
     * @var string
59
     */
60
    public $hashMethod;
61
    /**
62
     * Length of hash generated by hash method
63
     * @var int
64
     */
65
    public $cacheHashLength;
66
67
    /**
68
     * @var callable|null
69
     */
70
    public $filePathClosure;
71
    /**
72
     * @var callable|null
73
     */
74
    public $assetNameClosure;
75
76
77
    /**
78
     * Alias constructor.
79
     * @param array $config
80
     * @throws InvalidConfigException
81
     */
82 1
    public function __construct(array $config = [])
83
    {
84 1
        foreach ($config as $key => $value) {
85 1
            $this->$key = $value;
86
        }
87
88 1
        if (preg_match('/^[a-z0-9-_]$/i', $this->name)) {
89
            throw new InvalidConfigException(sprintf('Alias name must contain only latin letters, digits, hyphen (-) and underscore. `%s` got.', $this->name));
90
        }
91 1
        if (!\is_int($this->maxSize) || $this->maxSize <= 0) {
0 ignored issues
show
The condition is_int($this->maxSize) is always true.
Loading history...
92
            throw new InvalidConfigException(sprintf('Maximum file size must be positive integer but `%s` got.', $this->maxSize));
93
        }
94 1
        if (!\is_int($this->maxCount) || $this->maxCount <= 0) {
0 ignored issues
show
The condition is_int($this->maxCount) is always true.
Loading history...
95
            throw new InvalidConfigException(sprintf('Maximum file count must be positive integer but `%s` got.', $this->maxCount));
96
        }
97 1
        if (!\in_array($this->hashMethod, hash_algos(), true)) {
98
            throw new InvalidConfigException(sprintf('Hash method `%s` not found.', $this->hashMethod));
99
        }
100
        // TODO: create interface with all such methods.
101
        // Implement base version. And allow to use custom realizations.
102 1
        if ($this->filePathClosure !== null && !\is_callable($this->filePathClosure)) {
103
            throw new InvalidConfigException('FilePathClosure must be callable.');
104
        }
105 1
        if ($this->assetNameClosure !== null && !\is_callable($this->assetNameClosure)) {
106
            throw new InvalidConfigException('AssetNameClosure must be callable.');
107
        }
108 1
    }
109
110
    /**
111
     * Returns file path in contentFS
112
     * @param IFile $file
113
     * @return string|null
114
     */
115 1
    public function getFilePath(IFile $file): ?string
116
    {
117 1
        if ($this->filePathClosure !== null) {
118
            return \call_user_func($this->filePathClosure, $file);
119
        }
120
121 1
        if ($file instanceof ExternalFile) {
122
            return $file->getActualPath();
123
        }
124
125 1
        return $file->getId() !== null
126 1
            ? $this->getFileDirectory($file) . DIRECTORY_SEPARATOR . $this->getFileName($file)
127 1
            : null;
128
    }
129
130
    /**
131
     * Returns path in asset filesystem
132
     * @param IFile $file
133
     * @param string $formatName
134
     * @return string
135
     */
136
    public function getAssetPath(IFile $file, string $formatName): string
137
    {
138
        return implode('/', array_filter([
139
            Type::$folderPrefix[$file->getType()] . '_' . $formatName,
140
            $file->getModelAlias(),
141
            mb_substr($file->getHash(), 0, $this->cacheHashLength),
142
            $this->getAssetName($file),
143
        ]));
144
    }
145
146
    /**
147
     * File name in asset directory
148
     * @param IFile $file
149
     * @return string
150
     */
151
    public function getAssetName(IFile $file): string
152
    {
153
        if ($this->assetNameClosure !== null) {
154
            return \call_user_func($this->assetNameClosure, $file);
155
        }
156
157
        return $file->getId() . '_' . $file->getFullName();
158
    }
159
160
    /**
161
     * @param IFile $file
162
     * @return string
163
     */
164 1
    public function getFileName(IFile $file): string
165
    {
166 1
        return $file->getId() . ($file->getExtension() !== null ? '.' . $file->getExtension() : '');
167
    }
168
169
    /**
170
     * Returns target directory for uploaded file
171
     * @param IFile $file
172
     * @return string
173
     */
174 1
    public function getFileDirectory(IFile $file): string
175
    {
176 1
        return implode(DIRECTORY_SEPARATOR, array_filter([
177 1
            $this->directory,
178
            // TODO: check if used correct hash.
179
            // Which value should be passed into getDirectoryHash? Directory name or file id?
180 1
            $this->getDirectoryHash((string) $file->getId()),
181
        ]));
182
    }
183
184
    /**
185
     * @param string $value E.g. directory name
186
     * @return string
187
     */
188 1
    protected function getDirectoryHash(string $value): ?string
189
    {
190 1
        return $this->cacheHashLength > 0
191 1
            ? mb_substr(hash($this->hashMethod, $value), 0, $this->cacheHashLength)
192 1
            : null;
193
    }
194
}