diff --git a/.gitignore b/.gitignore index 422f1ce840571..e75f9a112448a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ phpunit.xml autoload.php /vendor/ +/nbproject/private/ \ No newline at end of file diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index 087085e2d0de0..e0c67c1a9dc36 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -88,6 +88,8 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3 * added ResponseHeaderBag::makeDisposition() (implements RFC 6266) * made mimetype to extension conversion configurable + * [BC BREAK] Flashes are now stored as a bucket of messages per $type. Moved flash messages + out of the session class. Must use $session->getFlashBag() to get FlashBag instance. ### HttpKernel @@ -117,4 +119,4 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * added a Size validator * added a SizeLength validator * improved the ImageValidator with min width, max width, min height, and max height constraints - * added support for MIME with wildcard in FileValidator + * added support for MIME with wildcard in FileValidator \ No newline at end of file diff --git a/src/Symfony/Component/HttpFoundation/FlashBag.php b/src/Symfony/Component/HttpFoundation/FlashBag.php new file mode 100644 index 0000000000000..7d4420a4e7047 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/FlashBag.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * FlashBag flash message container. + */ +class FlashBag implements FlashBagInterface +{ + const STATUS = 'status'; + const ERROR = 'error'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * Old flash messages to be purged. + * + * @var array + */ + private $oldFlashes = array(); + + /** + * @var boolean + */ + private $initialized = false; + + /** + * Initializes the FlashBag. + * + * @param array $flashes + */ + public function initialize(array $flashes) + { + if ($this->initialized) { + return; + } + + $this->flashes = $flashes; + $this->oldFlashes = $flashes; + $this->initialized = true; + } + + /** + * Adds a flash to the stack for a given type. + * + * @param string $message Message. + * @param string $type Message category + */ + public function add($message, $type = self::STATUS) + { + $this->flashes[$type][] = $message; + } + + /** + * Gets flashes for a given type. + * + * @return array + */ + public function get($type) + { + if (!$this->has($type)) { + throw new \InvalidArgumentException(sprintf('Specified $type %s does not exist', $type)); + } + + return $this->flashes[$type]; + } + + /** + * Sets an array of flash messages for a given type. + * + * @param string $type + * @param array $array + */ + public function set($type, array $array) + { + $this->flashes[$type] = $array; + } + + /** + * Has messages for a given type? + * + * @return boolean + */ + public function has($type) + { + return array_key_exists($type, $this->flashes); + } + + /** + * Returns a list of all defined types. + * + * @return array + */ + public function getTypes() + { + return array_keys($this->flashes); + } + + /** + * Gets all flashes. + * + * @return array + */ + public function all() + { + return $this->flashes; + } + + /** + * Clears flash messages for a given type. + */ + public function clear($type) + { + if (isset($this->flashes[$type])) { + unset($this->flashes[$type]); + } + + if (isset($this->oldFlashes[$type])) { + unset($this->oldFlashes[$type]); + } + } + + /** + * Clears all flash messages. + */ + public function clearAll() + { + $this->flashes = array(); + $this->oldFlashes = array(); + } + + /** + * Removes flash messages set in a previous request. + */ + public function purgeOldFlashes() + { + foreach ($this->oldFlashes as $type => $flashes) { + $this->flashes[$type] = array_diff($this->flashes[$type], $flashes); + } + } + +} diff --git a/src/Symfony/Component/HttpFoundation/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/FlashBagInterface.php new file mode 100644 index 0000000000000..47d2d2603c52e --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/FlashBagInterface.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * FlashBagInterface. + * + * @author Drak + */ +interface FlashBagInterface +{ + /** + * Initializes the FlashBag. + * + * @param array $flashes + */ + function initialize(array $flashes); + + /** + * Adds a flash to the stack for a given type. + */ + function add($type, $message); + + /** + * Gets flash messages for a given type. + * + * @return array + */ + function get($type); + + /** + * Sets an array of flash messages for a given type. + * + * @param string $type + * @param array $array + */ + function set($type, array $array); + + /** + * Hass flash messages for a given type? + * + * @return boolean + */ + function has($type); + + /** + * Returns a list of all defined types. + * + * @return array + */ + function getTypes(); + + /** + * Gets all flash messages. + * + * @return array + */ + function all(); + + /** + * Clears flash messages for a given type. + */ + function clear($type); + + /** + * Clears all flash messages. + */ + function clearAll(); + + /** + * Removes flash messages set in a previous request. + */ + function purgeOldFlashes(); +} diff --git a/src/Symfony/Component/HttpFoundation/Session.php b/src/Symfony/Component/HttpFoundation/Session.php index 721a6c7240b99..46be05925d060 100644 --- a/src/Symfony/Component/HttpFoundation/Session.php +++ b/src/Symfony/Component/HttpFoundation/Session.php @@ -25,24 +25,43 @@ class Session implements \Serializable protected $storage; protected $started; protected $attributes; - protected $flashes; - protected $oldFlashes; + + /** + * Flash message container. + * + * @var \Symfony\Component\HttpFoundation\FlashBagInterface + */ + protected $flashBag; protected $closed; - + /** * Constructor. * - * @param SessionStorageInterface $storage A SessionStorageInterface instance + * @param SessionStorageInterface $storage A SessionStorageInterface instance. + * @param FlashBagInterface $flashBag A FlashBagInterface instance. */ - public function __construct(SessionStorageInterface $storage) + public function __construct(SessionStorageInterface $storage, FlashBagInterface $flashBag) { $this->storage = $storage; - $this->flashes = array(); - $this->oldFlashes = array(); + $this->flashBag = $flashBag; $this->attributes = array(); $this->started = false; $this->closed = false; } + + /** + * Get the flashbag driver. + * + * @return FlashBagInterface + */ + public function getFlashBag() + { + if (false === $this->started) { + $this->start(); + } + + return $this->flashBag; + } /** * Starts the session storage. @@ -61,10 +80,7 @@ public function start() if (isset($attributes['attributes'])) { $this->attributes = $attributes['attributes']; - $this->flashes = $attributes['flashes']; - - // flag current flash messages to be removed at shutdown - $this->oldFlashes = $this->flashes; + $this->flashBag->initialize($attributes['flashes']); } $this->started = true; @@ -174,7 +190,7 @@ public function clear() } $this->attributes = array(); - $this->flashes = array(); + $this->flashBag->clearAll(); } /** @@ -215,114 +231,18 @@ public function getId() return $this->storage->getId(); } - /** - * Gets the flash messages. - * - * @return array - */ - public function getFlashes() - { - return $this->flashes; - } - - /** - * Sets the flash messages. - * - * @param array $values - */ - public function setFlashes($values) - { - if (false === $this->started) { - $this->start(); - } - - $this->flashes = $values; - $this->oldFlashes = array(); - } - - /** - * Gets a flash message. - * - * @param string $name - * @param string|null $default - * - * @return string - */ - public function getFlash($name, $default = null) - { - return array_key_exists($name, $this->flashes) ? $this->flashes[$name] : $default; - } - - /** - * Sets a flash message. - * - * @param string $name - * @param string $value - */ - public function setFlash($name, $value) - { - if (false === $this->started) { - $this->start(); - } - - $this->flashes[$name] = $value; - unset($this->oldFlashes[$name]); - } - - /** - * Checks whether a flash message exists. - * - * @param string $name - * - * @return Boolean - */ - public function hasFlash($name) - { - if (false === $this->started) { - $this->start(); - } - - return array_key_exists($name, $this->flashes); - } - - /** - * Removes a flash message. - * - * @param string $name - */ - public function removeFlash($name) - { - if (false === $this->started) { - $this->start(); - } - - unset($this->flashes[$name]); - } - - /** - * Removes the flash messages. - */ - public function clearFlashes() - { - if (false === $this->started) { - $this->start(); - } - - $this->flashes = array(); - $this->oldFlashes = array(); - } - public function save() { if (false === $this->started) { $this->start(); } - $this->flashes = array_diff_key($this->flashes, $this->oldFlashes); + // expire old flashes + $this->flashBag->purgeOldFlashes(); $this->storage->write('_symfony2', array( 'attributes' => $this->attributes, - 'flashes' => $this->flashes, + 'flashes' => $this->flashBag->all(), )); } diff --git a/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php new file mode 100644 index 0000000000000..c053e0d007e11 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\FlashBag; +use Symfony\Component\HttpFoundation\FlashBagInterface; + +/** + * FlashBagTest + * + * @author Drak + */ +class FlashBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\FlashBagInterface + */ + private $flashBag; + + public function setUp() + { + parent::setUp(); + $this->flashBag = new FlashBag(); + $this->flashBag->initialize(array('status' => array('A previous flash message'))); + } + + public function tearDown() + { + $this->flashBag = null; + parent::tearDown(); + } + + public function testInitialize() + { + $this->flashBag->initialize(array()); + $this->flashBag->initialize(array()); + } + + /** + * @todo Implement testAdd(). + */ + public function testAdd() + { + $this->flashBag->add('Something new', FlashBag::STATUS); + $this->flashBag->add('Smile, it might work next time', FlashBag::ERROR); + $this->assertEquals(array('A previous flash message', 'Something new'), $this->flashBag->get(FlashBag::STATUS)); + $this->assertEquals(array('Smile, it might work next time'), $this->flashBag->get(FlashBag::ERROR)); + } + + public function testGet() + { + $this->assertEquals(array('A previous flash message'), $this->flashBag->get(FlashBag::STATUS)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetException() + { + $bang = $this->flashBag->get('bang'); + } + + public function testSet() + { + $this->flashBag->set(FlashBag::STATUS, array('Foo', 'Bar')); + $this->assertEquals(array('Foo', 'Bar'), $this->flashBag->get(FlashBag::STATUS)); + } + + public function testHas() + { + $this->assertFalse($this->flashBag->has('nothing')); + $this->assertTrue($this->flashBag->has(FlashBag::STATUS)); + } + + /** + * @todo Implement testGetTypes(). + */ + public function testGetTypes() + { + $this->assertEquals(array(FlashBag::STATUS), $this->flashBag->getTypes()); + } + + public function testAll() + { + // nothing to do here + } + + public function testClear() + { + $this->assertTrue($this->flashBag->has(FlashBag::STATUS)); + $this->flashBag->clear(FlashBag::STATUS); + $this->assertFalse($this->flashBag->has(FlashBag::STATUS)); + } + + public function testClearAll() + { + $this->assertTrue($this->flashBag->has(FlashBag::STATUS)); + $this->flashBag->add('Smile, it might work next time', FlashBag::ERROR); + $this->assertTrue($this->flashBag->has(FlashBag::ERROR)); + $this->flashBag->clearAll(); + $this->assertFalse($this->flashBag->has(FlashBag::STATUS)); + $this->assertFalse($this->flashBag->has(FlashBag::ERROR)); + } + + public function testPurgeOldFlashes() + { + $this->flashBag->add('Foo', FlashBag::STATUS); + $this->flashBag->add('Bar', FlashBag::ERROR); + $this->flashBag->purgeOldFlashes(); + $this->assertEquals(array(1 => 'Foo'), $this->flashBag->get(FlashBag::STATUS)); + } + +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php index 8318101e665d1..b8721bc98d779 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php @@ -12,6 +12,8 @@ namespace Symfony\Tests\Component\HttpFoundation; use Symfony\Component\HttpFoundation\Session; +use Symfony\Component\HttpFoundation\FlashBag; +use Symfony\Component\HttpFoundation\FlashBagInterface; use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage; /** @@ -24,54 +26,29 @@ class SessionTest extends \PHPUnit_Framework_TestCase { protected $storage; protected $session; + + /** + * @var \Symfony\Component\HttpFoundation\FlashBagInterface + */ + protected $flashBag; public function setUp() { $this->storage = new ArraySessionStorage(); + $this->flashBag = new FlashBag(); $this->session = $this->getSession(); } protected function tearDown() { $this->storage = null; + $this->flashBag = null; $this->session = null; } - - public function testFlash() + + public function getFlashBag() { - $this->session->clearFlashes(); - - $this->assertSame(array(), $this->session->getFlashes()); - - $this->assertFalse($this->session->hasFlash('foo')); - - $this->session->setFlash('foo', 'bar'); - - $this->assertTrue($this->session->hasFlash('foo')); - $this->assertSame('bar', $this->session->getFlash('foo')); - - $this->session->removeFlash('foo'); - - $this->assertFalse($this->session->hasFlash('foo')); - - $flashes = array('foo' => 'bar', 'bar' => 'foo'); - - $this->session->setFlashes($flashes); - - $this->assertSame($flashes, $this->session->getFlashes()); - } - - public function testFlashesAreFlushedWhenNeeded() - { - $this->session->setFlash('foo', 'bar'); - $this->session->save(); - - $this->session = $this->getSession(); - $this->assertTrue($this->session->hasFlash('foo')); - $this->session->save(); - - $this->session = $this->getSession(); - $this->assertFalse($this->session->hasFlash('foo')); + $this->assetTrue($this->getFlashBag() instanceof FlashBagInterface); } public function testAll() @@ -109,26 +86,26 @@ public function testAll() public function testMigrateAndInvalidate() { $this->session->set('foo', 'bar'); - $this->session->setFlash('foo', 'bar'); + $this->session->getFlashBag()->set('foo', array('bar')); $this->assertSame('bar', $this->session->get('foo')); - $this->assertSame('bar', $this->session->getFlash('foo')); + $this->assertEquals(array('bar'), $this->session->getFlashBag()->get('foo')); $this->session->migrate(); $this->assertSame('bar', $this->session->get('foo')); - $this->assertSame('bar', $this->session->getFlash('foo')); + $this->assertEquals(array('bar'), $this->session->getFlashBag()->get('foo')); $this->session = $this->getSession(); $this->session->invalidate(); $this->assertSame(array(), $this->session->all()); - $this->assertSame(array(), $this->session->getFlashes()); + $this->assertEquals(array(), $this->session->getFlashBag()->all()); } public function testSerialize() { - $this->session = new Session($this->storage); + $this->session = new Session($this->storage, $this->flashBag); $compare = serialize($this->storage); @@ -145,7 +122,7 @@ public function testSerialize() public function testSave() { $this->storage = new ArraySessionStorage(); - $this->session = new Session($this->storage); + $this->session = new Session($this->storage, $this->flashBag); $this->session->set('foo', 'bar'); $this->session->save(); @@ -167,7 +144,7 @@ public function testStart() { $this->session->start(); - $this->assertSame(array(), $this->session->getFlashes()); + $this->assertSame(array(), $this->session->getFlashBag()->all()); $this->assertSame(array(), $this->session->all()); } @@ -227,6 +204,6 @@ public function testStorageRemove() protected function getSession() { - return new Session($this->storage); + return new Session($this->storage, $this->flashBag); } }