Completed
Push — 4.0 ( 0e3cc0...125a8d )
by Marco
03:38
created

Model::consolidate()   D

Complexity

Conditions 9
Paths 48

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 19.125

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 41
ccs 14
cts 28
cp 0.5
rs 4.909
cc 9
eloc 22
nc 48
nop 2
crap 19.125
1
<?php namespace Comodojo\Dispatcher\Response;
2
3
use \Comodojo\Dispatcher\Components\AbstractModel;
4
use \Comodojo\Dispatcher\Request\Model as Request;
5
use \Comodojo\Dispatcher\Router\Route;
6
use \Comodojo\Foundation\Timing\TimingTrait;
7
use \Comodojo\Foundation\Base\Configuration;
8
use \Comodojo\Cookies\CookieManager;
9
use \Psr\Log\LoggerInterface;
10
use \Serializable;
11
12
/**
13
 * @package     Comodojo Dispatcher
14
 * @author      Marco Giovinazzi <[email protected]>
15
 * @author      Marco Castiello <[email protected]>
16
 * @license     GPL-3.0+
17
 *
18
 * LICENSE:
19
 *
20
 * This program is free software: you can redistribute it and/or modify
21
 * it under the terms of the GNU Affero General Public License as
22
 * published by the Free Software Foundation, either version 3 of the
23
 * License, or (at your option) any later version.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License
31
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32
 */
33
34
class Model extends AbstractModel implements Serializable {
35
36
    use TimingTrait;
37
38
    protected static $no_content_statuses = [100, 101, 102, 204, 304];
39
40
    protected static $cacheable_methods = ['GET', 'HEAD', 'POST', 'PUT'];
41
42
    protected static $cacheable_statuses = [200, 203, 300, 301, 302, 404, 410];
43
44
    protected $headers;
45
46
    protected $cookies;
47
48
    protected $status;
49
50
    protected $content;
51
52
    protected $location;
53
    
54 5
    public function __construct(Configuration $configuration, LoggerInterface $logger) {
55
56 5
        parent::__construct($configuration, $logger);
57
58 5
        $this->setHeaders(new Headers());
59 5
        $this->setCookies(new CookieManager());
60 5
        $this->setStatus(new Status());
61 5
        $this->setContent(new Content());
62 5
        $this->setLocation(new Location());
63
64 5
    }
65
66 5
    public function getHeaders() {
67
68 5
        return $this->headers;
69
70
    }
71
72 5
    public function setHeaders(Headers $headers) {
73
74 5
        $this->headers = $headers;
75
76 5
        return $this;
77
78
    }
79
80 5
    public function getCookies() {
81
82 5
        return $this->cookies;
83
84
    }
85
86 5
    public function setCookies(CookieManager $cookies) {
87
88 5
        $this->cookies = $cookies;
89
90 5
        return $this;
91
92
    }
93
94 7
    public function getStatus() {
95
96 7
        return $this->status;
97
98
    }
99
100 5
    public function setStatus(Status $status) {
101
102 5
        $this->status = $status;
103
104 5
        return $this;
105
106
    }
107
108 7
    public function getContent() {
109
110 7
        return $this->content;
111
112
    }
113
114 5
    public function setContent(Content $content) {
115
116 5
        $this->content = $content;
117
118 5
        return $this;
119
120
    }
121
122 2
    public function getLocation() {
123
124 2
        return $this->location;
125
126
    }
127
128 5
    public function setLocation(Location $location) {
129
130 5
        $this->location = $location;
131
132 5
        return $this;
133
134
    }
135
136
    public function serialize() {
137
138
        return serialize($this->export());
139
140
    }
141
142
    public function unserialize($data) {
143
144
        $this->import(unserialize($data));
145
146
    }
147
148
    public function export() {
149
150
        return (object)[
151
            'headers' => $this->getHeaders(),
152
            'cookies' => $this->getCookies()->getAll(),
153
            'status' => $this->getStatus(),
154
            'content' => $this->getContent(),
155
            'location' => $this->getLocation()
156
        ];
157
158
    }
159
160
    public function import($data) {
161
162
        if (isset($data->headers)) $this->setHeaders($data->headers);
163
        if (isset($data->status)) $this->setStatus($data->status);
164
        if (isset($data->content)) $this->setContent($data->content);
165
        if (isset($data->location)) $this->setLocation($data->location);
166
167
        if (isset($data->cookies) && is_array($data->cookies)) {
168
            $cookies = $this->getCookies();
169
            foreach ($data->cookies as $name => $cookie) $cookies->add($cookie);
170
        }
171
172
    }
173
174 2
    public function consolidate(Request $request, Route $route = null) {
175
176 2
        $status = $this->getStatus()->get();
177
178 2
        $output_class_name = "\\Comodojo\\Dispatcher\\Response\\Preprocessor\\Status".$status;
179
180
        // @TODO: this condition will be removed when all preprocessors ready
181 2
        if (class_exists($output_class_name)) {
182 2
            $output = new $output_class_name($this);
183 2
        } else {
184
            $output = new \Comodojo\Dispatcher\Response\Preprocessor\Status200($this);
185
        }
186
187 2
        $output->consolidate();
188
189 2
        if ($route != null) {
190
            $this->setClientCache($request, $route);
191
        }
192
193
        // extra checks
194 2
        $content = $this->getContent();
195 2
        $headers = $this->getHeaders();
196
197 2
        if ((string)$request->getMethod() == 'HEAD' && !in_array($status, self::$no_content_statuses)) {
198
            $length = $content->length();
199
            $content->set(null);
200
            if ($length) {
201
                $headers->set('Content-Length', $length);
202
            }
203
        }
204
205 2
        if ($headers->get('Transfer-Encoding') != null) {
206
            $headers->delete('Content-Length');
207
        }
208
209 2
        if ((string)$request->getVersion() == '1.0' && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
210
            $headers->set('pragma', 'no-cache');
211
            $headers->set('expires', -1);
212
        }
213
214 2
    }
215
216
    private function setClientCache(Request $request, Route $route) {
217
218
        $cache = strtoupper($route->getParameter('cache'));
219
        $ttl = (int)$route->getParameter('ttl');
220
221
        if (
222
            ($cache == 'CLIENT' || $cache == 'BOTH') &&
223
            in_array((string)$request->getMethod(), self::$cacheable_methods) &&
224
            in_array($this->getStatus()->get(), self::$cacheable_statuses)
225
            // @TODO: here we should also check for Cache-Control no-store or private;
226
            //        the cache layer will be improoved in future versions.
227
        ) {
228
229
            $headers = $this->getHeaders();
230
            $timestamp = (int)$this->getTime()->format('U')+$ttl;
231
232
            if ($ttl > 0) {
233
234
                $headers->set("Cache-Control", "max-age=".$ttl.", must-revalidate");
235
                $headers->set("Expires", gmdate("D, d M Y H:i:s", $timestamp)." GMT");
236
237
            } else {
238
239
                $headers->set("Cache-Control", "no-cache, must-revalidate");
240
                $headers->set("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
241
242
            }
243
244
        }
245
246
    }
247
248
}
249