Completed
Push — master ( 6b8416...c520c0 )
by Filipe
02:12
created

UploadedFilesFactory::filesFlip()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 3
1
<?php
2
3
/**
4
 * This file is part of Http
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Http\Message\Server;
11
12
/**
13
 * UploadedFilesFactory
14
 *
15
 * @package Slick\Http\Message\Server
16
 */
17
class UploadedFilesFactory
18
{
19
20
    /**
21
     * Creates the uploaded file objects within a normalized files tree
22
     *
23
     * @return array|UploadedFile[]
24
     */
25
    public static function createFiles()
0 ignored issues
show
Coding Style introduced by
createFiles uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
26
    {
27
        $fixer = new static();
28
        $files = $fixer->saneFilesArray($_FILES);
29
        $fixed = [];
30
        foreach ($files as $key => $data) {
31
            $fixed[$key] = $fixer->createUploadedFile($data);
32
        }
33
        return $fixed;
34
    }
35
36
    /**
37
     * Helper function to recursively create UploadedFile objects
38
     *
39
     * @param array $data
40
     *
41
     * @return array|UploadedFile
42
     */
43
    private function createUploadedFile($data)
44
    {
45
        if (array_key_exists('tmp_name', $data)) {
46
            return UploadedFile::create($data);
47
        }
48
49
        $result = [];
50
        foreach ($data as $key => $datum) {
51
            $result[$key] = $this->createUploadedFile($datum);
52
        }
53
        return $result;
54
    }
55
56
    /**
57
     * Fixes the $_FILES array structure
58
     *
59
     * For each subtree in the file tree that's more than one item deep:
60
     *      For each leaf of the subtree:
61
     *      $leaf[a][b][c] ... [y][z] -> $result[z][a][b][c]  ... [y]
62
     *
63
     *
64
     * @see: https://stackoverflow.com/a/24397828/1271488
65
     *
66
     * @param array $files
67
     * @return array
68
     */
69
    private function saneFilesArray(array $files)
70
    {
71
        $result = [];
72
73
        foreach($files as $field => $data) {
74
            foreach($data as $key => $val) {
75
                $result[$field] = [];
76
                if(!is_array($val)) {
77
                    $result[$field] = $data;
78
                    continue;
79
                }
80
81
                $res = [];
82
                $this->filesFlip($res, [], $data);
83
                $result[$field] += $res;
84
            }
85
        }
86
87
        return $result;
88
    }
89
90
    /**
91
     * Move the innermost key to the outer spot
92
     *
93
     * @param array  $result
94
     * @param array  $keys
95
     * @param mixed  $value
96
     */
97
    private function filesFlip(&$result, $keys, $value)
98
    {
99
        if(is_array($value)) {
100
            foreach($value as $k => $v) {
101
                $newKeys = $keys;
102
                array_push($newKeys, $k);
103
                $this->filesFlip($result, $newKeys, $v);
104
            }
105
            return;
106
        }
107
108
        $res = $value;
109
        // Move the innermost key to the outer spot
110
        $first = array_shift($keys);
111
        array_push($keys, $first);
112
        foreach(array_reverse($keys) as $kk) {
113
            // You might think we'd say $res[$kk] = $res, but $res starts
114
            // out not as an array
115
            $res = array($kk => $res);
116
        }
117
118
        $result = $this->arrayMergeRecursive($result, $res);
119
    }
120
121
    /**
122
     * Recursively merge provided arrays
123
     *
124
     * @param array|string $array1
125
     * @param array|string $array2
126
     *
127
     * @return array
128
     */
129
    private function arrayMergeRecursive($array1, $array2)
130
    {
131
        if (!is_array($array1) or !is_array($array2)) { return $array2; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
132
133
        foreach ($array2 AS $sKey2 => $sValue2) {
134
            $array1[$sKey2] = $this->arrayMergeRecursive(@$array1[$sKey2], $sValue2);
135
        }
136
        return $array1;
137
    }
138
}