pictcode / lib / Cake / Controller / Component / CookieComponent.php @ 0b1b8047
履歴 | 表示 | アノテート | ダウンロード (14.843 KB)
1 | 635eef61 | spyder1211 | <?php
|
---|---|---|---|
2 | /**
|
||
3 | * Cookie Component
|
||
4 | *
|
||
5 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
6 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
7 | *
|
||
8 | * Licensed under The MIT License
|
||
9 | * For full copyright and license information, please see the LICENSE.txt
|
||
10 | * Redistributions of files must retain the above copyright notice.
|
||
11 | *
|
||
12 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
13 | * @link http://cakephp.org CakePHP(tm) Project
|
||
14 | * @package Cake.Controller.Component
|
||
15 | * @since CakePHP(tm) v 1.2.0.4213
|
||
16 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
17 | */
|
||
18 | |||
19 | App::uses('Component', 'Controller'); |
||
20 | App::uses('Security', 'Utility'); |
||
21 | App::uses('Hash', 'Utility'); |
||
22 | |||
23 | /**
|
||
24 | * Cookie Component.
|
||
25 | *
|
||
26 | * Cookie handling for the controller.
|
||
27 | *
|
||
28 | * @package Cake.Controller.Component
|
||
29 | * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html
|
||
30 | */
|
||
31 | class CookieComponent extends Component { |
||
32 | |||
33 | /**
|
||
34 | * The name of the cookie.
|
||
35 | *
|
||
36 | * Overridden with the controller beforeFilter();
|
||
37 | * $this->Cookie->name = 'CookieName';
|
||
38 | *
|
||
39 | * @var string
|
||
40 | */
|
||
41 | public $name = 'CakeCookie'; |
||
42 | |||
43 | /**
|
||
44 | * The time a cookie will remain valid.
|
||
45 | *
|
||
46 | * Can be either integer Unix timestamp or a date string.
|
||
47 | *
|
||
48 | * Overridden with the controller beforeFilter();
|
||
49 | * $this->Cookie->time = '5 Days';
|
||
50 | *
|
||
51 | * @var mixed
|
||
52 | */
|
||
53 | public $time = null; |
||
54 | |||
55 | /**
|
||
56 | * Cookie path.
|
||
57 | *
|
||
58 | * Overridden with the controller beforeFilter();
|
||
59 | * $this->Cookie->path = '/';
|
||
60 | *
|
||
61 | * The path on the server in which the cookie will be available on.
|
||
62 | * If public $cookiePath is set to '/foo/', the cookie will only be available
|
||
63 | * within the /foo/ directory and all sub-directories such as /foo/bar/ of domain.
|
||
64 | * The default value is the entire domain.
|
||
65 | *
|
||
66 | * @var string
|
||
67 | */
|
||
68 | public $path = '/'; |
||
69 | |||
70 | /**
|
||
71 | * Domain path.
|
||
72 | *
|
||
73 | * The domain that the cookie is available.
|
||
74 | *
|
||
75 | * Overridden with the controller beforeFilter();
|
||
76 | * $this->Cookie->domain = '.example.com';
|
||
77 | *
|
||
78 | * To make the cookie available on all subdomains of example.com.
|
||
79 | * Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter
|
||
80 | *
|
||
81 | * @var string
|
||
82 | */
|
||
83 | public $domain = ''; |
||
84 | |||
85 | /**
|
||
86 | * Secure HTTPS only cookie.
|
||
87 | *
|
||
88 | * Overridden with the controller beforeFilter();
|
||
89 | * $this->Cookie->secure = true;
|
||
90 | *
|
||
91 | * Indicates that the cookie should only be transmitted over a secure HTTPS connection.
|
||
92 | * When set to true, the cookie will only be set if a secure connection exists.
|
||
93 | *
|
||
94 | * @var bool
|
||
95 | */
|
||
96 | public $secure = false; |
||
97 | |||
98 | /**
|
||
99 | * Encryption key.
|
||
100 | *
|
||
101 | * Overridden with the controller beforeFilter();
|
||
102 | * $this->Cookie->key = 'SomeRandomString';
|
||
103 | *
|
||
104 | * @var string
|
||
105 | */
|
||
106 | public $key = null; |
||
107 | |||
108 | /**
|
||
109 | * HTTP only cookie
|
||
110 | *
|
||
111 | * Set to true to make HTTP only cookies. Cookies that are HTTP only
|
||
112 | * are not accessible in JavaScript.
|
||
113 | *
|
||
114 | * @var bool
|
||
115 | */
|
||
116 | public $httpOnly = false; |
||
117 | |||
118 | /**
|
||
119 | * Values stored in the cookie.
|
||
120 | *
|
||
121 | * Accessed in the controller using $this->Cookie->read('Name.key');
|
||
122 | *
|
||
123 | * @see CookieComponent::read();
|
||
124 | * @var string
|
||
125 | */
|
||
126 | protected $_values = array(); |
||
127 | |||
128 | /**
|
||
129 | * Type of encryption to use.
|
||
130 | *
|
||
131 | * Currently two methods are available: cipher and rijndael
|
||
132 | * Defaults to Security::cipher(). Cipher is horribly insecure and only
|
||
133 | * the default because of backwards compatibility. In new applications you should
|
||
134 | * always change this to 'aes' or 'rijndael'.
|
||
135 | *
|
||
136 | * @var string
|
||
137 | */
|
||
138 | protected $_type = 'cipher'; |
||
139 | |||
140 | /**
|
||
141 | * Used to reset cookie time if $expire is passed to CookieComponent::write()
|
||
142 | *
|
||
143 | * @var string
|
||
144 | */
|
||
145 | protected $_reset = null; |
||
146 | |||
147 | /**
|
||
148 | * Expire time of the cookie
|
||
149 | *
|
||
150 | * This is controlled by CookieComponent::time;
|
||
151 | *
|
||
152 | * @var string
|
||
153 | */
|
||
154 | protected $_expires = 0; |
||
155 | |||
156 | /**
|
||
157 | * A reference to the Controller's CakeResponse object
|
||
158 | *
|
||
159 | * @var CakeResponse
|
||
160 | */
|
||
161 | protected $_response = null; |
||
162 | |||
163 | /**
|
||
164 | * Constructor
|
||
165 | *
|
||
166 | * @param ComponentCollection $collection A ComponentCollection for this component
|
||
167 | * @param array $settings Array of settings.
|
||
168 | */
|
||
169 | public function __construct(ComponentCollection $collection, $settings = array()) { |
||
170 | $this->key = Configure::read('Security.salt'); |
||
171 | parent::__construct($collection, $settings); |
||
172 | if (isset($this->time)) { |
||
173 | $this->_expire($this->time); |
||
174 | } |
||
175 | |||
176 | $controller = $collection->getController(); |
||
177 | if ($controller && isset($controller->response)) { |
||
178 | $this->_response = $controller->response; |
||
179 | } else {
|
||
180 | $this->_response = new CakeResponse(); |
||
181 | } |
||
182 | } |
||
183 | |||
184 | /**
|
||
185 | * Start CookieComponent for use in the controller
|
||
186 | *
|
||
187 | * @param Controller $controller Controller instance.
|
||
188 | * @return void
|
||
189 | */
|
||
190 | public function startup(Controller $controller) { |
||
191 | $this->_expire($this->time); |
||
192 | |||
193 | $this->_values[$this->name] = array(); |
||
194 | } |
||
195 | |||
196 | /**
|
||
197 | * Write a value to the $_COOKIE[$key];
|
||
198 | *
|
||
199 | * Optional [Name.], required key, optional $value, optional $encrypt, optional $expires
|
||
200 | * $this->Cookie->write('[Name.]key, $value);
|
||
201 | *
|
||
202 | * By default all values are encrypted.
|
||
203 | * You must pass $encrypt false to store values in clear test
|
||
204 | *
|
||
205 | * You must use this method before any output is sent to the browser.
|
||
206 | * Failure to do so will result in header already sent errors.
|
||
207 | *
|
||
208 | * @param string|array $key Key for the value
|
||
209 | * @param mixed $value Value
|
||
210 | * @param bool $encrypt Set to true to encrypt value, false otherwise
|
||
211 | * @param int|string $expires Can be either the number of seconds until a cookie
|
||
212 | * expires, or a strtotime compatible time offset.
|
||
213 | * @return void
|
||
214 | * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::write
|
||
215 | */
|
||
216 | public function write($key, $value = null, $encrypt = true, $expires = null) { |
||
217 | if (empty($this->_values[$this->name])) { |
||
218 | $this->read();
|
||
219 | } |
||
220 | |||
221 | if ($encrypt === null) { |
||
222 | $encrypt = true; |
||
223 | } |
||
224 | $this->_encrypted = $encrypt; |
||
225 | $this->_expire($expires); |
||
226 | |||
227 | if (!is_array($key)) { |
||
228 | $key = array($key => $value); |
||
229 | } |
||
230 | |||
231 | foreach ($key as $name => $value) { |
||
232 | $names = array($name); |
||
233 | if (strpos($name, '.') !== false) { |
||
234 | $names = explode('.', $name, 2); |
||
235 | } |
||
236 | $firstName = $names[0]; |
||
237 | $isMultiValue = (is_array($value) || count($names) > 1); |
||
238 | |||
239 | if (!isset($this->_values[$this->name][$firstName]) && $isMultiValue) { |
||
240 | $this->_values[$this->name][$firstName] = array(); |
||
241 | } |
||
242 | |||
243 | if (count($names) > 1) { |
||
244 | $this->_values[$this->name][$firstName] = Hash::insert( |
||
245 | $this->_values[$this->name][$firstName], |
||
246 | $names[1], |
||
247 | $value
|
||
248 | ); |
||
249 | } else {
|
||
250 | $this->_values[$this->name][$firstName] = $value; |
||
251 | } |
||
252 | $this->_write('[' . $firstName . ']', $this->_values[$this->name][$firstName]); |
||
253 | } |
||
254 | $this->_encrypted = true; |
||
255 | } |
||
256 | |||
257 | /**
|
||
258 | * Read the value of the $_COOKIE[$key];
|
||
259 | *
|
||
260 | * Optional [Name.], required key
|
||
261 | * $this->Cookie->read(Name.key);
|
||
262 | *
|
||
263 | * @param string $key Key of the value to be obtained. If none specified, obtain map key => values
|
||
264 | * @return string|null Value for specified key
|
||
265 | * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::read
|
||
266 | */
|
||
267 | public function read($key = null) { |
||
268 | if (empty($this->_values[$this->name]) && isset($_COOKIE[$this->name])) { |
||
269 | $this->_values[$this->name] = $this->_decrypt($_COOKIE[$this->name]); |
||
270 | } |
||
271 | if (empty($this->_values[$this->name])) { |
||
272 | $this->_values[$this->name] = array(); |
||
273 | } |
||
274 | if ($key === null) { |
||
275 | return $this->_values[$this->name]; |
||
276 | } |
||
277 | |||
278 | if (strpos($key, '.') !== false) { |
||
279 | $names = explode('.', $key, 2); |
||
280 | $key = $names[0]; |
||
281 | } |
||
282 | if (!isset($this->_values[$this->name][$key])) { |
||
283 | return null; |
||
284 | } |
||
285 | |||
286 | if (!empty($names[1]) && is_array($this->_values[$this->name][$key])) { |
||
287 | return Hash::get($this->_values[$this->name][$key], $names[1]); |
||
288 | } |
||
289 | return $this->_values[$this->name][$key]; |
||
290 | } |
||
291 | |||
292 | /**
|
||
293 | * Returns true if given variable is set in cookie.
|
||
294 | *
|
||
295 | * @param string $key Variable name to check for
|
||
296 | * @return bool True if variable is there
|
||
297 | */
|
||
298 | public function check($key = null) { |
||
299 | if (empty($key)) { |
||
300 | return false; |
||
301 | } |
||
302 | return $this->read($key) !== null; |
||
303 | } |
||
304 | |||
305 | /**
|
||
306 | * Delete a cookie value
|
||
307 | *
|
||
308 | * Optional [Name.], required key
|
||
309 | * $this->Cookie->delete('Name.key);
|
||
310 | *
|
||
311 | * You must use this method before any output is sent to the browser.
|
||
312 | * Failure to do so will result in header already sent errors.
|
||
313 | *
|
||
314 | * This method will delete both the top level and 2nd level cookies set.
|
||
315 | * For example assuming that $name = App, deleting `User` will delete
|
||
316 | * both `App[User]` and any other cookie values like `App[User][email]`
|
||
317 | * This is done to clean up cookie storage from before 2.4.3, where cookies
|
||
318 | * were stored inconsistently.
|
||
319 | *
|
||
320 | * @param string $key Key of the value to be deleted
|
||
321 | * @return void
|
||
322 | * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::delete
|
||
323 | */
|
||
324 | public function delete($key) { |
||
325 | if (empty($this->_values[$this->name])) { |
||
326 | $this->read();
|
||
327 | } |
||
328 | if (strpos($key, '.') === false) { |
||
329 | if (isset($this->_values[$this->name][$key]) && is_array($this->_values[$this->name][$key])) { |
||
330 | foreach ($this->_values[$this->name][$key] as $idx => $val) { |
||
331 | $this->_delete("[$key][$idx]"); |
||
332 | } |
||
333 | } |
||
334 | $this->_delete("[$key]"); |
||
335 | unset($this->_values[$this->name][$key]); |
||
336 | return;
|
||
337 | } |
||
338 | $names = explode('.', $key, 2); |
||
339 | if (isset($this->_values[$this->name][$names[0]])) { |
||
340 | $this->_values[$this->name][$names[0]] = Hash::remove($this->_values[$this->name][$names[0]], $names[1]); |
||
341 | } |
||
342 | $this->_delete('[' . implode('][', $names) . ']'); |
||
343 | } |
||
344 | |||
345 | /**
|
||
346 | * Destroy current cookie
|
||
347 | *
|
||
348 | * You must use this method before any output is sent to the browser.
|
||
349 | * Failure to do so will result in header already sent errors.
|
||
350 | *
|
||
351 | * @return void
|
||
352 | * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::destroy
|
||
353 | */
|
||
354 | public function destroy() { |
||
355 | if (isset($_COOKIE[$this->name])) { |
||
356 | $this->_values[$this->name] = $this->_decrypt($_COOKIE[$this->name]); |
||
357 | } |
||
358 | |||
359 | foreach ($this->_values[$this->name] as $name => $value) { |
||
360 | if (is_array($value)) { |
||
361 | foreach ($value as $key => $val) { |
||
362 | unset($this->_values[$this->name][$name][$key]); |
||
363 | $this->_delete("[$name][$key]"); |
||
364 | } |
||
365 | } |
||
366 | unset($this->_values[$this->name][$name]); |
||
367 | $this->_delete("[$name]"); |
||
368 | } |
||
369 | } |
||
370 | |||
371 | /**
|
||
372 | * Will allow overriding default encryption method. Use this method
|
||
373 | * in ex: AppController::beforeFilter() before you have read or
|
||
374 | * written any cookies.
|
||
375 | *
|
||
376 | * @param string $type Encryption method
|
||
377 | * @return void
|
||
378 | */
|
||
379 | public function type($type = 'cipher') { |
||
380 | $availableTypes = array( |
||
381 | 'cipher',
|
||
382 | 'rijndael',
|
||
383 | 'aes'
|
||
384 | ); |
||
385 | if (!in_array($type, $availableTypes)) { |
||
386 | trigger_error(__d('cake_dev', 'You must use cipher, rijndael or aes for cookie encryption type'), E_USER_WARNING); |
||
387 | $type = 'cipher'; |
||
388 | } |
||
389 | $this->_type = $type; |
||
390 | } |
||
391 | |||
392 | /**
|
||
393 | * Set the expire time for a session variable.
|
||
394 | *
|
||
395 | * Creates a new expire time for a session variable.
|
||
396 | * $expire can be either integer Unix timestamp or a date string.
|
||
397 | *
|
||
398 | * Used by write()
|
||
399 | * CookieComponent::write(string, string, boolean, 8400);
|
||
400 | * CookieComponent::write(string, string, boolean, '5 Days');
|
||
401 | *
|
||
402 | * @param int|string $expires Can be either Unix timestamp, or date string
|
||
403 | * @return int Unix timestamp
|
||
404 | */
|
||
405 | protected function _expire($expires = null) { |
||
406 | if ($expires === null) { |
||
407 | return $this->_expires; |
||
408 | } |
||
409 | $this->_reset = $this->_expires; |
||
410 | if (!$expires) { |
||
411 | return $this->_expires = 0; |
||
412 | } |
||
413 | $now = new DateTime(); |
||
414 | |||
415 | if (is_int($expires) || is_numeric($expires)) { |
||
416 | return $this->_expires = $now->format('U') + (int)$expires; |
||
417 | } |
||
418 | $now->modify($expires); |
||
419 | return $this->_expires = $now->format('U'); |
||
420 | } |
||
421 | |||
422 | /**
|
||
423 | * Set cookie
|
||
424 | *
|
||
425 | * @param string $name Name for cookie
|
||
426 | * @param string $value Value for cookie
|
||
427 | * @return void
|
||
428 | */
|
||
429 | protected function _write($name, $value) { |
||
430 | $this->_response->cookie(array( |
||
431 | 'name' => $this->name . $name, |
||
432 | 'value' => $this->_encrypt($value), |
||
433 | 'expire' => $this->_expires, |
||
434 | 'path' => $this->path, |
||
435 | 'domain' => $this->domain, |
||
436 | 'secure' => $this->secure, |
||
437 | 'httpOnly' => $this->httpOnly |
||
438 | )); |
||
439 | |||
440 | if (!empty($this->_reset)) { |
||
441 | $this->_expires = $this->_reset; |
||
442 | $this->_reset = null; |
||
443 | } |
||
444 | } |
||
445 | |||
446 | /**
|
||
447 | * Sets a cookie expire time to remove cookie value
|
||
448 | *
|
||
449 | * @param string $name Name of cookie
|
||
450 | * @return void
|
||
451 | */
|
||
452 | protected function _delete($name) { |
||
453 | $this->_response->cookie(array( |
||
454 | 'name' => $this->name . $name, |
||
455 | 'value' => '', |
||
456 | 'expire' => time() - 42000, |
||
457 | 'path' => $this->path, |
||
458 | 'domain' => $this->domain, |
||
459 | 'secure' => $this->secure, |
||
460 | 'httpOnly' => $this->httpOnly |
||
461 | )); |
||
462 | } |
||
463 | |||
464 | /**
|
||
465 | * Encrypts $value using public $type method in Security class
|
||
466 | *
|
||
467 | * @param string $value Value to encrypt
|
||
468 | * @return string Encoded values
|
||
469 | */
|
||
470 | protected function _encrypt($value) { |
||
471 | if (is_array($value)) { |
||
472 | $value = $this->_implode($value); |
||
473 | } |
||
474 | if (!$this->_encrypted) { |
||
475 | return $value; |
||
476 | } |
||
477 | $prefix = "Q2FrZQ==."; |
||
478 | if ($this->_type === 'rijndael') { |
||
479 | $cipher = Security::rijndael($value, $this->key, 'encrypt'); |
||
480 | } |
||
481 | if ($this->_type === 'cipher') { |
||
482 | $cipher = Security::cipher($value, $this->key); |
||
483 | } |
||
484 | if ($this->_type === 'aes') { |
||
485 | $cipher = Security::encrypt($value, $this->key); |
||
486 | } |
||
487 | return $prefix . base64_encode($cipher); |
||
488 | } |
||
489 | |||
490 | /**
|
||
491 | * Decrypts $value using public $type method in Security class
|
||
492 | *
|
||
493 | * @param array $values Values to decrypt
|
||
494 | * @return string decrypted string
|
||
495 | */
|
||
496 | protected function _decrypt($values) { |
||
497 | $decrypted = array(); |
||
498 | $type = $this->_type; |
||
499 | |||
500 | foreach ((array)$values as $name => $value) { |
||
501 | if (is_array($value)) { |
||
502 | foreach ($value as $key => $val) { |
||
503 | $decrypted[$name][$key] = $this->_decode($val); |
||
504 | } |
||
505 | } else {
|
||
506 | $decrypted[$name] = $this->_decode($value); |
||
507 | } |
||
508 | } |
||
509 | return $decrypted; |
||
510 | } |
||
511 | |||
512 | /**
|
||
513 | * Decodes and decrypts a single value.
|
||
514 | *
|
||
515 | * @param string $value The value to decode & decrypt.
|
||
516 | * @return string Decoded value.
|
||
517 | */
|
||
518 | protected function _decode($value) { |
||
519 | $prefix = 'Q2FrZQ==.'; |
||
520 | $pos = strpos($value, $prefix); |
||
521 | if ($pos === false) { |
||
522 | return $this->_explode($value); |
||
523 | } |
||
524 | $value = base64_decode(substr($value, strlen($prefix))); |
||
525 | if ($this->_type === 'rijndael') { |
||
526 | $plain = Security::rijndael($value, $this->key, 'decrypt'); |
||
527 | } |
||
528 | if ($this->_type === 'cipher') { |
||
529 | $plain = Security::cipher($value, $this->key); |
||
530 | } |
||
531 | if ($this->_type === 'aes') { |
||
532 | $plain = Security::decrypt($value, $this->key); |
||
533 | } |
||
534 | return $this->_explode($plain); |
||
535 | } |
||
536 | |||
537 | /**
|
||
538 | * Implode method to keep keys are multidimensional arrays
|
||
539 | *
|
||
540 | * @param array $array Map of key and values
|
||
541 | * @return string A json encoded string.
|
||
542 | */
|
||
543 | protected function _implode(array $array) { |
||
544 | return json_encode($array); |
||
545 | } |
||
546 | |||
547 | /**
|
||
548 | * Explode method to return array from string set in CookieComponent::_implode()
|
||
549 | * Maintains reading backwards compatibility with 1.x CookieComponent::_implode().
|
||
550 | *
|
||
551 | * @param string $string A string containing JSON encoded data, or a bare string.
|
||
552 | * @return array Map of key and values
|
||
553 | */
|
||
554 | protected function _explode($string) { |
||
555 | $first = substr($string, 0, 1); |
||
556 | if ($first === '{' || $first === '[') { |
||
557 | $ret = json_decode($string, true); |
||
558 | return ($ret !== null) ? $ret : $string; |
||
559 | } |
||
560 | $array = array(); |
||
561 | foreach (explode(',', $string) as $pair) { |
||
562 | $key = explode('|', $pair); |
||
563 | if (!isset($key[1])) { |
||
564 | return $key[0]; |
||
565 | } |
||
566 | $array[$key[0]] = $key[1]; |
||
567 | } |
||
568 | return $array; |
||
569 | } |
||
570 | } |