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