diff --git a/framework/web/Cookie.php b/framework/web/Cookie.php index 40bab05..2d8662e 100644 --- a/framework/web/Cookie.php +++ b/framework/web/Cookie.php @@ -48,4 +48,20 @@ class Cookie extends \yii\base\Object * such as JavaScript, which can effectively help to reduce identity theft through XSS attacks. */ public $httpOnly = false; + + /** + * Magic method to turn a cookie object into a string without having to explicitly access [[value]]. + * + * ~~~ + * if (isset($request->cookies['name'])) { + * $value = (string)$request->cookies['name']; + * } + * ~~~ + * + * @return string The value of the cookie. If the value property is null, an empty string will be returned. + */ + public function __toString() + { + return (string)$this->value; + } } diff --git a/framework/web/CookieCollection.php b/framework/web/CookieCollection.php index 1d87bf0..bafd990 100644 --- a/framework/web/CookieCollection.php +++ b/framework/web/CookieCollection.php @@ -1,6 +1,6 @@ $value) // traverse the items in the dictionary - * $n = count($dictionary); // returns the number of items in the dictionary - * ~~~ - * - * @property integer $count the number of items in the dictionary - * @property array $keys The keys in the dictionary + * @property integer $count the number of cookies in the collection * * @author Qiang Xue * @since 2.0 @@ -39,15 +22,13 @@ use yii\base\DictionaryIterator; class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ArrayAccess, \Countable { /** - * @var Cookie[] internal data storage + * @var Cookie[] the cookies in this collection (indexed by the cookie names) */ private $_cookies = array(); /** * Constructor. - * Initializes the dictionary with an array or an iterable object. - * @param array $cookies the initial data to be populated into the dictionary. - * This can be an array or an iterable object. + * @param Cookie[] $cookies the initial cookies in the collection. * @param array $config name-value pairs that will be used to initialize the object properties */ public function __construct($cookies = array(), $config = array()) @@ -57,10 +38,10 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ } /** - * Returns an iterator for traversing the items in the dictionary. + * Returns an iterator for traversing the cookies in the collection. * This method is required by the SPL interface `IteratorAggregate`. - * It will be implicitly called when you use `foreach` to traverse the dictionary. - * @return DictionaryIterator an iterator for traversing the items in the dictionary. + * It will be implicitly called when you use `foreach` to traverse the collection. + * @return DictionaryIterator an iterator for traversing the cookies in the collection. */ public function getIterator() { @@ -68,10 +49,10 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ } /** - * Returns the number of items in the dictionary. + * Returns the number of cookies in the collection. * This method is required by the SPL `Countable` interface. - * It will be implicitly called when you use `count($dictionary)`. - * @return integer number of items in the dictionary. + * It will be implicitly called when you use `count($collection)`. + * @return integer the number of cookies in the collection. */ public function count() { @@ -79,8 +60,8 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ } /** - * Returns the number of items in the dictionary. - * @return integer the number of items in the dictionary + * Returns the number of cookies in the collection. + * @return integer the number of cookies in the collection. */ public function getCount() { @@ -88,72 +69,74 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ } /** - * Returns the keys stored in the dictionary. - * @return array the key list + * Returns the cookie with the specified name. + * @param string $name the cookie name + * @return Cookie the cookie with the specified name. Null if the named cookie does not exist. + * @see getValue() */ - public function getNames() + public function get($name) { - return array_keys($this->_cookies); + return isset($this->_cookies[$name]) ? $this->_cookies[$name] : null; } /** - * Returns the item with the specified key. - * @param mixed $name the key - * @return Cookie the element with the specified key. - * Null if the key cannot be found in the dictionary. + * Returns the value of the named cookie. + * @param string $name the cookie name + * @param mixed $defaultValue the value that should be returned when the named cookie does not exist. + * @return mixed the value of the named cookie. + * @see get() */ - public function getCookie($name) + public function getValue($name, $defaultValue) { - return isset($this->_cookies[$name]) ? $this->_cookies[$name] : null; + return isset($this->_cookies[$name]) ? $this->_cookies[$name]->value : $defaultValue; } /** - * Adds an item into the dictionary. - * Note, if the specified key already exists, the old value will be overwritten. - * @param Cookie $cookie value - * @throws Exception if the dictionary is read-only + * Adds a cookie to the collection. + * If there is already a cookie with the same name in the collection, it will be removed first. + * @param Cookie $cookie the cookie to be added */ - public function add(Cookie $cookie) + public function add($cookie) { if (isset($this->_cookies[$cookie->name])) { - $this->remove($this->_cookies[$cookie->name]); + $c = $this->_cookies[$cookie->name]; + setcookie($c->name, '', 0, $c->path, $c->domain, $c->secure, $c->httpOnly); } setcookie($cookie->name, $cookie->value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly); $this->_cookies[$cookie->name] = $cookie; } /** - * Removes an item from the dictionary by its key. - * @param mixed $key the key of the item to be removed - * @return mixed the removed value, null if no such key exists. - * @throws Exception if the dictionary is read-only + * Removes a cookie from the collection. + * @param Cookie|string $cookie the cookie object or the name of the cookie to be removed. */ - public function remove(Cookie $cookie) + public function remove($cookie) { - setcookie($cookie->name, '', 0, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly); - unset($this->_cookies[$cookie->name]); + if (is_string($cookie) && isset($this->_cookies[$cookie])) { + $cookie = $this->_cookies[$cookie]; + } + if ($cookie instanceof Cookie) { + setcookie($cookie->name, '', 0, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly); + unset($this->_cookies[$cookie->name]); + } } /** - * Removes all items from the dictionary. - * @param boolean $safeClear whether to clear every item by calling [[remove]]. - * Defaults to false, meaning all items in the dictionary will be cleared directly - * without calling [[remove]]. + * Removes all cookies. */ - public function clear($safeClear = false) + public function removeAll() { - if ($safeClear) { - foreach (array_keys($this->_cookies) as $key) { - $this->remove($key); - } - } else { - $this->_cookies = array(); + foreach ($this->_cookies as $cookie) { + setcookie($cookie->name, '', 0, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly); } + $this->_cookies = array(); } /** - * Returns the dictionary as a PHP array. - * @return array the list of items in array + * Returns the collection as a PHP array. + * @return array the array representation of the collection. + * The array keys are cookie names, and the array values are the corresponding + * cookie objects. */ public function toArray() { @@ -161,78 +144,52 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ } /** - * Returns whether there is an element at the specified offset. + * Returns whether there is a cookie with the specified name. * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `isset($dictionary[$offset])`. - * This is equivalent to [[contains]]. - * @param mixed $offset the offset to check on - * @return boolean + * It is implicitly called when you use something like `isset($collection[$name])`. + * @param string $name the cookie name + * @return boolean whether the named cookie exists */ - public function offsetExists($offset) + public function offsetExists($name) { - return isset($this->_cookies[$offset]); + return isset($this->_cookies[$name]); } /** - * Returns the element at the specified offset. + * Returns the cookie with the specified name. * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$value = $dictionary[$offset];`. - * This is equivalent to [[itemAt]]. - * @param mixed $offset the offset to retrieve element. - * @return mixed the element at the offset, null if no element is found at the offset + * It is implicitly called when you use something like `$cookie = $collection[$name];`. + * This is equivalent to [[get()]]. + * @param string $name the cookie name + * @return Cookie the cookie with the specified name, null if the named cookie does not exist. */ - public function offsetGet($offset) + public function offsetGet($name) { - return $this->getCookie($offset); + return $this->get($name); } /** - * Sets the element at the specified offset. + * Adds the cookie to the collection. * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$dictionary[$offset] = $item;`. - * If the offset is null, the new item will be appended to the dictionary. - * Otherwise, the existing item at the offset will be replaced with the new item. - * This is equivalent to [[add]]. - * @param mixed $offset the offset to set element - * @param mixed $item the element value + * It is implicitly called when you use something like `$collection[$name] = $cookie;`. + * This is equivalent to [[add()]]. + * @param string $name the cookie name + * @param Cookie $cookie the cookie to be added */ - public function offsetSet($offset, $item) + public function offsetSet($name, $cookie) { - $this->add($item); + $this->add($cookie); } /** - * Unsets the element at the specified offset. + * Removes the named cookie. * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `unset($dictionary[$offset])`. - * This is equivalent to [[remove]]. - * @param mixed $offset the offset to unset element + * It is implicitly called when you use something like `unset($collection[$name])`. + * This is equivalent to [[remove()]]. + * @param string $name the cookie name */ - public function offsetUnset($offset) + public function offsetUnset($name) { - if (isset($this->_cookies[$offset])) { - $this->remove($this->_cookies[$offset]); - } - } - - /** - * @return array list of validated cookies - */ - protected function loadCookies($data) - { - $cookies = array(); - if ($this->_request->enableCookieValidation) { - $sm = Yii::app()->getSecurityManager(); - foreach ($_COOKIE as $name => $value) { - if (is_string($value) && ($value = $sm->validateData($value)) !== false) { - $cookies[$name] = new CHttpCookie($name, @unserialize($value)); - } - } - } else { - foreach ($_COOKIE as $name => $value) { - $cookies[$name] = new CHttpCookie($name, $value); - } - } - return $cookies; + $this->remove($name); } } diff --git a/framework/web/Request.php b/framework/web/Request.php index 9592d22..8c7d7b3 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -695,21 +695,53 @@ class Request extends \yii\base\Request return isset($languages[0]) ? $languages[0] : false; } - /** * Returns the cookie collection. - * The result can be used like an associative array. Adding {@link CHttpCookie} objects - * to the collection will send the cookies to the client; and removing the objects - * from the collection will delete those cookies on the client. - * @return CCookieCollection the cookie collection. + * Through the returned cookie collection, you may access a cookie using the following syntax: + * + * ~~~ + * $cookie = $request->cookies['name'] + * if ($cookie !== null) { + * $value = $cookie->value; + * } + * + * // alternatively + * $value = $request->cookies->getValue('name'); + * ~~~ + * + * @return CookieCollection the cookie collection. */ public function getCookies() { - if ($this->_cookies !== null) { - return $this->_cookies; + if ($this->_cookies === null) { + $this->_cookies = new CookieCollection($this->loadCookies()); + } + return $this->_cookies; + } + + /** + * Returns the current cookies in terms of [[Cookie]] objects. + * @return Cookie[] list of current cookies + */ + protected function loadCookies() + { + $cookies = array(); + if ($this->enableCookieValidation) { + $sm = Yii::app()->getSecurityManager(); + foreach ($_COOKIE as $name => $value) { + if (is_string($value) && ($value = $sm->validateData($value)) !== false) { + $cookies[$name] = new CHttpCookie($name, @unserialize($value)); + } + } } else { - return $this->_cookies = new CCookieCollection($this); + foreach ($_COOKIE as $name => $value) { + $cookies[$name] = new Cookie(array( + 'name' => $name, + 'value' => $value, + )); + } } + return $cookies; } private $_csrfToken; diff --git a/framework/web/Response.php b/framework/web/Response.php index 73a28e3..99c4865 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -161,4 +161,28 @@ class Response extends \yii\base\Response Yii::app()->end(); } } + + /** + * Returns the cookie collection. + * Through the returned cookie collection, you add or remove cookies as follows, + * + * ~~~ + * // add a cookie + * $response->cookies->add(new Cookie(array( + * 'name' => $name, + * 'value' => $value, + * )); + * + * // remove a cookie + * $response->cookies->remove('name'); + * // alternatively + * unset($response->cookies['name']); + * ~~~ + * + * @return CookieCollection the cookie collection. + */ + public function getCookies() + { + return \Yii::$app->getRequest()->getCookies(); + } }