ServerRequestFactory   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 168
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 95.45%

Importance

Changes 0
Metric Value
dl 0
loc 168
ccs 63
cts 66
cp 0.9545
rs 10
c 0
b 0
f 0
wmc 27
lcom 1
cbo 7

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A createFromGlobals() 0 4 1
A createFromSocketBody() 0 9 1
D create() 0 42 9
A getPhpServerValuesFromPlainHeader() 0 14 2
A getUri() 0 9 2
B getScheme() 0 8 5
A getHostAndPort() 0 14 4
A getRequestUri() 0 7 2
1
<?php
2
namespace Wandu\Http\Factory;
3
4
use Psr\Http\Message\StreamInterface;
5
use Wandu\Http\Psr\ServerRequest;
6
use Wandu\Http\Psr\Stream;
7
use Wandu\Http\Psr\Stream\PhpInputStream;
8
use Wandu\Http\Psr\Uri;
9
10
class ServerRequestFactory
11
{
12
    use HelperTrait;
13
14
    /** @var \Wandu\Http\Factory\UploadedFileFactory */
15
    protected $fileFactory;
16
17
    /**
18
     * @param \Wandu\Http\Factory\UploadedFileFactory $fileFactory
19
     */
20 16
    public function __construct(UploadedFileFactory $fileFactory)
21
    {
22 16
        $this->fileFactory = $fileFactory;
23 16
    }
24
25
    /**
26
     * @return \Psr\Http\Message\ServerRequestInterface
27
     */
28
    public function createFromGlobals()
0 ignored issues
show
Coding Style introduced by
createFromGlobals uses the super-global variable $_SERVER 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...
Coding Style introduced by
createFromGlobals uses the super-global variable $_GET 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...
Coding Style introduced by
createFromGlobals uses the super-global variable $_POST 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...
Coding Style introduced by
createFromGlobals uses the super-global variable $_COOKIE 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...
Coding Style introduced by
createFromGlobals 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...
29
    {
30
        return $this->create($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES, new PhpInputStream());
31
    }
32
33
    /**
34
     * @param string $body
35
     * @return \Psr\Http\Message\ServerRequestInterface
36
     */
37 1
    public function createFromSocketBody($body)
38
    {
39 1
        $lines = array_map('trim', explode("\n", $body));
40 1
        $blankKey = array_search('', $lines);
41
42 1
        $phpServer = $this->getPhpServerValuesFromPlainHeader(array_slice($lines, 0, $blankKey));
43
44 1
        return $this->create($phpServer, [], [], [], []);
45
    }
46
47
    /**
48
     * @param array $server
49
     * @param array $get
50
     * @param array $post
51
     * @param array $cookies
52
     * @param array $files
53
     * @param \Psr\Http\Message\StreamInterface $stream
54
     * @return \Psr\Http\Message\ServerRequestInterface
55
     */
56 16
    public function create(
57
        array $server,
58
        array $get,
59
        array $post,
60
        array $cookies,
61
        array $files,
62
        StreamInterface $stream = null
63
    ) {
64 16
        if (!isset($stream)) {
65 5
            $stream = new Stream('php://memory');
66
        }
67 16
        $headers = $this->getPsrHeadersFromServerParams($server);
68
69
        // exists body, but not exists posts
70 16
        $bodyContent = $stream->__toString();
71 16
        if ($bodyContent !== '' && count($post) === 0) {
72 10
            if (isset($headers['content-type'])) {
73
                // do not define multipart/form-data
74
                // because, it use only in POST method.
75
                // ref. en: https://issues.apache.org/jira/browse/FILEUPLOAD-197#comment-13595136
76
                // ref. kr: https://blog.outsider.ne.kr/1001
77 10
                if (strpos($headers['content-type'], 'application/json') === 0) {
78 6
                    $jsonBody = json_decode($bodyContent, true);
79 6
                    $post = $jsonBody ? $jsonBody : $post;
80 4
                } elseif (strpos($headers['content-type'], 'application/x-www-form-urlencoded') === 0) {
81 4
                    parse_str($bodyContent, $post);
82
                }
83
            }
84
        }
85 16
        return new ServerRequest(
86 16
            isset($server['REQUEST_METHOD']) ? $server['REQUEST_METHOD'] : 'GET',
87 16
            $this->getUri($server),
88 16
            $stream,
89 16
            $headers,
90 16
            '1.1',
91 16
            $server,
92 16
            $get,
93 16
            $post,
94 16
            $cookies,
95 16
            $this->fileFactory->createFromFiles($files)
96
        );
97
    }
98
99
    /**
100
     * Parse plain headers.
101
     *
102
     * @param array $plainHeaders
103
     * @return array
104
     */
105 1
    protected function getPhpServerValuesFromPlainHeader(array $plainHeaders)
106
    {
107 1
        $httpInformation = explode(' ', array_shift($plainHeaders));
108
        $servers = [
109 1
            'REQUEST_METHOD' => $httpInformation[0],
110 1
            'REQUEST_URI' => $httpInformation[1],
111 1
            'SERVER_PROTOCOL' => $httpInformation[2],
112
        ];
113 1
        foreach ($plainHeaders as $plainHeader) {
114 1
            list($key, $value) = array_map('trim', explode(':', $plainHeader, 2));
115 1
            $servers['HTTP_' . strtoupper(str_replace('-', '_', $key))] = $value;
116
        }
117 1
        return $servers;
118
    }
119
120
    /**
121
     * @param array $server
122
     * @return \Wandu\Http\Psr\Uri
123
     */
124 16
    protected function getUri(array $server)
125
    {
126 16
        $stringUri = $this->getHostAndPort($server);
127 16
        if ($stringUri !== '') {
128 5
            $stringUri = $this->getScheme($server) . '://' . $stringUri;
129
        }
130 16
        $stringUri .= $this->getRequestUri($server);
131 16
        return new Uri($stringUri);
132
    }
133
134
    /**
135
     * @param array $server
136
     * @return string
137
     */
138 5
    protected function getScheme(array $server)
139
    {
140 5
        if ((isset($server['HTTPS']) && $server['HTTPS'] !== 'off')
141 5
            || (isset($server['HTTP_X_FORWAREDED_PROTO']) && $server['HTTP_X_FORWAREDED_PROTO'] === 'https')) {
142
            return 'https';
143
        }
144 5
        return 'http';
145
    }
146
147
    /**
148
     * @param array $server
149
     * @return string
150
     */
151 16
    protected function getHostAndPort(array $server)
152
    {
153 16
        if (isset($server['HTTP_HOST'])) {
154 4
            return $server['HTTP_HOST'];
155
        }
156 12
        if (!isset($server['SERVER_NAME'])) {
157 11
            return '';
158
        }
159 1
        $host = $server['SERVER_NAME'];
160 1
        if (isset($server['SERVER_PORT'])) {
161 1
            $host .= ':' . $server['SERVER_PORT'];
162
        }
163 1
        return $host;
164
    }
165
166
    /**
167
     * @param array $server
168
     * @return string
169
     */
170 16
    protected function getRequestUri(array $server)
171
    {
172 16
        if (isset($server['REQUEST_URI'])) {
173 4
            return $server['REQUEST_URI'];
174
        }
175 12
        return '/';
176
    }
177
}
178