diff --git a/demo/bootstrap.php b/demo/bootstrap.php
index 21bdb42..cbc9cdc 100644
--- a/demo/bootstrap.php
+++ b/demo/bootstrap.php
@@ -2,6 +2,9 @@
include __DIR__ . '/../tests/bootstrap.php';
+// for stack data
+session_start();
+
use DebugBar\StandardDebugBar;
$debugbar = new StandardDebugBar();
diff --git a/demo/index.php b/demo/index.php
index 75f3bc8..71c574f 100644
--- a/demo/index.php
+++ b/demo/index.php
@@ -25,6 +25,10 @@ render_demo_page(function() {
load ajax content
load ajax content with exception
+Stack
+
PDO
- PDO demo
diff --git a/demo/stack.php b/demo/stack.php
new file mode 100644
index 0000000..4f61979
--- /dev/null
+++ b/demo/stack.php
@@ -0,0 +1,6 @@
+addMessage('hello from redirect');
+$debugbar->stackData();
+header('Location: index.php');
\ No newline at end of file
diff --git a/docs/ajax.md b/docs/ajax.md
deleted file mode 100644
index 9d80aa1..0000000
--- a/docs/ajax.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# AJAX
-
-As mentioned in the previous chapter, if you are performing AJAX requests
-which return HTML content, you can use `JavascriptRenderer::render(false)`.
-
-In the case you are sending back non-HTML data (eg: JSON), the DebugBar can
-send data to the client using HTTP headers using the `sendDataInHeaders()` method
-(no need to use the `JavascriptRenderer`):
-
- $debugbar = new DebugBar();
- // ...
- $debugbar->sendDataInHeaders();
-
-On the client side, an instance of `PhpDebugBar.AjaxHandler` will
-parse the headers and add the dataset to the debugbar.
-
-The AjaxHandler automatically binds to jQuery's *ajaxComplete* event
-so if you are using jQuery, you have nothing to configure.
-
-If you're not using jQuery, you can call `AjaxHandler.handle(xhr)`.
-If you are using the `JavascriptRenderer` initialization, the instance
-of `AjaxHandler` is stored in the `ajaxHandler` property of the `DebugBar` object.
-
- debugbar.ajaxHandler.handle(xhr);
\ No newline at end of file
diff --git a/docs/ajax_and_stack.md b/docs/ajax_and_stack.md
new file mode 100644
index 0000000..a1cac4a
--- /dev/null
+++ b/docs/ajax_and_stack.md
@@ -0,0 +1,49 @@
+# AJAX and Stacked data
+
+## AJAX
+
+As mentioned in the previous chapter, if you are performing AJAX requests
+which return HTML content, you can use `JavascriptRenderer::render(false)`.
+
+In the case you are sending back non-HTML data (eg: JSON), the DebugBar can
+send data to the client using HTTP headers using the `sendDataInHeaders()` method
+(no need to use the `JavascriptRenderer`):
+
+ $debugbar = new DebugBar();
+ // ...
+ $debugbar->sendDataInHeaders();
+
+On the client side, an instance of `PhpDebugBar.AjaxHandler` will
+parse the headers and add the dataset to the debugbar.
+
+The AjaxHandler automatically binds to jQuery's *ajaxComplete* event
+so if you are using jQuery, you have nothing to configure.
+
+If you're not using jQuery, you can call `AjaxHandler.handle(xhr)`.
+If you are using the `JavascriptRenderer` initialization, the instance
+of `AjaxHandler` is stored in the `ajaxHandler` property of the `DebugBar` object.
+
+ debugbar.ajaxHandler.handle(xhr);
+
+## Stacked data
+
+Some times you need to collect data about a request but the page won't actually
+be displayed. The best example of that is during a redirect. You can use the
+debug bar storage mechanism to store the data and re-open it later but it can
+be cumbersome while testing a redirect page.
+
+The solution is to use stacked data. The debug bar can temporarily store the
+collected data in the session until the next time it will be displayed.
+Simply call `DebugBar::stackData()` instead of rendering the debug bar.
+
+PHP's session must be started before using this feature.
+
+Note: The stacked data feature will use the storage mechanism of it's enabled
+instead of storing the data in the session.
+
+ $debugbar = new DebugBar();
+ // ...
+ $debugbar->stackData();
+
+Stacked data are rendered each time the debug bar is rendered using the
+`JavascriptRenderer`.
\ No newline at end of file
diff --git a/docs/manifest.json b/docs/manifest.json
index 5603147..1a04f90 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -6,7 +6,7 @@
"../README.md",
"data_collectors.md",
"rendering.md",
- "ajax.md",
+ "ajax_and_stack.md",
"base_collectors.md",
"bridge_collectors.md",
"storage.md",
diff --git a/src/DebugBar/DebugBar.php b/src/DebugBar/DebugBar.php
index d7dc9f1..8b3c36f 100644
--- a/src/DebugBar/DebugBar.php
+++ b/src/DebugBar/DebugBar.php
@@ -40,6 +40,10 @@ class DebugBar implements ArrayAccess
protected $storage;
+ protected $stackSessionNamespace = 'PHPDEBUGBAR_STACK_DATA';
+
+ protected $stackAlwaysUseSessionStorage = false;
+
/**
* Adds a data collector
*
@@ -230,6 +234,120 @@ class DebugBar implements ArrayAccess
return $this;
}
+ /**
+ * Stacks the data in the session for later rendering
+ */
+ public function stackData()
+ {
+ $this->initStackSession();
+
+ $data = null;
+ if (!$this->isDataPersisted() || $this->stackAlwaysUseSessionStorage) {
+ $data = $this->getData();
+ } else if ($this->data === null) {
+ $this->collect();
+ }
+
+ $_SESSION[$this->stackSessionNamespace][$this->getCurrentRequestId()] = $data;
+ return $this;
+ }
+
+ /**
+ * Checks if there is stacked data in the session
+ *
+ * @return boolean
+ */
+ public function hasStackedData()
+ {
+ $this->initStackSession();
+ return count($_SESSION[$this->stackSessionNamespace]) > 0;
+ }
+
+ /**
+ * Returns the data stacked in the session
+ *
+ * @param boolean $delete Whether to delete the data in the session
+ * @return array
+ */
+ public function getStackedData($delete = true)
+ {
+ $this->initStackSession();
+ $stackedData = $_SESSION[$this->stackSessionNamespace];
+ if ($delete) {
+ unset($_SESSION[$this->stackSessionNamespace]);
+ }
+
+ $datasets = array();
+ if ($this->isDataPersisted() && !$this->stackAlwaysUseSessionStorage) {
+ foreach ($stackedData as $id => $data) {
+ $datasets[$id] = $this->getStorage()->get($id);
+ }
+ } else {
+ $datasets = $stackedData;
+ }
+
+ return $datasets;
+ }
+
+ /**
+ * Sets the key to use in the $_SESSION array
+ *
+ * @param string $ns
+ */
+ public function setStackDataSessionNamespace($ns)
+ {
+ $this->stackSessionNamespace = $ns;
+ return $this;
+ }
+
+ /**
+ * Returns the key used in the $_SESSION array
+ *
+ * @return string
+ */
+ public function getStackDataSessionNamespace()
+ {
+ return $this->stackSessionNamespace;
+ }
+
+ /**
+ * Sets whether to only use the session to store stacked data even
+ * if a storage is enabled
+ *
+ * @param boolean $enabled
+ */
+ public function setStackAlwaysUseSessionStorage($enabled = true)
+ {
+ $this->stackAlwaysUseSessionStorage = $enabled;
+ return $this;
+ }
+
+ /**
+ * Checks if the session is always used to store stacked data
+ * even if a storage is enabled
+ *
+ * @return boolean
+ */
+ public function isStackAlwaysUseSessionStorage()
+ {
+ return $this->stackAlwaysUseSessionStorage;
+ }
+
+ /**
+ * Initializes the session for stacked data
+ */
+ protected function initStackSession()
+ {
+ if ((function_exists('session_status') && session_status() == PHP_SESSION_NONE) || !isset($_SESSION)) {
+ throw new DebugBarException("Session must be started before using stack data in the debug bar");
+ }
+
+ $ns = $this->stackSessionNamespace;
+ if (!isset($_SESSION[$ns])) {
+ $_SESSION[$ns] = array();
+ }
+ }
+
/**
* Returns a JavascriptRenderer for this instance
*
diff --git a/src/DebugBar/JavascriptRenderer.php b/src/DebugBar/JavascriptRenderer.php
index e34f1b7..55b1dd4 100644
--- a/src/DebugBar/JavascriptRenderer.php
+++ b/src/DebugBar/JavascriptRenderer.php
@@ -614,18 +614,22 @@ class JavascriptRenderer
* @param boolean $initialize Whether to render the de bug bar initialization code
* @return string
*/
- public function render($initialize = true)
+ public function render($initialize = true, $renderStackedData = true)
{
$js = '';
if ($initialize) {
$js = $this->getJsInitializationCode();
}
+
+ if ($renderStackedData && $this->debugBar->hasStackedData()) {
+ foreach ($this->debugBar->getStackedData() as $id => $data) {
+ $js .= $this->getAddDatasetCode($id, $data);
+ }
+ }
- $js .= sprintf("%s.addDataSet(%s, \"%s\");\n",
- $this->variableName,
- json_encode($this->debugBar->getData()),
- $this->debugBar->getCurrentRequestId());
+ $js .= $this->getAddDatasetCode($this->debugBar->getCurrentRequestId(), $this->debugBar->getData());
+
return "\n";
}
@@ -728,4 +732,20 @@ class JavascriptRenderer
return $js;
}
+
+ /**
+ * Returns the js code needed to add a dataset
+ *
+ * @param string $requestId
+ * @param array $data
+ * @return string
+ */
+ protected function getAddDatasetCode($requestId, $data)
+ {
+ $js = sprintf("%s.addDataSet(%s, \"%s\");\n",
+ $this->variableName,
+ json_encode($data),
+ $requestId);
+ return $js;
+ }
}
diff --git a/tests/DebugBar/Tests/DebugBarTest.php b/tests/DebugBar/Tests/DebugBarTest.php
index 1230466..b6fc927 100644
--- a/tests/DebugBar/Tests/DebugBarTest.php
+++ b/tests/DebugBar/Tests/DebugBarTest.php
@@ -53,4 +53,38 @@ class DebugBarTest extends DebugBarTestCase
$data = $this->debugbar->collect();
$this->assertEquals($s->data[$this->debugbar->getCurrentRequestId()], $data);
}
+
+ public function testStackedData()
+ {
+ $_SESSION = array();
+ $this->debugbar->addCollector($c = new MockCollector(array('foo')));
+ $this->debugbar->stackData();
+
+ $this->assertArrayHasKey($ns = $this->debugbar->getStackDataSessionNamespace(), $_SESSION);
+ $this->assertArrayHasKey($id = $this->debugbar->getCurrentRequestId(), $_SESSION[$ns]);
+ $this->assertArrayHasKey('mock', $_SESSION[$ns][$id]);
+ $this->assertEquals($c->collect(), $_SESSION[$ns][$id]['mock']);
+ $this->assertTrue($this->debugbar->hasStackedData());
+
+ $data = $this->debugbar->getStackedData();
+ $this->assertArrayNotHasKey($ns, $_SESSION);
+ $this->assertArrayHasKey($id, $data);
+ $this->assertEquals(1, count($data));
+ $this->assertArrayHasKey('mock', $data[$id]);
+ $this->assertEquals($c->collect(), $data[$id]['mock']);
+ }
+
+ public function testStackedDataWithStorage()
+ {
+ $s = new MockStorage();
+ $this->debugbar->setStorage($s);
+ $this->debugbar->addCollector($c = new MockCollector(array('foo')));
+ $this->debugbar->stackData();
+
+ $id = $this->debugbar->getCurrentRequestId();
+ $this->assertNull($_SESSION[$this->debugbar->getStackDataSessionNamespace()][$id]);
+
+ $data = $this->debugbar->getStackedData();
+ $this->assertEquals($c->collect(), $data[$id]['mock']);
+ }
}
\ No newline at end of file