Passed
Push — master ( 4ac306...d2aa89 )
by Mihail
05:30
created

build_files_array()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 11
c 0
b 0
f 0
nc 5
nop 1
dl 0
loc 16
ccs 10
cts 10
cp 1
crap 6
rs 9.2222
1
<?php
2
3
namespace Koded\Http;
4
5
/*
6
 * This file is part of the Koded package.
7
 *
8
 * (c) Mihail Binev <[email protected]>
9
 *
10
 * Please view the LICENSE distributed with this source code
11
 * for the full copyright and license information.
12
 *
13
 */
14
15
use InvalidArgumentException;
16
use Psr\Http\Message\{StreamInterface, UploadedFileInterface};
17
use RuntimeException;
18
19
/**
20
 * @param null|callable|StreamInterface|object|resource $resource A gypsy wannabe argument
21
 * @param string                                        $mode
22
 *
23
 * @return StreamInterface
24
 */
25
function create_stream($resource, string $mode = 'r+'): StreamInterface
26
{
27 188
    if (null === $resource || is_string($resource)) {
28 161
        $stream = fopen('php://temp', $mode);
29 161
        fwrite($stream, $resource);
0 ignored issues
show
Bug introduced by
It seems like $stream can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, 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

29
        fwrite(/** @scrutinizer ignore-type */ $stream, $resource);
Loading history...
30 161
        fseek($stream, 0);
0 ignored issues
show
Bug introduced by
It seems like $stream can also be of type false; however, parameter $handle of fseek() does only seem to accept resource, 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

30
        fseek(/** @scrutinizer ignore-type */ $stream, 0);
Loading history...
31
32 161
        return new Stream($stream);
33
    }
34
35 28
    if ($resource instanceof StreamInterface) {
36 3
        return $resource;
37
    }
38
39 25
    if (is_callable($resource)) {
40 2
        return new CallableStream($resource);
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type object and resource; however, parameter $callable of Koded\Http\CallableStream::__construct() does only seem to accept callable, 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

40
        return new CallableStream(/** @scrutinizer ignore-type */ $resource);
Loading history...
41
    }
42
43 23
    $type = gettype($resource);
44
45 23
    if ('resource' === $type) {
46 21
        return new Stream($resource);
47
    }
48
49 2
    if ('object' === $type && method_exists($resource, '__toString')) {
50 1
        return create_stream((string)$resource);
51
    }
52
53 1
    throw new InvalidArgumentException('Failed to create a stream. '
54
        . 'Expected a file name, StreamInterface instance, or a resource. '
55 1
        . "Given {$type} type.");
56
}
57
58
/**
59
 * Copies the stream to another stream object.
60
 *
61
 * @param StreamInterface $source      The source stream object
62
 * @param StreamInterface $destination Destination stream object
63
 * @param int             [optional] $length Read up to $length bytes from the source stream.
64
 *                                     Fewer than $length bytes may be returned if underlying stream
65
 *                                     call returns fewer bytes
66
 *
67
 * @return int The total count of bytes copied
68
 */
69
function stream_copy(StreamInterface $source, StreamInterface $destination, int $length = 8192): int
70
{
71 1
    $bytes = 0;
72 1
    while (false === $source->eof()) {
73 1
        $bytes += $destination->write($source->read($length));
74
    }
75
76 1
    $destination->close();
77
78 1
    return $bytes;
79
}
80
81
/**
82
 * @param StreamInterface $stream
83
 *
84
 * @return string
85
 * @throws RuntimeException on failure
86
 */
87
function stream_to_string(StreamInterface $stream): string
88
{
89 3
    $content = '';
90 3
    $stream->rewind();
91
92 3
    while (false === $stream->eof()) {
93 3
        $content .= $stream->read(1048576); // 1MB
94
    }
95
96 2
    return $content;
97
}
98
99
/**
100
 * Transforms the array to much desired files array structure.
101
 * Deals with any nested level.
102
 *
103
 * @param array $files Typically the $_FILES array
104
 *
105
 * @return array A files array to a sane format
106
 */
107
function normalize_files_array(array $files): array
108
{
109
    $sane = function(array $files, array $file = [], array $path = []) use (&$sane) {
110 8
        foreach ($files as $k => $v) {
111 8
            $list   = $path;
112 8
            $list[] = $k;
113
114 8
            if (is_array($v)) {
115 7
                $file = $sane($v, $file, $list);
116
            } else {
117 8
                $next = array_splice($list, 1, 1);
118 8
                $copy = &$file;
119 8
                foreach (array_merge($list, $next) as $k) {
0 ignored issues
show
Comprehensibility Bug introduced by
$k is overwriting a variable from outer foreach loop.
Loading history...
120 8
                    $copy = &$copy[$k];
121
                }
122 8
                $copy = $v;
123
            }
124
        }
125
126 8
        return $file;
127 8
    };
128
129 8
    return $sane($files);
130
}
131
132
/**
133
 * Preserves the array structure and replaces the
134
 * file description with instance of UploadedFile.
135
 *
136
 * @param array $files Normalized _FILES array
137
 *
138
 * @return array An array tree of UploadedFileInterface instances
139
 */
140
function build_files_array(array $files): array
141
{
142 8
    foreach ($files as $index => $file) {
143 8
        if ($file instanceof UploadedFileInterface) {
144 2
            $files[$index] = $file;
145 7
        } elseif (isset($file['tmp_name']) && is_array($file)) {
146 6
            $files[$index] = new UploadedFile($file);
147 4
        } elseif (is_array($file)) {
148 3
            $files[$index] = build_files_array($file);
149 3
            continue;
150
        } else {
151 1
            throw new InvalidArgumentException('Failed to process the uploaded files. Invalid file structure provided');
152
        }
153
    }
154
155 7
    return $files;
156
}
157