{"id":21447561,"url":"https://github.com/oat-sa/qti-sdk","last_synced_at":"2025-05-15T23:07:36.700Z","repository":{"id":18532156,"uuid":"21732754","full_name":"oat-sa/qti-sdk","owner":"oat-sa","description":"A QTI (Question \u0026 Test Interoperability) Software Development Kit for PHP","archived":false,"fork":false,"pushed_at":"2025-03-19T23:15:15.000Z","size":16923,"stargazers_count":84,"open_issues_count":10,"forks_count":32,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-08T10:33:12.177Z","etag":null,"topics":["assessment","assessment-item","assessment-test","branching","computer-based-assessment","ims-qti","outcome-processing","php","preconditions","qti","qti-20","qti-21","qti-22","qti-sdk","qti-specification","question-test-interoperability","rendering-engine","response-processing","template-engine","test-interoperability"],"latest_commit_sha":null,"homepage":"http://www.taotesting.com","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oat-sa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-07-11T10:39:04.000Z","updated_at":"2025-02-25T20:47:22.000Z","dependencies_parsed_at":"2023-12-16T07:01:16.743Z","dependency_job_id":"714fc5e8-2b43-4f68-b457-9c65da31e2fe","html_url":"https://github.com/oat-sa/qti-sdk","commit_stats":{"total_commits":2298,"total_committers":39,"mean_commits":58.92307692307692,"dds":0.5926892950391645,"last_synced_commit":"d5c0b434a31fcf888fd3831c8fffef58920aac1b"},"previous_names":[],"tags_count":281,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oat-sa%2Fqti-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oat-sa%2Fqti-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oat-sa%2Fqti-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oat-sa%2Fqti-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oat-sa","download_url":"https://codeload.github.com/oat-sa/qti-sdk/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253547279,"owners_count":21925550,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assessment","assessment-item","assessment-test","branching","computer-based-assessment","ims-qti","outcome-processing","php","preconditions","qti","qti-20","qti-21","qti-22","qti-sdk","qti-specification","question-test-interoperability","rendering-engine","response-processing","template-engine","test-interoperability"],"created_at":"2024-11-23T03:10:42.670Z","updated_at":"2025-05-15T23:07:30.556Z","avatar_url":"https://github.com/oat-sa.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://cloud.githubusercontent.com/assets/4217431/6734391/3b3ef4e8-ce58-11e4-83a1-d6e39a91400f.png\" alt=\"QTI-SDK\" /\u003e\u003c/p\u003e\n\n[![Latest Version](https://img.shields.io/github/tag/oat-sa/qti-sdk.svg?style=flat\u0026label=release)](https://github.com/oat-sa/qti-sdk/tags)\n[![Coverage Status](https://codecov.io/gh/oat-sa/qti-sdk/branch/master/graph/badge.svg?token=Z02IN6NSmp)](https://codecov.io/gh/oat-sa/qti-sdk)\n[![License GPL2](http://img.shields.io/badge/licence-gpl2-blue.svg)](http://www.gnu.org/licenses/gpl-2.0.html)\n[![Packagist Downloads](http://img.shields.io/packagist/dt/qtism/qtism.svg)](https://packagist.org/packages/qtism/qtism)\n\n# QTI Software Development Kit for PHP\n\nAn IMS QTI (Question \u0026amp; Test Interoperability) Software Development Kit for PHP 7.0 and higher supporting a wide \nrange of features described by the [IMS QTI specification family](http://www.imsglobal.org/question).\n\n__This implementation of QTI is under constant enhancement. The API of the master branch might change at any time.__\n\n## Features\n\n* Targets QTI 2.0, 2.1 and partially 2.2\n* Complete QTI Information Model\n* Complete QTI Rule Engine Support\n* Custom Operator Hooks through PSR-0/PSR-4\n* [Wilbert Kraan's](http://blogs.cetis.ac.uk/wilbert/2013/11/06/using-standards-to-make-assessment-in-e-textbooks-scalable-engaging-but-robust) / [Steve Lay's](http://swl10.blogspot.co.uk/2013/09/transforming-qti-v2-into-xhtml-5.html) Goldilocks Rendering\n* CSS Parser for direct QTI Information Model mapping at rendering time\n* Item and Test Sessions (with lightning fast binary persistence)\n* Nice and Clean API for QTI Document manipulation/traversal\n* PreConditions \u0026 Branching\n* Selection and Ordering\n* Response, Outcome and Template Processing\n* aria-* attributes\n* Unit test driven\n\n## Installation (developers)\n\n1. Clone the repository.\n2. Make sure you know how [Composer](https://getcomposer.org/download/) works and it is installed on your system.\n3. php composer.phar install\n4. You are ready!\n\n## Unit Tests (developers)\n\nRun Unit Tests by invoking the following shell command:\n\n```shell\ncp phpunit.xml.dist phpunit.xml\n./vendor/bin/phpunit test\n```\n\n## Contribute\n\nWe are always looking for people to feed the project with:\n\n* Bug reports\n* Unit tests\n* New features\n\n[Please make yourself known](https://github.com/bugalot)!\n\n## QTI Item Session Management\n\n### Introduction Example\n\nThe following example demonstrates how to instantiate an item session for a given QTI XML item document. The item\nin use in this example is the [*\"Composition of Water\"*](http://www.imsglobal.org/question/qtiv2p1/examples/items/choice_multiple.xml) item, from the [QTI 2.1 Implementation Guide](http://www.imsglobal.org/question/qtiv2p1/imsqti_implv2p1.html).\n\n```php\n\u003c?php\nuse qtism\\common\\enums\\BaseType;\nuse qtism\\common\\enums\\Cardinality;\nuse qtism\\common\\datatypes\\QtiIdentifier;\nuse qtism\\data\\storage\\xml\\XmlDocument;\nuse qtism\\runtime\\common\\State;\nuse qtism\\runtime\\common\\ResponseVariable;\nuse qtism\\runtime\\common\\MultipleContainer;\nuse qtism\\runtime\\tests\\AssessmentItemSession;\n\n// Instantiate a new QTI XML document, and load a QTI XML document.\n$itemDoc = new XmlDocument('2.1');\n$itemDoc-\u003eload('choice_multiple.xml');\n\n/* \n * A QTI XML document can be used to load various pieces of QTI content such as assessmentItem,\n * assessmentTest, responseProcessing, ... components. Our target is an assessmentItem, which is the\n * root component of our document.\n */\n$item = $itemDoc-\u003egetDocumentComponent();\n\n/* \n * The item session represents the collected and computed data related to the interactions a\n * candidate performs on a single assessmentItem. As per the QTI specification, \"an item session\n * is the accumulation of all attempts at a particular instance of an assessmentItem made by\n * a candidate.\n */\n$itemSession = new AssessmentItemSession($item);\n\n// The candidate is entering the item session, and is beginning his first attempt.\n$itemSession-\u003ebeginItemSession();\n$itemSession-\u003ebeginAttempt();\n\n/* \n * We instantiate the responses provided by the candidate for this assessmentItem, for the current\n * item session. For this assessmentItem, the data collected from the candidate is represented by \n * a State, composed of a single ResponseVariable named 'RESPONSE'.\n */\n$responses = new State(\n    array(\n        // The 'RESPONSE' ResponseVariable has a QTI multiple cardinality, and a QTI identifier baseType.\n        new ResponseVariable(\n            'RESPONSE',\n            Cardinality::MULTIPLE,\n            BaseType::IDENTIFIER,\n            /* \n             * The ResponseVariable value is a Container with multiple cardinality and an identifier\n             * baseType to meet the cardinality and baseType requirements of the ResponseVariable.\n             */\n            new MultipleContainer(\n                BaseType::IDENTIFIER,\n                /*\n                 * The values composing the Container are identifiers 'H' and 'O', which represent\n                 * the correct response to this item.\n                 */\n                array(\n                    new QtiIdentifier('H'),\n                    new QtiIdentifier('O')\n                )\n            )\n        )\n    )\n);\n\n/*\n * The candidate is finishing the current attempt, by providing a correct response.\n * ResponseProcessing takes place to produce a new value for the 'SCORE' OutcomeVariable.\n */\n$itemSession-\u003eendAttempt($responses);\n\n// The item session variables and their values can be accessed by their identifier.\necho 'numAttempts: ' . $itemSession['numAttempts'] . \"\\n\";\necho 'completionStatus: ' . $itemSession['completionStatus'] . \"\\n\";\necho 'RESPONSE: ' . $itemSession['RESPONSE'] . \"\\n\";\necho 'SCORE: ' . $itemSession['SCORE'] . \"\\n\";\n\n/*\n * numAttempts: 1\n * completionStatus: completed\n * RESPONSE: ['H'; 'O']\n * SCORE: 2\n */\n\n// End the current item session.\n$itemSession-\u003eendItemSession();\n```\n\n### Multiple Attempts Example\n\nAs per the QTI specification, item sessions allow a single attempt to be performed by default. Trying to begin an\nattempt that will make the item session exceeding the maximum number of attempts will lead to a PHP exception, as in\nthe following example.\n\n```php\n\u003c?php\nuse qtism\\data\\storage\\xml\\XmlDocument;\nuse qtism\\runtime\\common\\State;\nuse qtism\\runtime\\tests\\AssessmentItemSession;\nuse qtism\\runtime\\tests\\AssessmentItemSessionException;\n\n$itemDoc = new XmlDocument('2.1');\n$itemDoc-\u003eload('choice_multiple.xml');\n$item = $itemDoc-\u003egetDocumentComponent();\n\n$itemSession = new AssessmentItemSession($item);\n$itemSession-\u003ebeginItemSession();\n\n// Begin 1st attempt.\n$itemSession-\u003ebeginAttempt();\n// End attempt by providing an empty response...\n$itemSession-\u003eendAttempt(new State());\n\n// Begin 2nd attempt, but by default, maximum number of attempts is 1.\ntry {\n    $itemSession-\u003ebeginAttempt();\n} catch (AssessmentItemSessionException $e) {\n    echo $e-\u003egetMessage();\n    // A new attempt for item 'choiceMultiple' is not allowed. The maximum number of attempts (1) is reached.\n}\n```\n\nIf multiple attempts are permitted on a given assessmentItem, the `itemSessionControl`'s `maxAttempts` attribute \ncan be modified to allow multiple or unlimited attempts that can be performed by a candidate.\n\n```php\n\u003c?php\nuse qtism\\data\\ItemSessionControl;\nuse qtism\\data\\storage\\xml\\XmlDocument;\nuse qtism\\runtime\\common\\State;\nuse qtism\\runtime\\tests\\AssessmentItemSession;\n\n$itemDoc = new XmlDocument('2.1');\n$itemDoc-\u003eload('choice_multiple.xml');\n$item = $itemDoc-\u003egetDocumentComponent();\n$itemSession = new AssessmentItemSession($item);\n\n// Set the maximum number of attempts to 0 (means unlimited).\n$itemSessionControl = new ItemSessionControl();\n$itemSessionControl-\u003esetMaxAttempts(0);\n$itemSession-\u003esetItemSessionControl($itemSessionControl);\n\n// Performing multiple attempts will not lead to a PHP exception anymore, because the maximum number of attemps is unlimited!\n$itemSession-\u003ebeginItemSession();\n\n// 1st attempt will be an incorrect response from the candidate (['H'; 'Cl']).\n$responses = new State(\n    array(\n        new ResponseVariable(\n            'RESPONSE',\n            Cardinality::MULTIPLE,\n            BaseType::IDENTIFIER,\n            new MultipleContainer(\n                BaseType::IDENTIFIER,\n                array(\n                    new QtiIdentifier('H'),\n                    new QtiIdentifier('Cl')\n                )\n            )\n        )\n    )\n);\n\n$itemSession-\u003eendAttempt($responses);\n\necho 'numAttempts: ' . $itemSession['numAttempts'] . \"\\n\";\necho 'completionStatus: ' . $itemSession['completionStatus'] . \"\\n\";\necho 'RESPONSE: ' . $itemSession['RESPONSE'] . \"\\n\";\necho 'SCORE: ' . $itemSession['SCORE'] . \"\\n\";\n\n/*\n * numAttempts: 1\n * completionStatus: completed\n * RESPONSE: ['H'; 'N']\n * SCORE: 0\n */\n\n// 2nd attempt will send a correct response this time (['H'; 'O'])!\n$itemSession-\u003ebeginAttempt();\n$responses['RESPONSE'][1]-\u003esetValue('O');\n$itemSession-\u003eendAttempt();\n\necho 'numAttempts: ' . $itemSession['numAttempts'] . \"\\n\";\necho 'completionStatus: ' . $itemSession['completionStatus'] . \"\\n\";\necho 'RESPONSE: ' . $itemSession['RESPONSE'] . \"\\n\";\necho 'SCORE: ' . $itemSession['SCORE'] . \"\\n\";\n\n/*\n * numAttempts: 2\n * completionStatus: completed\n * RESPONSE: ['H'; 'O']\n * SCORE: 2\n */\n\n$itemSession-\u003eendItemSession();\n```\n\nYou can get more information on the QTI-SDK GitHub Wiki!\n\n## QTI Rendering\n\nThe QTI Software Development Kit enables you to transform XML serialized QTI files\ninto their (X)HTML5 Goldilocks equivalent. The following shell command renders the `path/to/qti.xml` QTI file into an HTML5 \ndocument using the (X)HTML5 Golidlocks rendering flavour with indentation formatting. The rendering output (stdout) \nis redirected to the `/home/jerome/qti.html` file.\n\n```shell\n./vendor/bin/qtisdk render -df --source path/to/qti.xml --flavour goldilocks \u003e /home/jerome/qti.html\n```\n\nFor additional help and information, just call the help screen to know about the features provided by the rendering binaries!\n\n```shell\n./vendor/bin/qtisdk render --help\n```\n\n## Configuration\n\nAs for other major PHP frameworks such as [Doctrine](http://stackoverflow.com/questions/21925354/doctrine-is-freaking-out-when-i-turn-on-php-opcache-on) QTI-SDK makes use\nof annotations. In such a context, the two following Zend Opcache configuration directives must be\nconfigured as below.\n\n* [opcache.save_comments](http://php.net/manual/en/opcache.configuration.php#ini.opcache.save-comments): 1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foat-sa%2Fqti-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foat-sa%2Fqti-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foat-sa%2Fqti-sdk/lists"}