• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

packbackbooks / lti-1-3-php-library / 16699073232

25 Jul 2025 03:48PM UTC coverage: 97.702%. Remained the same
16699073232

push

github

web-flow
Merge pull request #165 from packbackbooks/mgmt-194-cleanup

Update linting rules

80 of 86 new or added lines in 6 files covered. (93.02%)

19 existing lines in 4 files now uncovered.

1148 of 1175 relevant lines covered (97.7%)

6.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.56
/src/LtiAssignmentsGradesService.php
1
<?php
2

3
namespace Packback\Lti1p3;
4

5
class LtiAssignmentsGradesService extends LtiAbstractService
6
{
7
    public const CONTENTTYPE_SCORE = 'application/vnd.ims.lis.v1.score+json';
8
    public const CONTENTTYPE_LINEITEM = 'application/vnd.ims.lis.v2.lineitem+json';
9
    public const CONTENTTYPE_LINEITEMCONTAINER = 'application/vnd.ims.lis.v2.lineitemcontainer+json';
10
    public const CONTENTTYPE_RESULTCONTAINER = 'application/vnd.ims.lis.v2.resultcontainer+json';
11

12
    public static function appendLineItemPath(LtiLineitem $lineItem, string $suffix): string
6✔
13
    {
14
        $url = $lineItem->getId();
6✔
15

16
        $path = implode('', [
6✔
17
            parse_url($url, PHP_URL_HOST),
6✔
18
            parse_url($url, PHP_URL_PORT) ? ':'.parse_url($url, PHP_URL_PORT) : '',
6✔
19
            parse_url($url, PHP_URL_PATH),
6✔
20
        ]);
6✔
21

22
        return str_replace($path, $path.$suffix, $url);
6✔
23
    }
24

25
    public static function appendQueryParams(string $url, array $params): string
1✔
26
    {
27
        $existingQueryString = parse_url($url, PHP_URL_QUERY);
1✔
28
        if ($existingQueryString) {
1✔
29
            parse_str($existingQueryString, $existingQueryParams);
1✔
30
            $queryString = http_build_query(array_merge($existingQueryParams, $params));
1✔
31

32
            return str_replace($existingQueryString, $queryString, $url);
1✔
33
        } else {
34
            $queryString = http_build_query($params);
1✔
35

36
            return $url.'?'.$queryString;
1✔
37
        }
38
    }
39

40
    public function getScope(): array
17✔
41
    {
42
        return $this->getServiceData()['scope'];
17✔
43
    }
44

45
    // https://www.imsglobal.org/spec/lti-ags/v2p0#assignment-and-grade-service-claim
46
    // When an LTI message is launching a resource associated to one and only one lineitem,
47
    // the claim must include the endpoint URL for accessing the associated line item;
48
    // in all other cases, this property must be either blank or not included in the claim.
49
    public function getResourceLaunchLineItem(): ?LtiLineitem
4✔
50
    {
51
        $serviceData = $this->getServiceData();
4✔
52
        if (empty($serviceData['lineitem'])) {
4✔
53
            return null;
2✔
54
        }
55

56
        return LtiLineitem::new()->setId($serviceData['lineitem']);
2✔
57
    }
58

59
    public function putGrade(LtiGrade $grade, ?LtiLineitem $lineitem = null)
1✔
60
    {
61
        $this->validateScopes([LtiConstants::AGS_SCOPE_SCORE]);
1✔
62

63
        $lineitem = $this->ensureLineItemExists($lineitem);
1✔
64
        $scoreUrl = static::appendLineItemPath($lineitem, '/scores');
1✔
65

66
        $request = new ServiceRequest(
1✔
67
            ServiceRequest::METHOD_POST,
1✔
68
            $scoreUrl,
1✔
69
            ServiceRequest::TYPE_SYNC_GRADE
1✔
70
        );
1✔
71
        $request->setBody($grade);
1✔
72
        $request->setContentType(static::CONTENTTYPE_SCORE);
1✔
73

74
        return $this->makeServiceRequest($request);
1✔
75
    }
76

77
    public function findLineItem(LtiLineitem $newLineItem): ?LtiLineitem
4✔
78
    {
79
        $lineitems = $this->getLineItems();
4✔
80

81
        foreach ($lineitems as $lineitem) {
4✔
82
            if ($this->isMatchingLineitem($lineitem, $newLineItem)) {
2✔
83
                return new LtiLineitem($lineitem);
2✔
84
            }
85
        }
86

87
        return null;
2✔
88
    }
89

90
    public function updateLineitem(LtiLineitem $lineitemToUpdate): LtiLineitem
1✔
91
    {
92
        $request = new ServiceRequest(
1✔
93
            ServiceRequest::METHOD_PUT,
1✔
94
            $lineitemToUpdate->getId(),
1✔
95
            ServiceRequest::TYPE_UPDATE_LINEITEM
1✔
96
        );
1✔
97

98
        $request->setBody($lineitemToUpdate)
1✔
99
            ->setContentType(static::CONTENTTYPE_LINEITEM)
1✔
100
            ->setAccept(static::CONTENTTYPE_LINEITEM);
1✔
101

102
        $updatedLineitem = $this->makeServiceRequest($request);
1✔
103

104
        return new LtiLineitem($updatedLineitem['body']);
1✔
105
    }
106

107
    public function createLineitem(LtiLineitem $newLineItem): LtiLineitem
3✔
108
    {
109
        $request = new ServiceRequest(
3✔
110
            ServiceRequest::METHOD_POST,
3✔
111
            $this->getServiceData()['lineitems'],
3✔
112
            ServiceRequest::TYPE_CREATE_LINEITEM
3✔
113
        );
3✔
114
        $request->setBody($newLineItem)
3✔
115
            ->setContentType(static::CONTENTTYPE_LINEITEM)
3✔
116
            ->setAccept(static::CONTENTTYPE_LINEITEM);
3✔
117
        $createdLineItem = $this->makeServiceRequest($request);
3✔
118

119
        return new LtiLineitem($createdLineItem['body']);
3✔
120
    }
121

122
    public function deleteLineitem(): array
1✔
123
    {
124
        $request = new ServiceRequest(
1✔
125
            ServiceRequest::METHOD_DELETE,
1✔
126
            $this->getServiceData()['lineitem'],
1✔
127
            ServiceRequest::TYPE_DELETE_LINEITEM
1✔
128
        );
1✔
129

130
        return $this->makeServiceRequest($request);
1✔
131
    }
132

133
    public function findOrCreateLineitem(LtiLineitem $newLineItem): LtiLineitem
2✔
134
    {
135
        return $this->findLineItem($newLineItem) ?? $this->createLineitem($newLineItem);
2✔
136
    }
137

138
    public function getGrades(?LtiLineitem $lineitem = null, ?string $userId = null)
4✔
139
    {
140
        $lineitem = $this->ensureLineItemExists($lineitem);
4✔
141
        $resultsUrl = static::appendLineItemPath($lineitem, '/results');
4✔
142

143
        if (isset($userId)) {
4✔
UNCOV
144
            $resultsUrl = static::appendQueryParams($resultsUrl, [
×
UNCOV
145
                'user_id' => $userId,
×
UNCOV
146
            ]);
×
147
        }
148

149
        $request = new ServiceRequest(
4✔
150
            ServiceRequest::METHOD_GET,
4✔
151
            $resultsUrl,
4✔
152
            ServiceRequest::TYPE_GET_GRADES
4✔
153
        );
4✔
154
        $request->setAccept(static::CONTENTTYPE_RESULTCONTAINER);
4✔
155

156
        return $this->getAll($request);
4✔
157
    }
158

159
    public function getLineItems(): array
6✔
160
    {
161
        $this->validateScopes([LtiConstants::AGS_SCOPE_LINEITEM, LtiConstants::AGS_SCOPE_LINEITEM_READONLY]);
6✔
162

163
        $request = new ServiceRequest(
6✔
164
            ServiceRequest::METHOD_GET,
6✔
165
            $this->getServiceData()['lineitems'],
6✔
166
            ServiceRequest::TYPE_GET_LINEITEMS
6✔
167
        );
6✔
168
        $request->setAccept(static::CONTENTTYPE_LINEITEMCONTAINER);
6✔
169

170
        $lineitems = $this->getAll($request);
6✔
171

172
        // If there is only one item, then wrap it in an array so the foreach works
173
        if (isset($lineitems['body']['id'])) {
6✔
174
            $lineitems['body'] = [$lineitems['body']];
1✔
175
        }
176

177
        return $lineitems;
6✔
178
    }
179

180
    public function getLineItem(string $url): LtiLineitem
3✔
181
    {
182
        $this->validateScopes([LtiConstants::AGS_SCOPE_LINEITEM, LtiConstants::AGS_SCOPE_LINEITEM_READONLY]);
3✔
183

184
        $request = new ServiceRequest(
2✔
185
            ServiceRequest::METHOD_GET,
2✔
186
            $url,
2✔
187
            ServiceRequest::TYPE_GET_LINEITEM
2✔
188
        );
2✔
189
        $request->setAccept(static::CONTENTTYPE_LINEITEM);
2✔
190

191
        $response = $this->makeServiceRequest($request)['body'];
2✔
192

193
        return new LtiLineitem($response);
2✔
194
    }
195

196
    private function ensureLineItemExists(?LtiLineitem $lineitem = null): LtiLineitem
5✔
197
    {
198
        // If no line item is passed in, attempt to use the one associated with
199
        // this launch.
200
        if (!isset($lineitem)) {
5✔
201
            $lineitem = $this->getResourceLaunchLineItem();
2✔
202
        }
203

204
        // If none exists still, create a default line item.
205
        if (!isset($lineitem)) {
5✔
206
            $defaultLineitem = LtiLineitem::new()
1✔
207
                ->setLabel('default')
1✔
208
                ->setScoreMaximum(100);
1✔
209
            $lineitem = $this->createLineitem($defaultLineitem);
1✔
210
        }
211

212
        // If the line item does not contain an ID, find or create it.
213
        if (empty($lineitem->getId())) {
5✔
214
            $lineitem = $this->findOrCreateLineitem($lineitem);
1✔
215
        }
216

217
        return $lineitem;
5✔
218
    }
219

220
    private function isMatchingLineitem(array $lineitem, LtiLineitem $newLineItem): bool
2✔
221
    {
222
        return $newLineItem->getTag() == ($lineitem['tag'] ?? null) &&
2✔
223
            $newLineItem->getResourceId() == ($lineitem['resourceId'] ?? null) &&
2✔
224
            $newLineItem->getResourceLinkId() == ($lineitem['resourceLinkId'] ?? null);
2✔
225
    }
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc