Completed
Push — master ( baf356...7843c7 )
by Alex
07:30 queued 03:24
created

ChangeSetParser   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Importance

Changes 5
Bugs 4 Features 0
Metric Value
c 5
b 4
f 0
dl 0
loc 192
rs 8.8
wmc 36

9 Methods

Rating   Name   Duplication   Size   Complexity  
C getResponse() 0 27 7
D handleData() 0 84 18
A getService() 0 3 1
B process() 0 23 5
A getData() 0 3 1
A __construct() 0 4 1
A getRawRequests() 0 3 1
A processSubRequest() 0 8 1
A getBoundary() 0 3 1
1
<?php
2
namespace POData\BatchProcessor;
3
4
use \POData\OperationContext\ServiceHost;
5
use Illuminate\Http\Request;
6
use POData\BaseService;
7
use POData\OperationContext\Web\Illuminate\IlluminateOperationContext;
8
9
class ChangeSetParser implements IBatchParser
10
{
11
    protected $data;
12
    protected $changeSetBoundary;
13
    protected $rawRequests = [];
14
    protected $service;
15
    protected $contentIDToLocationLookup =[];
16
17
    public function __construct(BaseService $service, $body)
18
    {
19
        $this->service = $service;
20
        $this->data = trim($body);
21
    }
22
23
    public function getBoundary()
24
    {
25
        return $this->changeSetBoundary;
26
    }
27
28
    public function process()
29
    {
30
        $raw = $this->getRawRequests();
31
        foreach ($raw as $contentID => &$workingObject) {
32
            foreach ($this->contentIDToLocationLookup as $lookupID => $location) {
33
                if (0 > $lookupID) {
34
                    continue;
35
                }
36
                $workingObject->Content = str_replace('$' . $lookupID, $location, $workingObject->Content);
37
            }
38
39
            $workingObject->Request = Request::create(
40
                $workingObject->RequestURL,
41
                $workingObject->RequestVerb,
42
                [],
43
                [],
44
                [],
45
                $workingObject->ServerParams,
46
                $workingObject->Content
47
            );
48
            $this->processSubRequest($workingObject);
49
            if ('GET' != $workingObject->RequestVerb) {
50
                $this->contentIDToLocationLookup[$contentID] = $workingObject->Response->getHeaders()['Location'];
51
            }
52
        }
53
    }
54
55
    public function getResponse()
56
    {
57
        $response = '';
58
        $splitter = false === $this->changeSetBoundary ? '' : '--' . $this->changeSetBoundary . "\r\n";
59
        $raw = $this->getRawRequests();
60
        foreach ($raw as $contentID => &$workingObject) {
61
            $response .= $splitter;
62
 
63
            $response .= 'Content-Type: application/http' . "\r\n";
64
            $response .= 'Content-Transfer-Encoding: binary' . "\r\n";
65
            $response .= "\r\n";
66
            $headers = $workingObject->Response->getHeaders();
67
            foreach ($headers as $headerName => $headerValue) {
68
                if (null !== $headerValue) {
69
                    $response .= $headerName . ': ' . $headerValue . "\r\n";
70
                }
71
            }
72
            $response .= "\r\n";
73
            $response .= $workingObject->Response->getStream();
74
        }
75
        $response .= trim($splitter);
76
        $response .= false === $this->changeSetBoundary ? "\r\n" : "--\r\n";
77
        $response = 'Content-Length: ' . strlen($response) . "\r\n\r\n" . $response;
78
        $response = false === $this->changeSetBoundary ?
79
            $response :
80
            'Content-Type: multipart/mixed; boundary=' . $this->changeSetBoundary . "\r\n" . $response;
81
        return $response;
82
    }
83
84
    public function handleData()
85
    {
86
        $firstLine = trim(strtok($this->getData(), "\n"));
87
        $this->changeSetBoundary = substr($firstLine, 40);
88
89
        $prefix = 'HTTP_';
90
        $matches = explode('--' . $this->changeSetBoundary, $this->getData());
0 ignored issues
show
Bug introduced by
Are you sure $this->changeSetBoundary of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

90
        $matches = explode('--' . /** @scrutinizer ignore-type */ $this->changeSetBoundary, $this->getData());
Loading history...
91
        array_shift($matches);
92
        $contentIDinit = -1;
93
        foreach ($matches as $match) {
94
            if ('--' === trim($match)) {
95
                continue;
96
            }
97
98
            $stage = 0;
99
            $gotRequestPathParts = false;
100
            $match = trim($match);
101
            $lines = explode("\n", $match);
102
103
            $requestPathParts = [];
104
            $serverParts = [];
105
            $contentID = $contentIDinit;
106
            $content = '';
107
108
            foreach ($lines as $line) {
109
                if ('' == $line) {
110
                    $stage++;
111
                    continue;
112
                }
113
                switch ($stage) {
114
                    case 0:
115
                        if (strtolower('Content-Type') == strtolower(substr($line, 0, 12))
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
Bug introduced by
It seems like substr($line, 0, 12) can also be of type false; however, parameter $str of strtolower() does only seem to accept string, 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

115
                        if (strtolower('Content-Type') == strtolower(/** @scrutinizer ignore-type */ substr($line, 0, 12))
Loading history...
116
                            && 'application/http' != strtolower(substr($line, -16))
117
                        ) {
118
                            //TODO: throw an error about incorrect content type for changeSet
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
119
                        }
120
                        if (strtolower('Content-Transfer-Encoding') == strtolower(substr($line, 0, 25))
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
121
                            && 'binary' != strtolower(substr($line, -6))
122
                        ) {
123
                            //TODO: throw an error about unsupported encoding
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
124
                        }
125
                        break;
126
                    case 1:
127
                        if (!$gotRequestPathParts) {
128
                            $requestPathParts = explode(' ', $line);
129
                            $gotRequestPathParts = true;
130
                            continue;
131
                        }
132
                        $headerSides = explode(':', $line);
133
                        if (count($headerSides) != 2) {
134
                            throw new \Exception('Malformed header line: '.$line);
135
                        }
136
                        if (strtolower(trim($headerSides[0])) == strtolower('Content-ID')) {
137
                            $contentID = trim($headerSides[1]);
138
                            continue;
139
                        }
140
141
                        $name = trim($headerSides[0]);
142
                        $name = strtr(strtoupper($name), '-', '_');
143
                        $value = trim($headerSides[1]);
144
                        if (!starts_with($name, $prefix) && $name != 'CONTENT_TYPE') {
145
                            $name = $prefix . $name;
146
                        }
147
                        $serverParts[$name] = $value;
148
149
                        break;
150
                    case 2:
151
                        $content .= $line;
152
                        break;
153
                    default:
154
                        throw new \Exception('how did we end up with more than 3 stages??');
155
                }
156
            }
157
158
            if ($contentIDinit == $contentID) {
159
                $contentIDinit--;
160
            }
161
            $this->rawRequests[$contentID] = (object)[
162
                'RequestVerb' => $requestPathParts[0],
163
                'RequestURL' => $requestPathParts[1],
164
                'ServerParams' => $serverParts,
165
                'Content' => $content,
166
                'Request' => null,
167
                'Response' => null
168
            ];
169
        }
170
    }
171
172
    /**
173
     * @return BaseService
174
     */
175
    public function getService()
176
    {
177
        return $this->service;
178
    }
179
180
    /**
181
     * @return string
182
     */
183
    public function getData()
184
    {
185
        return $this->data;
186
    }
187
188
    public function getRawRequests()
189
    {
190
        return $this->rawRequests;
191
    }
192
193
    protected function processSubRequest(&$workingObject)
194
    {
195
        $newContext = new IlluminateOperationContext($workingObject->Request);
196
        $newHost = new ServiceHost($newContext, $workingObject->Request);
197
198
        $this->getService()->setHost($newHost);
199
        $this->getService()->handleRequest();
200
        $workingObject->Response = $newContext->outgoingResponse();
201
    }
202
}
203