[HttpFoundation] Added FlashBagInterface and concrete implementation.

This commit outsources the flash message processing to it's own interface.

Overall flash messages now can have multiple flash types and each type can
store multiple messages.  For convenience there are now four flash types
by default, INFO, NOTICE, WARNING and ERROR.

There are two concrete implementations: one preserving the old behaviour of
flash messages expiring exactly after one page load, regardless of being
displayed or not; and the other where flash messages persist until explicitly
popped.
This commit is contained in:
Drak 2011-11-24 10:52:08 +05:45
parent 39288bcdaa
commit c9694237d2
5 changed files with 746 additions and 0 deletions

View File

@ -0,0 +1,168 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
/**
* AutoExpireFlashBag flash message container.
*
* @author Drak <drak@zikula.org>
*/
class AutoExpireFlashBag implements FlashBagInterface
{
/**
* Flash messages.
*
* @var array
*/
private $flashes = array();
/**
* The storage key for flashes in the session
*
* @var string
*/
private $storageKey;
/**
* Constructor.
*
* @param type $storageKey The key used to store flashes in the session.
*/
public function __construct($storageKey = '_sf2_flashes')
{
$this->storageKey = $storageKey;
$this->flashes = array('display' => array(), 'new' => array());
}
/**
* {@inheritdoc}
*/
public function initialize(array &$flashes)
{
$this->flashes = &$flashes;
// The logic: messages from the last request will be stored in new, so we move them to previous
// This request we will show what is in 'display'. What is placed into 'new' this time round will
// be moved to display next time round.
$this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
$this->flashes['new'] = array();
}
/**
* {@inheritdoc}
*/
public function add($message, $type = self::NOTICE)
{
$this->flashes['new'][$type][] = $message;
}
/**
* {@inheritdoc}
*/
public function get($type)
{
if (!$this->has($type)) {
return array();
}
return $this->flashes['display'][$type];
}
/**
* {@inheritdoc}
*/
public function pop($type)
{
if (!$this->has($type)) {
return array();
}
return $this->clear($type);
}
/**
* {@inheritdoc}
*/
public function popAll()
{
return $this->clearAll();
}
/**
* {@inheritdoc}
*/
public function set($type, array $array)
{
$this->flashes['new'][$type] = $array;
}
/**
* {@inheritdoc}
*/
public function has($type)
{
return array_key_exists($type, $this->flashes['display']);
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_keys($this->flashes['display']);
}
/**
* {@inheritdoc}
*/
public function all()
{
return array_key_exists('display', $this->flashes) ? (array)$this->flashes['display'] : array();
}
/**
* {@inheritdoc}
*/
public function clear($type)
{
$return = array();
if (isset($this->flashes['new'][$type])) {
unset($this->flashes['new'][$type]);
}
if (isset($this->flashes['display'][$type])) {
$return = $this->flashes['display'][$type];
unset($this->flashes['display'][$type]);
}
return $return;
}
/**
* {@inheritdoc}
*/
public function clearAll()
{
$return = $this->flashes['display'];
$this->flashes = array('new' => array(), 'display' => array());
return $return;
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
}

View File

@ -0,0 +1,157 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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.
*
* @author Drak <drak@zikula.org>
*/
class FlashBag implements FlashBagInterface
{
/**
* Flash messages.
*
* @var array
*/
private $flashes = array();
/**
* The storage key for flashes in the session
*
* @var string
*/
private $storageKey;
/**
* Constructor.
*
* @param type $storageKey The key used to store flashes in the session.
*/
public function __construct($storageKey = '_sf2_flashes')
{
$this->storageKey = $storageKey;
}
/**
* {@inheritdoc}
*/
public function initialize(array &$flashes)
{
$this->flashes = &$flashes;
}
/**
* {@inheritdoc}
*/
public function add($message, $type = self::NOTICE)
{
$this->flashes[$type][] = $message;
}
/**
* {@inheritdoc}
*/
public function get($type)
{
if (!$this->has($type)) {
return array();
}
return $this->flashes[$type];
}
/**
* {@inheritdoc}
*/
public function pop($type)
{
if (!$this->has($type)) {
return array();
}
return $this->clear($type);
}
/**
* {@inheritdoc}
*/
public function popAll()
{
return $this->clearAll();
}
/**
* {@inheritdoc}
*/
public function set($type, array $array)
{
$this->flashes[$type] = $array;
}
/**
* {@inheritdoc}
*/
public function has($type)
{
return array_key_exists($type, $this->flashes);
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_keys($this->flashes);
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->flashes;
}
/**
* {@inheritdoc}
*/
public function clear($type)
{
$return = array();
if (isset($this->flashes[$type])) {
$return = $this->flashes[$type];
unset($this->flashes[$type]);
}
return $return;
}
/**
* {@inheritdoc}
*/
public function clearAll()
{
$return = $this->flashes;
$this->flashes = array();
return $return;
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
}

View File

@ -0,0 +1,121 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <drak@zikula.org>
*/
interface FlashBagInterface
{
const INFO = 'info';
const NOTICE = 'notice';
const WARNING = 'warning';
const ERROR = 'error';
/**
* Initializes the FlashBag.
*
* @param array &$flashes
*/
function initialize(array &$flashes);
/**
* Adds a flash to the stack for a given type.
*
* @param string $message
* @param string $type
*/
function add($message, $type = self::NOTICE);
/**
* Gets flash messages for a given type.
*
* @param string $type Message category type.
*
* @return array
*/
function get($type);
/**
* Pops and clears flashes from the stack.
*
* @param string $type
*
* @return array
*/
function pop($type);
/**
* Pops all flashes from the stacl and clears flashes.
*
* @param string $type
*
* @return array Empty array, or indexed array of arrays.
*/
function popAll();
/**
* Sets an array of flash messages for a given type.
*
* @param string $type
* @param array $array
*/
function set($type, array $array);
/**
* Has flash messages for a given type?
*
* @param string $type
*
* @return boolean
*/
function has($type);
/**
* Returns a list of all defined types.
*
* @return array
*/
function keys();
/**
* Gets all flash messages.
*
* @return array
*/
function all();
/**
* Clears flash messages for a given type.
*
* @param string $type
*
* @return array Returns an array of what was just cleared.
*/
function clear($type);
/**
* Clears all flash messages.
*
* @return array Empty array or indexed arrays or array if none.
*/
function clearAll();
/**
* Gets the storage key for this bag.
*
* @return string
*/
function getStorageKey();
}

View File

@ -0,0 +1,152 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\AutoExpireFlashBag as FlashBag;
use Symfony\Component\HttpFoundation\FlashBagInterface;
/**
* AutoExpireFlashBagTest
*
* @author Drak <drak@zikula.org>
*/
class AutoExpireFlashBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\FlashBagInterface
*/
private $bag;
/**
* @var array
*/
protected $array = array();
public function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array('new' => array(FlashBag::NOTICE => array('A previous flash message')));
$this->bag->initialize($this->array);
}
public function tearDown()
{
$this->bag = null;
parent::tearDown();
}
public function testInitialize()
{
$bag = new FlashBag();
$array = array('new' => array(FlashBag::NOTICE => array('A previous flash message')));
$bag->initialize($array);
$this->assertEquals(array('A previous flash message'), $bag->get(FlashBag::NOTICE));
$array = array('new' => array(
FlashBag::NOTICE => array('Something else'),
FlashBag::ERROR => array('a', 'b'),
));
$bag->initialize($array);
$this->assertEquals(array('Something else'), $bag->get(FlashBag::NOTICE));
$this->assertEquals(array('a', 'b'), $bag->get(FlashBag::ERROR));
}
public function testAdd()
{
$this->bag->add('Something new', FlashBag::NOTICE);
$this->bag->add('Smile, it might work next time', FlashBag::ERROR);
$this->assertEquals(array('A previous flash message'), $this->bag->get(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->get(FlashBag::ERROR));
}
public function testGet()
{
$this->assertEquals(array('A previous flash message'), $this->bag->get(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->get('non_existing_type'));
}
public function testSet()
{
$this->bag->set(FlashBag::NOTICE, array('Foo', 'Bar'));
$this->assertNotEquals(array('Foo', 'Bar'), $this->bag->get(FlashBag::NOTICE));
}
public function testHas()
{
$this->assertFalse($this->bag->has('nothing'));
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
}
public function testKeys()
{
$this->assertEquals(array(FlashBag::NOTICE), $this->bag->keys());
}
public function testAll()
{
$array = array(
'new' => array(
FlashBag::NOTICE => array('Foo'),
FlashBag::ERROR => array('Bar'),
),
);
$this->bag->initialize($array);
$this->assertEquals(array(
FlashBag::NOTICE => array('Foo'),
FlashBag::ERROR => array('Bar'),
), $this->bag->all()
);
}
public function testPop()
{
$this->assertEquals(array('A previous flash message'), $this->bag->pop(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->pop(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->pop('non_existing_type'));
}
public function testPopAll()
{
$this->bag->set(FlashBag::NOTICE, array('Foo'));
$this->bag->set(FlashBag::ERROR, array('Bar'));
$this->assertEquals(array(
FlashBag::NOTICE => array('A previous flash message'),
), $this->bag->popAll()
);
$this->assertEquals(array(), $this->bag->popAll());
}
public function testClear()
{
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
$this->assertEquals(array('A previous flash message'), $this->bag->clear(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->clear(FlashBag::NOTICE));
$this->assertFalse($this->bag->has(FlashBag::NOTICE));
}
public function testClearAll()
{
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
$this->bag->add('Smile, it might work next time', FlashBag::ERROR);
$this->assertFalse($this->bag->has(FlashBag::ERROR));
$this->assertEquals(array(
FlashBag::NOTICE => array('A previous flash message'),
), $this->bag->clearAll()
);
$this->assertEquals(array(), $this->bag->clearAll());
$this->assertFalse($this->bag->has(FlashBag::NOTICE));
$this->assertFalse($this->bag->has(FlashBag::ERROR));
}
}

View File

@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <drak@zikula.org>
*/
class FlashBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\FlashBagInterface
*/
private $bag;
/**
* @var array
*/
protected $array = array();
public function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array(FlashBag::NOTICE => array('A previous flash message'));
$this->bag->initialize($this->array);
}
public function tearDown()
{
$this->bag = null;
parent::tearDown();
}
public function testInitialize()
{
$bag = new FlashBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $bag->all());
$array = array('should' => array('change'));
$bag->initialize($array);
$this->assertEquals($array, $bag->all());
}
public function testAdd()
{
$this->bag->add('Something new', FlashBag::NOTICE);
$this->bag->add('Smile, it might work next time', FlashBag::ERROR);
$this->assertEquals(array('A previous flash message', 'Something new'), $this->bag->get(FlashBag::NOTICE));
$this->assertEquals(array('Smile, it might work next time'), $this->bag->get(FlashBag::ERROR));
}
public function testGet()
{
$this->assertEquals(array('A previous flash message'), $this->bag->get(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->get('non_existing_type'));
}
public function testPop()
{
$this->assertEquals(array('A previous flash message'), $this->bag->pop(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->pop(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->pop('non_existing_type'));
}
public function testPopAll()
{
$this->bag->set(FlashBag::NOTICE, array('Foo'));
$this->bag->set(FlashBag::ERROR, array('Bar'));
$this->assertEquals(array(
FlashBag::NOTICE => array('Foo'),
FlashBag::ERROR => array('Bar')), $this->bag->popAll()
);
$this->assertEquals(array(), $this->bag->popAll());
}
public function testSet()
{
$this->bag->set(FlashBag::NOTICE, array('Foo', 'Bar'));
$this->assertEquals(array('Foo', 'Bar'), $this->bag->get(FlashBag::NOTICE));
}
public function testHas()
{
$this->assertFalse($this->bag->has('nothing'));
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
}
public function testKeys()
{
$this->assertEquals(array(FlashBag::NOTICE), $this->bag->keys());
}
public function testAll()
{
$this->bag->set(FlashBag::NOTICE, array('Foo'));
$this->bag->set(FlashBag::ERROR, array('Bar'));
$this->assertEquals(array(
FlashBag::NOTICE => array('Foo'),
FlashBag::ERROR => array('Bar')), $this->bag->all()
);
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
$this->assertTrue($this->bag->has(FlashBag::ERROR));
$this->assertEquals(array(
FlashBag::NOTICE => array('Foo'),
FlashBag::ERROR => array('Bar'),
), $this->bag->all()
);
}
public function testClear()
{
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
$this->assertEquals(array('A previous flash message'), $this->bag->clear(FlashBag::NOTICE));
$this->assertEquals(array(), $this->bag->clear(FlashBag::NOTICE));
$this->assertFalse($this->bag->has(FlashBag::NOTICE));
}
public function testClearAll()
{
$this->assertTrue($this->bag->has(FlashBag::NOTICE));
$this->bag->add('Smile, it might work next time', FlashBag::ERROR);
$this->assertTrue($this->bag->has(FlashBag::ERROR));
$this->assertEquals(array(
FlashBag::NOTICE => array('A previous flash message'),
FlashBag::ERROR => array('Smile, it might work next time'),
), $this->bag->clearAll()
);
$this->assertEquals(array(), $this->bag->clearAll());
$this->assertFalse($this->bag->has(FlashBag::NOTICE));
$this->assertFalse($this->bag->has(FlashBag::ERROR));
}
}