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: + * + * + * 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