diff --git a/qtism/runtime/tests/AssessmentItemSession.php b/qtism/runtime/tests/AssessmentItemSession.php
index 37d2b4e34..1b284246c 100644
--- a/qtism/runtime/tests/AssessmentItemSession.php
+++ b/qtism/runtime/tests/AssessmentItemSession.php
@@ -234,6 +234,12 @@ class AssessmentItemSession extends State {
* @var AbstractSessionManager
*/
private $sessionManager;
+
+ /**
+ * List of callback functions
+ * @var array
+ */
+ private $callbacks = array();
/**
* Create a new AssessmentItemSession object.
@@ -586,6 +592,7 @@ public function beginAttempt() {
// Register a time reference that will be used later on to compute the duration built-in variable.
$this->timeReference = new DateTime('now', new DateTimeZone('UTC'));
+ $this->runCallback('beginAttempt');
}
/**
@@ -741,6 +748,7 @@ public function endAttempt(State $responses = null, $responseProcessing = true,
// Wait for the next attempt.
$this->attempting = false;
+ $this->runCallback('endAttempt');
}
/**
@@ -760,6 +768,7 @@ public function suspend() {
$this->updateDuration();
$this->state = AssessmentItemSessionState::SUSPENDED;
+ $this->runCallback('suspend');
}
}
@@ -783,6 +792,7 @@ public function interact() {
// Reset the time reference. If not, the time spent in SUSPENDED mode will be taken into account!
$this->setTimeReference(new DateTime('now', new DateTimeZone('UTC')));
+ $this->runCallback('interact');
}
}
@@ -1114,6 +1124,49 @@ public function onDurationUpdate(array $callback) {
$this->onDurationUpdate[] = $callback;
}
+ /**
+ * Register callback function which will be invoked after method
+ * specified in the $eventName parameter is called.
+ * Events available for callback registration:
+ *
+ * - beginAttempt
+ * - endAttempt
+ * - suspend
+ * - interact
+ *
+ *
+ * Note that first parameter passed to the callback function always will be instance of current class,
+ * and the remaining parameters will be taken from the $params array.
+ *
+ * @param string $eventName name of method of current class after which callback function will be invoked.
+ * @param array $callback The function or method to be called.
+ * This parameter may be an array, with the name of the class, and the method, or a string, with a function name.
+ * @param array $params Parameters to be passed to the callback, as an indexed array.
+ */
+ public function registerCallback($eventName, $callback, $params = array())
+ {
+ $this->callbacks[$eventName][] = array(
+ 'callback' => $callback,
+ 'params' => $params
+ );
+ }
+
+ /**
+ * Call callback functions registered for method specified in $eventName parameter.
+ * $this variable will be passed to the callback function as first parameter.
+ *
+ * @param string $eventName
+ */
+ protected function runCallback($eventName)
+ {
+ if (isset($this->callbacks[$eventName])) {
+ foreach($this->callbacks[$eventName] as $callback) {
+ array_unshift($callback['params'], $this);
+ call_user_func_array($callback['callback'], $callback['params']);
+ }
+ }
+ }
+
public function __clone() {
$newData = array();
$oldData = $this->getDataPlaceHolder();
diff --git a/test/qtism/runtime/tests/AssessmentItemSessionTest.php b/test/qtism/runtime/tests/AssessmentItemSessionTest.php
index 158ca426a..6287fda59 100644
--- a/test/qtism/runtime/tests/AssessmentItemSessionTest.php
+++ b/test/qtism/runtime/tests/AssessmentItemSessionTest.php
@@ -346,4 +346,42 @@ public function testSimultaneousSubmissionOnlyOneAttempt() {
$this->assertEquals(AssessmentItemSessionException::STATE_VIOLATION, $e->getCode());
}
}
+
+ public function testRunCallback() {
+ $itemSession = self::instantiateBasicAssessmentItemSession();
+ $itemSession->beginItemSession();
+
+ $itemSession->registerCallback('beginAttempt', function ($item, $itemSessionTest, $itemSession) {
+ $itemSessionTest->assertEquals($item, $itemSession);
+ $itemSessionTest->assertEquals($item->getState(), AssessmentItemSessionState::INTERACTING);
+ },
+ array($this, $itemSession)
+ );
+
+ $itemSession->registerCallback('suspend', function ($item, $itemSessionTest, $itemSession) {
+ $itemSessionTest->assertEquals($item, $itemSession);
+ $itemSessionTest->assertEquals($item->getState(), AssessmentItemSessionState::SUSPENDED);
+ },
+ array($this, $itemSession)
+ );
+
+ $itemSession->registerCallback('interact', function ($item, $itemSessionTest, $itemSession) {
+ $itemSessionTest->assertEquals($item, $itemSession);
+ $itemSessionTest->assertEquals($item->getState(), AssessmentItemSessionState::INTERACTING);
+ },
+ array($this, $itemSession)
+ );
+
+ $itemSession->registerCallback('endAttempt', function ($item, $itemSessionTest, $itemSession) {
+ $itemSessionTest->assertEquals($item, $itemSession);
+ $itemSessionTest->assertEquals($item->getState(), AssessmentItemSessionState::CLOSED);
+ },
+ array($this, $itemSession)
+ );
+
+ $itemSession->beginAttempt();
+ $itemSession->suspend();
+ $itemSession->interact();
+ $itemSession->endAttempt();
+ }
}
\ No newline at end of file