|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Charcoal\Object; |
|
4
|
|
|
|
|
5
|
|
|
use DateTime; |
|
6
|
|
|
use DateTimeInterface; |
|
7
|
|
|
use Exception; |
|
8
|
|
|
use InvalidArgumentException; |
|
9
|
|
|
|
|
10
|
|
|
// From `pimple/pimple` |
|
11
|
|
|
use Pimple\Container; |
|
12
|
|
|
|
|
13
|
|
|
// From `charcoal-core` |
|
14
|
|
|
use Charcoal\Model\AbstractModel; |
|
15
|
|
|
|
|
16
|
|
|
// From `charcoal-translation` |
|
17
|
|
|
use Charcoal\Translator\TranslatorAwareTrait; |
|
18
|
|
|
|
|
19
|
|
|
// Local namespace (`charcoal-object`) dependency |
|
|
|
|
|
|
20
|
|
|
use Charcoal\Object\UserDataInterface; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* User Data is a base model for objects typically submitted by the end-user of the application. |
|
24
|
|
|
*/ |
|
25
|
|
|
class UserData extends AbstractModel implements |
|
26
|
|
|
UserDataInterface |
|
27
|
|
|
{ |
|
28
|
|
|
use TranslatorAwareTrait; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* Client IP address of the end-user. |
|
32
|
|
|
* |
|
33
|
|
|
* @var integer|null |
|
34
|
|
|
*/ |
|
35
|
|
|
private $ip; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* Language of the end-user or source URI. |
|
39
|
|
|
* |
|
40
|
|
|
* @var string|null |
|
41
|
|
|
*/ |
|
42
|
|
|
private $lang; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* Source URL or identifier of end-user submission. |
|
46
|
|
|
* |
|
47
|
|
|
* @var string|null |
|
48
|
|
|
*/ |
|
49
|
|
|
private $origin; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Creation timestamp of submission. |
|
53
|
|
|
* |
|
54
|
|
|
* @var DateTimeInterface|null |
|
55
|
|
|
*/ |
|
56
|
|
|
private $ts; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* Dependencies |
|
60
|
|
|
* @param Container $container DI Container. |
|
61
|
|
|
* @return void |
|
62
|
|
|
*/ |
|
63
|
|
|
public function setDependencies(Container $container) |
|
64
|
|
|
{ |
|
65
|
|
|
parent::setDependencies($container); |
|
66
|
|
|
|
|
67
|
|
|
$this->setTranslator($container['translator']); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
|
|
71
|
|
|
/** |
|
72
|
|
|
* Set the client IP address. |
|
73
|
|
|
* |
|
74
|
|
|
* @param integer|null $ip The remote IP at object creation. |
|
75
|
|
|
* @return UserDataInterface Chainable |
|
76
|
|
|
*/ |
|
77
|
|
|
public function setIp($ip) |
|
78
|
|
|
{ |
|
79
|
|
|
if ($ip === null) { |
|
80
|
|
|
$this->ip = null; |
|
81
|
|
|
return $this; |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
if (is_string($ip)) { |
|
85
|
|
|
$ip = ip2long($ip); |
|
86
|
|
|
} elseif (is_numeric($ip)) { |
|
87
|
|
|
$ip = (int)$ip; |
|
88
|
|
|
} else { |
|
89
|
|
|
$ip = 0; |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
$this->ip = $ip; |
|
93
|
|
|
|
|
94
|
|
|
return $this; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* Retrieve the client IP address. |
|
99
|
|
|
* |
|
100
|
|
|
* @return integer|null |
|
101
|
|
|
*/ |
|
102
|
|
|
public function ip() |
|
103
|
|
|
{ |
|
104
|
|
|
return $this->ip; |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* Set the origin language. |
|
109
|
|
|
* |
|
110
|
|
|
* @param string $lang The language code. |
|
111
|
|
|
* @throws InvalidArgumentException If the argument is not a string. |
|
112
|
|
|
* @return UserDataInterface Chainable |
|
113
|
|
|
*/ |
|
114
|
|
View Code Duplication |
public function setLang($lang) |
|
|
|
|
|
|
115
|
|
|
{ |
|
116
|
|
|
if ($lang !== null) { |
|
117
|
|
|
if (!is_string($lang)) { |
|
118
|
|
|
throw new InvalidArgumentException( |
|
119
|
|
|
'Language must be a string' |
|
120
|
|
|
); |
|
121
|
|
|
} |
|
122
|
|
|
} |
|
123
|
|
|
|
|
124
|
|
|
$this->lang = $lang; |
|
125
|
|
|
|
|
126
|
|
|
return $this; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* Retrieve the language. |
|
131
|
|
|
* |
|
132
|
|
|
* @return string |
|
133
|
|
|
*/ |
|
134
|
|
|
public function lang() |
|
135
|
|
|
{ |
|
136
|
|
|
return $this->lang; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Set the origin of the object submission. |
|
141
|
|
|
* |
|
142
|
|
|
* @param string $origin The source URL or identifier of the submission. |
|
143
|
|
|
* @throws InvalidArgumentException If the argument is not a string. |
|
144
|
|
|
* @return UserDataInterface Chainable |
|
145
|
|
|
*/ |
|
146
|
|
View Code Duplication |
public function setOrigin($origin) |
|
|
|
|
|
|
147
|
|
|
{ |
|
148
|
|
|
if ($origin !== null) { |
|
149
|
|
|
if (!is_string($origin)) { |
|
150
|
|
|
throw new InvalidArgumentException( |
|
151
|
|
|
'Origin must be a string.' |
|
152
|
|
|
); |
|
153
|
|
|
} |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
$this->origin = $origin; |
|
157
|
|
|
|
|
158
|
|
|
return $this; |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Resolve the origin of the user data. |
|
163
|
|
|
* |
|
164
|
|
|
* @return string |
|
165
|
|
|
*/ |
|
166
|
|
|
public function resolveOrigin() |
|
167
|
|
|
{ |
|
168
|
|
|
$host = getenv('HTTP_HOST'); |
|
169
|
|
|
$uri = ''; |
|
170
|
|
|
if ($host) { |
|
171
|
|
|
$uri = 'http'; |
|
172
|
|
|
|
|
173
|
|
|
if (getenv('HTTPS') === 'on') { |
|
174
|
|
|
$uri .= 's'; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
$uri .= '://'.$host; |
|
178
|
|
|
} |
|
179
|
|
|
$uri .= getenv('REQUEST_URI'); |
|
180
|
|
|
|
|
181
|
|
|
return $uri; |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
/** |
|
185
|
|
|
* Retrieve the origin of the object submission. |
|
186
|
|
|
* |
|
187
|
|
|
* @return string |
|
188
|
|
|
*/ |
|
189
|
|
|
public function origin() |
|
190
|
|
|
{ |
|
191
|
|
|
return $this->origin; |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
/** |
|
195
|
|
|
* Set when the object was created. |
|
196
|
|
|
* |
|
197
|
|
|
* @param DateTime|string|null $timestamp The timestamp at object's creation. |
|
198
|
|
|
* NULL is accepted and instances of DateTimeInterface are recommended; |
|
199
|
|
|
* any other value will be converted (if possible) into one. |
|
200
|
|
|
* @throws InvalidArgumentException If the timestamp is invalid. |
|
201
|
|
|
* @return UserDataInterface Chainable |
|
202
|
|
|
*/ |
|
203
|
|
View Code Duplication |
public function setTs($timestamp) |
|
|
|
|
|
|
204
|
|
|
{ |
|
205
|
|
|
if ($timestamp === null) { |
|
206
|
|
|
$this->ts = null; |
|
207
|
|
|
return $this; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
if (is_string($timestamp)) { |
|
211
|
|
|
try { |
|
212
|
|
|
$timestamp = new DateTime($timestamp); |
|
213
|
|
|
} catch (Exception $e) { |
|
214
|
|
|
throw new InvalidArgumentException(sprintf( |
|
215
|
|
|
'Invalid timestamp: %s', |
|
216
|
|
|
$e->getMessage() |
|
217
|
|
|
), 0, $e); |
|
218
|
|
|
} |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
if (!$timestamp instanceof DateTimeInterface) { |
|
222
|
|
|
throw new InvalidArgumentException( |
|
223
|
|
|
'Invalid timestamp value. Must be a date/time string or a DateTime object.' |
|
224
|
|
|
); |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
$this->ts = $timestamp; |
|
228
|
|
|
|
|
229
|
|
|
return $this; |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* Retrieve the creation timestamp. |
|
234
|
|
|
* |
|
235
|
|
|
* @return DateTime|null |
|
236
|
|
|
*/ |
|
237
|
|
|
public function ts() |
|
238
|
|
|
{ |
|
239
|
|
|
return $this->ts; |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* Event called before _creating_ the object. |
|
244
|
|
|
* |
|
245
|
|
|
* @see Charcoal\Source\StorableTrait::preSave() For the "create" Event. |
|
246
|
|
|
* @return boolean |
|
247
|
|
|
*/ |
|
248
|
|
|
public function preSave() |
|
249
|
|
|
{ |
|
250
|
|
|
$result = parent::preSave(); |
|
251
|
|
|
|
|
252
|
|
|
$this->setTs('now'); |
|
253
|
|
|
|
|
254
|
|
|
if (getenv('REMOTE_ADDR')) { |
|
255
|
|
|
$this->setIp(getenv('REMOTE_ADDR')); |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
if (!isset($this->origin)) { |
|
259
|
|
|
$this->setOrigin($this->resolveOrigin()); |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
return $result; |
|
263
|
|
|
} |
|
264
|
|
|
} |
|
265
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.