Completed
Push — 4.0 ( 4d6b64...872671 )
by Marco
03:00
created

Model::setClientCache()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 0
cts 17
cp 0
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 3
nop 2
crap 42
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
    const NO_CONTENT_STATUSES = [100,101,102,204,304];
39
40
    const CACHEABLE_METHODS = ['GET', 'HEAD', 'POST', 'PUT'];
41
42
    const CACHEABLE_STATUSES = [200, 203, 300, 301, 302, 404, 410];
43
44 5
    public function __construct(Configuration $configuration, LoggerInterface $logger) {
45
46 5
        parent::__construct($configuration, $logger);
47
48 5
        $this->setHeaders(new Headers());
49 5
        $this->setCookies(new CookieManager());
50 5
        $this->setStatus(new Status());
51 5
        $this->setContent(new Content());
52 5
        $this->setLocation(new Location());
53
54 5
    }
55
56 5
    public function getHeaders() {
57
58 5
        return $this->headers;
0 ignored issues
show
Bug introduced by
The property headers does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
59
60
    }
61
62 5
    public function setHeaders(Headers $headers) {
63
64 5
        $this->headers = $headers;
65
66 5
        return $this;
67
68
    }
69
70 5
    public function getCookies() {
71
72 5
        return $this->cookies;
0 ignored issues
show
Bug introduced by
The property cookies does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
73
74
    }
75
76 5
    public function setCookies(CookieManager $cookies) {
77
78 5
        $this->cookies = $cookies;
79
80 5
        return $this;
81
82
    }
83
84 7
    public function getStatus() {
85
86 7
        return $this->status;
0 ignored issues
show
Bug introduced by
The property status does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
87
88
    }
89
90 5
    public function setStatus(Status $status) {
91
92 5
        $this->status = $status;
93
94 5
        return $this;
95
96
    }
97
98 7
    public function getContent() {
99
100 7
        return $this->content;
0 ignored issues
show
Bug introduced by
The property content does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
101
102
    }
103
104 5
    public function setContent(Content $content) {
105
106 5
        $this->content = $content;
107
108 5
        return $this;
109
110
    }
111
112 2
    public function getLocation() {
113
114 2
        return $this->location;
0 ignored issues
show
Bug introduced by
The property location does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
115
116
    }
117
118 5
    public function setLocation(Location $location) {
119
120 5
        $this->location = $location;
121
122 5
        return $this;
123
124
    }
125
126
    public function serialize() {
127
128
        return serialize($this->export());
129
130
    }
131
132
    public function unserialize($data) {
133
134
        $this->import(unserialize($data));
135
136
    }
137
138
    public function export() {
139
140
        return (object) [
141
            'headers' => $this->getHeaders(),
142
            'cookies' => $this->getCookies()->getAll(),
143
            'status' => $this->getStatus(),
144
            'content' => $this->getContent(),
145
            'location' => $this->getLocation()
146
        ];
147
148
    }
149
150
    public function import($data) {
151
152
        if ( isset($data->headers) ) $this->setHeaders($data->headers);
153
        if ( isset($data->status) ) $this->setStatus($data->status);
154
        if ( isset($data->content) ) $this->setContent($data->content);
155
        if ( isset($data->location) ) $this->setLocation($data->location);
156
157
        if ( isset($data->cookies) && is_array($data->cookies) ) {
158
            $cookies = $this->getCookies();
159
            foreach ($data->cookies as $name => $cookie) $cookies->add($cookie);
160
        }
161
162
    }
163
164 2
    public function consolidate(Request $request, Route $route = null) {
165
166 2
        $status = $this->getStatus()->get();
167
168 2
        $output_class_name = "\\Comodojo\\Dispatcher\\Response\\Preprocessor\\Status".$status;
169
170
        // @TODO: this condition will be removed when all preprocessors ready
171 2
        if ( class_exists($output_class_name) ) {
172 2
            $output = new $output_class_name($this);
173 2
        } else {
174
            $output = new \Comodojo\Dispatcher\Response\Preprocessor\Status200($this);
175
        }
176
177 2
        $output->consolidate();
178
179 2
        if ( $route != null ) {
180
            $this->setClientCache($request, $route);
181
        }
182
183
        // extra checks
184 2
        $content = $this->getContent();
185 2
        $headers = $this->getHeaders();
186
187 2
        if ( (string) $request->getMethod() == 'HEAD' && !in_array($status, self::NO_CONTENT_STATUSES) ) {
188
            $length = $content->length();
189
            $content->set(null);
190
            if ($length) $headers->set('Content-Length', $length);
191
        }
192
193 2
        if ($headers->get('Transfer-Encoding') != null) {
194
            $headers->delete('Content-Length');
195
        }
196
197 2
        if ( (string) $request->getVersion() == '1.0' && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
198
            $headers->set('pragma', 'no-cache');
199
            $headers->set('expires', -1);
200
        }
201
202 2
    }
203
204
    private function setClientCache(Request $request, Route $route) {
205
206
        $cache = strtoupper($route->getParameter('cache'));
207
        $ttl = (int) $route->getParameter('ttl');
208
209
        if (
210
            ($cache == 'CLIENT' || $cache == 'BOTH') &&
211
            in_array((string) $request->getMethod(), self::CACHEABLE_METHODS) &&
212
            in_array($this->getStatus()->get(), self::CACHEABLE_STATUSES)
213
            // @TODO: here we should also check for Cache-Control no-store or private;
214
            //        the cache layer will be improoved in future versions.
215
        ) {
216
217
            $headers = $this->getHeaders();
218
            $timestamp = (int) $this->getTime()->format('U') + $ttl;
219
220
            if ( $ttl > 0 ) {
221
222
                $headers->set("Cache-Control", "max-age=".$ttl.", must-revalidate");
223
                $headers->set("Expires", gmdate("D, d M Y H:i:s", $timestamp)." GMT");
224
225
            } else {
226
227
                $headers->set("Cache-Control", "no-cache, must-revalidate");
228
                $headers->set("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
229
230
            }
231
232
        }
233
234
    }
235
236
}
237