UploadedFileFactory   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Test Coverage

Coverage 97.83%

Importance

Changes 2
Bugs 0 Features 2
Metric Value
eloc 42
c 2
b 0
f 2
dl 0
loc 136
ccs 45
cts 46
cp 0.9783
rs 10
wmc 23

6 Methods

Rating   Name   Duplication   Size   Complexity  
A asUploadedFileInstance() 0 18 3
A createUploadedFile() 0 12 2
A isUploadedFileEntry() 0 7 4
A __construct() 0 3 1
A normalizeUploadedFileArray() 0 14 4
B createUploadedFilesFromGlobalFiles() 0 25 9
1
<?php
2
3
namespace DMT\Aura\Psr\Factory;
4
5
use DMT\Aura\Psr\Message\UploadedFile;
6
use InvalidArgumentException;
7
use Psr\Http\Message\StreamFactoryInterface;
8
use Psr\Http\Message\StreamInterface;
9
use Psr\Http\Message\UploadedFileFactoryInterface;
10
use Psr\Http\Message\UploadedFileInterface;
11
use const UPLOAD_ERR_OK;
12
13
/**
14
 * Class UploadedFileFactory
15
 *
16
 * @package DMT\Aura\Psr\Factory
17
 */
18
class UploadedFileFactory implements UploadedFileFactoryInterface
19
{
20
    /** @var StreamFactoryInterface $streamFactory */
21
    protected $streamFactory;
22
23
    /**
24
     * UploadedFileFactory constructor.
25
     * @param StreamFactoryInterface $streamFactory
26
     */
27 16
    public function __construct(StreamFactoryInterface $streamFactory = null)
28
    {
29 16
        $this->streamFactory = $streamFactory ?? new StreamFactory();
30 16
    }
31
32
    /**
33
     * Create a new uploaded file.
34
     *
35
     * If a size is not provided it will be determined by checking the size of
36
     * the file.
37
     *
38
     * @see http://php.net/manual/features.file-upload.post-method.php
39
     * @see http://php.net/manual/features.file-upload.errors.php
40
     *
41
     * @param StreamInterface $stream Underlying stream representing the uploaded file content.
42
     * @param int $size in bytes
43
     * @param int $error PHP file upload error
44
     * @param string $clientFilename Filename as provided by the client, if any.
45
     * @param string $clientMediaType Media type as provided by the client, if any.
46
     *
47
     * @return UploadedFileInterface
48
     *
49
     * @throws InvalidArgumentException If the file resource is not readable.
50
     */
51 16
    public function createUploadedFile(
52
        StreamInterface $stream,
53
        int $size = null,
54
        int $error = UPLOAD_ERR_OK,
55
        string $clientFilename = null,
56
        string $clientMediaType = null
57
    ): UploadedFileInterface {
58 16
        if (!$stream->isReadable()) {
59 1
            throw new InvalidArgumentException('file can not be read');
60
        }
61
62 15
        return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
63
    }
64
65
    /**
66
     * Create uploaded files from $_FILES.
67
     *
68
     * @param array $uploadedFiles
69
     * @return array|UploadedFileInterface[]
70
     */
71 4
    public function createUploadedFilesFromGlobalFiles(array $uploadedFiles): array
72
    {
73 4
        foreach ($uploadedFiles as $file => &$uploadedFile) {
74 4
            if (!is_array($uploadedFile) || !array_key_exists('tmp_name', $uploadedFile)) {
75
                continue;
76
            }
77
78 4
            if (is_array($uploadedFile['tmp_name'])) {
79 2
                $files = [];
80 2
                foreach ($uploadedFile as $key => $values) {
81 2
                    if (is_string(key($values))) {
82 2
                        $files[key($values)][$key] = current($values);
83
                    }
84
                }
85 2
                if ($files) {
86 1
                    $uploadedFile = $this->createUploadedFilesFromGlobalFiles($files);
87
                }
88
            }
89
90 4
            if ($this->isUploadedFileEntry($uploadedFile)) {
91 4
                $uploadedFile = $this->asUploadedFileInstance($uploadedFile);
92
            }
93
        }
94
95 4
        return $uploadedFiles;
96
    }
97
98
    /**
99
     * @param array $uploadedFileEntry
100
     * @return bool
101
     */
102 4
    private function isUploadedFileEntry($uploadedFileEntry): bool
103
    {
104 4
        if (!is_array($uploadedFileEntry) || !array_key_exists('tmp_name', $uploadedFileEntry)) {
0 ignored issues
show
introduced by
The condition is_array($uploadedFileEntry) is always true.
Loading history...
105 1
            return false;
106
        }
107
108 4
        return is_string($uploadedFileEntry['tmp_name']) || is_string(current((array)$uploadedFileEntry['tmp_name']));
109
    }
110
111
    /**
112
     * @param array $uploadedFile
113
     * @return array|UploadedFileInterface
114
     */
115 4
    private function asUploadedFileInstance($uploadedFile)
116
    {
117 4
        if (is_array($uploadedFile['tmp_name'])) {
118 1
            return array_map([$this, __FUNCTION__], $this->normalizeUploadedFileArray($uploadedFile));
119
        }
120
121 4
        if ($uploadedFile['error'] === UPLOAD_ERR_OK) {
122 3
            $stream = $this->streamFactory->createStreamFromFile($uploadedFile['tmp_name']);
123
        } else {
124 1
            $stream = $this->streamFactory->createStream();
125
        }
126
127 4
        return $this->createUploadedFile(
128 4
            $stream,
129 4
            $uploadedFile['size'] ?? null,
130 4
            $uploadedFile['error'] ?? UPLOAD_ERR_OK,
131 4
            $uploadedFile['name'] ?? null,
132 4
            $uploadedFile['type'] ?? null
133
        );
134
    }
135
136
    /**
137
     * @param array $uploadedFile
138
     * @return array
139
     */
140 1
    private function normalizeUploadedFileArray(array $uploadedFile)
141
    {
142 1
        if (!is_string($uploadedFile['tmp_name'])) {
143 1
            $files = [];
144 1
            for ($i = 0; $i < count($uploadedFile['tmp_name']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
145 1
                $files[$i] = [];
146 1
                foreach ($uploadedFile as $key => $value) {
147 1
                    $files[$i][$key] = $value[$i] ?? null;
148
                }
149
            }
150 1
            $uploadedFile = $files;
151
        }
152
153 1
        return $uploadedFile;
154
    }
155
}
156