Completed
Push — master ( 616932...69cdb4 )
by Nazar
04:13
created

Response::cookie()   D

Complexity

Conditions 9
Paths 96

Size

Total Lines 35
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 35
rs 4.909
cc 9
eloc 27
nc 96
nop 4
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs;
9
use
10
	cs\Response\Compatibility,
11
	cs\Response\Psr7;
12
13
class Response implements \ArrayAccess, \Iterator {
14
	use
15
		Singleton,
16
		Compatibility,
17
		Psr7;
18
	/**
19
	 * Protocol, for instance: `HTTP/1.0`, `HTTP/1.1` (default), HTTP/2.0
20
	 *
21
	 * @var string
22
	 */
23
	public $protocol;
24
	/**
25
	 * HTTP status code
26
	 *
27
	 * @var int
28
	 */
29
	public $code;
30
	/**
31
	 * Headers are normalized to lowercase keys with hyphen as separator, for instance: `connection`, `referer`, `content-type`, `accept-language`
32
	 *
33
	 * Values might be strings in case of single value or array of strings in case of multiple values with the same field name
34
	 *
35
	 * @var string[][]
36
	 */
37
	public $headers;
38
	/**
39
	 * String body (is used instead of `$this->body_stream` in most cases, ignored if `$this->body_stream` is present)
40
	 *
41
	 * @var string
42
	 */
43
	public $body;
44
	/**
45
	 * Body in form of stream (might be used instead of `$this->body` in some cases, if present, `$this->body` is ignored)
46
	 *
47
	 * Stream is read/write
48
	 *
49
	 * @var resource
50
	 */
51
	public $body_stream;
52
	/**
53
	 * Initialize response object with specified data
54
	 *
55
	 * @param string               $body
56
	 * @param null|resource|string $body_stream String, like `php://temp` or resource, like `fopen('php://temp', 'a+b')`, if present, `$body` is ignored
57
	 * @param string[]|string[][]  $headers     Headers are normalized to lowercase keys with hyphen as separator, for instance: `connection`, `referer`,
58
	 *                                          `content-type`, `accept-language`; Values might be strings in case of single value or array of strings in case
59
	 *                                          of multiple values with the same field name
60
	 * @param int                  $code        HTTP status code
61
	 * @param string               $protocol    Protocol, for instance: `HTTP/1.0`, `HTTP/1.1` (default), HTTP/2.0
62
	 *
63
	 * @return Response
64
	 */
65
	function init ($body = '', $body_stream = null, $headers = [], $code = 200, $protocol = 'HTTP/1.1') {
66
		$this->protocol = $protocol;
67
		$this->code     = $code;
68
		$this->headers  = _array($headers);
69
		$this->body     = $body;
70
		if ($this->body_stream) {
71
			fclose($this->body_stream);
72
		}
73
		$this->data_stream = is_string($body_stream) ? fopen($body_stream, 'a+b') : $body_stream;
74
		return $this;
75
	}
76
	/**
77
	 * Initialize with typical default settings (headers `Content-Type` and `Vary`, protocol taken from `cs\Request::$protocol`)
78
	 *
79
	 * @return Response
80
	 */
81
	function init_with_typical_default_settings () {
82
		return $this->init(
83
			'',
84
			null,
85
			[
86
				'Content-Type' => 'text/html; charset=utf-8',
87
				'Vary'         => 'Accept-Language,User-Agent,Cookie'
88
			],
89
			200,
90
			Request::instance()->protocol
91
		);
92
	}
93
	/**
94
	 * Set raw HTTP header
95
	 *
96
	 * @param string $field        Field
97
	 * @param string $value        Value, empty string will cause header removal
98
	 * @param bool   $replace      The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header
99
	 *                             of the same type. By default it will replace
100
	 *
101
	 * @return Response
102
	 */
103
	function header ($field, $value, $replace = true) {
104
		$field = strtolower($field);
105
		if ($value === '') {
106
			unset($this->headers[$field]);
107
		} elseif ($replace || !isset($this->headers[$field])) {
108
			$this->headers[$field] = [$value];
109
		} else {
110
			$this->headers[$field][] = $value;
111
		}
112
		return $this;
113
	}
114
	/**
115
	 * Make redirect to specified location
116
	 *
117
	 * @param string $location
118
	 * @param int    $code
119
	 *
120
	 * @return Response
121
	 */
122
	function redirect ($location, $code = 302) {
123
		$this->header('location', $location);
124
		$this->code = $code;
125
		return $this;
126
	}
127
	/**
128
	 * Function for setting cookies, taking into account cookies prefix. Parameters like in system `setcookie()` function, but `$path`, `$domain` and `$secure`
129
	 * are skipped, they are detected automatically
130
	 *
131
	 * This function have side effect of setting cookie on `cs\Request` object
132
	 *
133
	 * @param string $name
134
	 * @param string $value
135
	 * @param int    $expire
136
	 * @param bool   $httponly
137
	 *
138
	 * @return Response
139
	 */
140
	function cookie ($name, $value, $expire = 0, $httponly = false) {
141
		$Request = Request::instance();
142
		$Config  = Config::instance();
143
		$prefix  = '';
144
		$domain  = explode(':', $Request->host)[0];
145
		if ($Config) {
146
			$prefix         = $Config->core['cookie_prefix'];
147
			$cookie_domains = $Config->core['cookie_domain'];
148
			$domain         = isset($cookie_domains[$Request->mirror_index]) ? $cookie_domains[$Request->mirror_index] : $cookie_domains[0];
149
		}
150
		if ($value === '') {
151
			unset($Request->cookie[$name], $Request->cookie[$prefix.$name]);
152
		} else {
153
			$Request->cookie[$name]         = $value;
154
			$Request->cookie[$prefix.$name] = $value;
155
		}
156
		$header = [
157
			rawurlencode($prefix.$name).'='.rawurlencode($value),
158
			'path=/'
159
		];
160
		if ($expire || !$value) {
161
			$header[] = 'expires='.gmdate('D, d-M-Y H:i:s', $expire).' GMT';
162
		}
163
		if ($domain) {
164
			$header[] = "domain=$domain";
165
		}
166
		if ($Request->secure) {
167
			$header[] = 'secure';
168
		}
169
		if ($httponly) {
170
			$header[] = 'HttpOnly';
171
		}
172
		$this->header('set-cookie', implode('; ', $header), false);
173
		return $this;
174
	}
175
	/**
176
	 * Provides default output for all the response data using `header()`, `http_response_code()` and `echo` or `php://output`
177
	 */
178
	function output_default () {
179
		foreach ($this->headers as $header => $value) {
180
			foreach ($value as $v) {
181
				header("$header: $v", false);
182
			}
183
		}
184
		http_response_code($this->code);
185
		if (is_resource($this->body_stream)) {
186
			$position = ftell($this->body_stream);
187
			stream_copy_to_stream($this->body_stream, fopen('php:://output', 'wb'));
188
			fseek($this->body_stream, $position);
189
		} else {
190
			echo $this->body;
191
		}
192
	}
193
}
194