Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.77% covered (danger)
36.77%
57 / 155
38.89% covered (danger)
38.89%
14 / 36
CRAP
0.00% covered (danger)
0.00%
0 / 1
LtiLaunchData
36.77% covered (danger)
36.77%
57 / 155
38.89% covered (danger)
38.89%
14 / 36
1955.30
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fromJsonArray
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fromPsrRequest
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 fromRequest
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fromLti1p3MessagePayload
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
756
 getParametersFromUrl
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
7.02
 unserializeAgsClaims
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getCustomParameter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getCustomParameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getVariables
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getResourceLinkID
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getVariable
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getBooleanVariable
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 getResourceLinkTitle
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 hasVariable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserID
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserGivenName
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getUserFamilyName
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getUserFullName
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getUserEmail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserRoles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasLaunchLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLaunchLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getToolConsumerName
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 getLtiConsumer
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getOauthKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserEmail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserFamilyName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserGivenName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserPersonSourcedId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLtiForUserRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasReturnUrl
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getReturnUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 jsonSerialize
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2013-2019 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoLti\models\classes;
23
24use common_http_Request;
25use core_kernel_classes_Resource;
26use OAT\Library\Lti1p3Core\Message\Payload\Claim\AgsClaim;
27use OAT\Library\Lti1p3Core\Message\Payload\LtiMessagePayloadInterface;
28use OAT\Library\Lti1p3Core\Platform\PlatformInterface;
29use oat\oatbox\log\LoggerAwareTrait;
30use oat\taoLti\models\classes\LtiMessages\LtiErrorMessage;
31use Psr\Http\Message\ServerRequestInterface;
32use tao_helpers_Request;
33
34class LtiLaunchData implements \JsonSerializable
35{
36    use LoggerAwareTrait;
37
38    public const OAUTH_CONSUMER_KEY  = 'oauth_consumer_key';
39    public const RESOURCE_LINK_ID    = 'resource_link_id';
40    public const RESOURCE_LINK_TITLE = 'resource_link_title';
41    public const CONTEXT_ID          = 'context_id';
42    public const CONTEXT_LABEL       = 'context_label';
43    public const CONTEXT_TITLE       = 'context_title';
44
45    public const USER_ID                          = 'user_id';
46    public const ROLES                            = 'roles';
47    public const LIS_PERSON_NAME_GIVEN            = 'lis_person_name_given';
48    public const LIS_PERSON_NAME_FAMILY           = 'lis_person_name_family';
49    public const LIS_PERSON_NAME_FULL             = 'lis_person_name_full';
50    public const LIS_PERSON_CONTACT_EMAIL_PRIMARY = 'lis_person_contact_email_primary';
51
52    public const LAUNCH_PRESENTATION_LOCALE     = 'launch_presentation_locale';
53    public const LAUNCH_PRESENTATION_RETURN_URL = 'launch_presentation_return_url';
54
55    public const TOOL_CONSUMER_INSTANCE_ID          = 'tool_consumer_instance_id';
56    public const TOOL_CONSUMER_INSTANCE_NAME        = 'tool_consumer_instance_name';
57    public const TOOL_CONSUMER_INSTANCE_DESCRIPTION = 'tool_consumer_instance_description';
58
59    public const LTI_VERSION = 'lti_version';
60    public const LTI_MESSAGE_TYPE = 'lti_message_type';
61
62    public const LIS_RESULT_SOURCEDID = 'lis_result_sourcedid';
63    public const LIS_OUTCOME_SERVICE_URL = 'lis_outcome_service_url';
64
65    // review mode
66    public const LTI_SHOW_SCORE = 'custom_show_score';
67    public const LTI_SHOW_CORRECT = 'custom_show_correct';
68
69    public const LTI_REDIRECT_AFTER_LOGOUT_URL = 'authoringSettings.redirectAfterLogoutUrl';
70
71    public const LTI_TAO_LOGIN_URL = 'authoringSettings.taoLoginUrl';
72
73    // for user claim
74    private const LTI_FOR_USER_ID = 'lti_for_user_id';
75    private const LTI_FOR_USER_EMAIL = 'lti_for_user_email';
76    private const LTI_FOR_USER_FAMILY_NAME = 'lti_for_user_family_name';
77    private const LTI_FOR_USER_GIVEN_NAME = 'lti_for_user_given_name';
78    private const LTI_FOR_USER_NAME = 'lti_for_user_name';
79    private const LTI_FOR_USER_PERSON_SOURCED_ID = 'lti_for_user_person_sourced_id';
80    private const LTI_FOR_USER_ROLES = 'lti_for_user_roles';
81
82    // AGS
83    public const AGS_CLAIMS = 'ags_claims';
84
85    /**
86     * LTI variables
87     *
88     * @var array
89     */
90    private $variables;
91
92    /**
93     * Custom parameters of the LTI call
94     *
95     * @var array
96     */
97    private $customParams;
98
99    /**
100     * @var core_kernel_classes_Resource
101     */
102    private $ltiConsumer;
103
104    /**
105     * Spawns an LtiSession
106     *
107     * @param array $ltiVariables
108     * @param array $customParameters
109     */
110    public function __construct(array $ltiVariables, array $customParameters)
111    {
112        $this->variables = $ltiVariables;
113        $this->customParams = $customParameters;
114    }
115
116    public static function fromJsonArray(array $json): LtiLaunchData
117    {
118        static::unserializeAgsClaims($json);
119        return new static($json['variables'], $json['customParams']);
120    }
121
122    /**
123     *
124     * @param common_http_Request $request
125     * @return LtiLaunchData
126     * @throws \ResolverException
127     */
128    public static function fromPsrRequest(ServerRequestInterface $request)
129    {
130        $extra = self::getParametersFromUrl($request->getUri()->__toString());
131        $combined = array_merge($request->getQueryParams(), $request->getParsedBody());
132        return new static($combined, $extra);
133    }
134
135    /**
136     *
137     * @param common_http_Request $request
138     * @return LtiLaunchData
139     * @throws \ResolverException
140     */
141    public static function fromRequest(common_http_Request $request)
142    {
143        $extra = self::getParametersFromUrl($request->getUrl());
144        return new static($request->getParams(), $extra);
145    }
146
147    public static function fromLti1p3MessagePayload(
148        LtiMessagePayloadInterface $payload,
149        PlatformInterface $platform = null
150    ): self {
151        $variables[self::OAUTH_CONSUMER_KEY] = '';
152        $variables[self::RESOURCE_LINK_ID] =
153            $payload->getResourceLink() ? $payload->getResourceLink()->getIdentifier() : null;
154        $variables[self::RESOURCE_LINK_TITLE] =
155            $payload->getResourceLink() ? $payload->getResourceLink()->getTitle() : null;
156
157        $variables[self::CONTEXT_ID] = $payload->getContext() ? $payload->getContext()->getIdentifier() : null;
158        $variables[self::CONTEXT_LABEL] = $payload->getContext() ? $payload->getContext()->getLabel() : null;
159        $variables[self::CONTEXT_TITLE] = $payload->getContext() ? $payload->getContext()->getTitle() : null;
160
161        $variables[self::USER_ID] =
162            $payload->getUserIdentity() ? $payload->getUserIdentity()->getIdentifier() : null;
163
164        $variables[self::ROLES] = implode(',', $payload->getRoles());
165        $variables[self::LIS_PERSON_NAME_GIVEN] =
166            $payload->getUserIdentity() ? $payload->getUserIdentity()->getGivenName() : null;
167        $variables[self::LIS_PERSON_NAME_FAMILY] =
168            $payload->getUserIdentity() ? $payload->getUserIdentity()->getFamilyName() : null;
169        $variables[self::LIS_PERSON_NAME_FULL] =
170            $payload->getUserIdentity() ? $payload->getUserIdentity()->getName() : null;
171        $variables[self::LIS_PERSON_CONTACT_EMAIL_PRIMARY] =
172            $payload->getUserIdentity() ? $payload->getUserIdentity()->getEmail() : null;
173
174        $variables[self::LAUNCH_PRESENTATION_LOCALE] =
175            $payload->getLaunchPresentation() ? $payload->getLaunchPresentation()->getLocale() : null;
176        $variables[self::LAUNCH_PRESENTATION_RETURN_URL] =
177            $payload->getLaunchPresentation() ? $payload->getLaunchPresentation()->getReturnUrl() : null;
178
179        $variables[self::LTI_VERSION] = $payload->getVersion();
180        $variables[self::LTI_MESSAGE_TYPE] = $payload->getMessageType();
181        $variables[self::LIS_RESULT_SOURCEDID] =
182            $payload->getBasicOutcome() ? $payload->getBasicOutcome()->getLisResultSourcedId() : null;
183        $variables[self::LIS_OUTCOME_SERVICE_URL] =
184            $payload->getBasicOutcome() ? $payload->getBasicOutcome()->getLisOutcomeServiceUrl() : null;
185        $variables[self::LTI_FOR_USER_ID] =
186            $payload->getForUser() ? $payload->getForUser()->getIdentifier() : null;
187        $variables[self::LTI_FOR_USER_EMAIL] =
188            $payload->getForUser() ? $payload->getForUser()->getEmail() : null;
189        $variables[self::LTI_FOR_USER_FAMILY_NAME] =
190            $payload->getForUser() ? $payload->getForUser()->getFamilyName() : null;
191        $variables[self::LTI_FOR_USER_GIVEN_NAME] =
192            $payload->getForUser() ? $payload->getForUser()->getGivenName() : null;
193        $variables[self::LTI_FOR_USER_NAME] =
194            $payload->getForUser() ? $payload->getForUser()->getName() : null;
195        $variables[self::LTI_FOR_USER_PERSON_SOURCED_ID] =
196            $payload->getForUser() ? $payload->getForUser()->getPersonSourcedId() : null;
197        $variables[self::LTI_FOR_USER_ROLES] =
198            $payload->getForUser() ? $payload->getForUser()->getRoles() : null;
199
200        if ($platform) {
201            // we need to have inner platform ID
202            $variables[self::TOOL_CONSUMER_INSTANCE_ID] = $platform->getIdentifier();
203
204            if ($platformFromClaim = $payload->getPlatformInstance()) {
205                $variables[self::TOOL_CONSUMER_INSTANCE_NAME] = $platformFromClaim->getName();
206                $variables[self::TOOL_CONSUMER_INSTANCE_DESCRIPTION] = $platformFromClaim->getDescription();
207            } else {
208                $variables[self::TOOL_CONSUMER_INSTANCE_NAME] = $platform->getName();
209                $variables[self::TOOL_CONSUMER_INSTANCE_DESCRIPTION] = $platform->getName();
210            }
211        }
212
213        if ($ags = $payload->getAgs()) {
214            $variables[self::AGS_CLAIMS] = $ags;
215        }
216
217        $customParams = $payload->getCustom();
218
219        // review mode
220        if (isset($customParams[self::LTI_SHOW_SCORE])) {
221            $variables[self::LTI_SHOW_SCORE] = filter_var(
222                $customParams[self::LTI_SHOW_SCORE],
223                FILTER_VALIDATE_BOOLEAN
224            );
225        }
226
227        if (isset($customParams[self::LTI_SHOW_CORRECT])) {
228            $variables[self::LTI_SHOW_CORRECT] = filter_var(
229                $customParams[self::LTI_SHOW_CORRECT],
230                FILTER_VALIDATE_BOOLEAN
231            );
232        }
233
234        return new static($variables, $customParams);
235    }
236
237    /**
238     * @throws \ResolverException
239     */
240    private static function getParametersFromUrl(string $url): array
241    {
242        $returnValue = [];
243
244        parse_str(parse_url($url, PHP_URL_QUERY), $returnValue);
245
246        // encoded in url
247        $parts = explode('/', tao_helpers_Request::getRelativeUrl($url), 4);
248        if (count($parts) == 4) {
249            [$extension, $module, $action, $codedUri] = $parts;
250            $base64String = base64_decode($codedUri);
251            if ($base64String !== false) {
252                // old serialised url
253                if (substr($base64String, 0, strlen('a:')) == 'a:') {
254                    $additionalParams = unserialize($base64String);
255                } else {
256                    $additionalParams = json_decode($base64String, true);
257                }
258                if ($additionalParams !== false && is_array($additionalParams)) {
259                    foreach ($additionalParams as $key => $value) {
260                        $returnValue[$key] = $value;
261                    }
262                }
263            }
264        }
265
266        return $returnValue;
267    }
268
269    private static function unserializeAgsClaims(array &$json): void
270    {
271        if (isset($json['variables'][self::AGS_CLAIMS])) {
272            $json['variables'][self::AGS_CLAIMS] = AgsClaim::denormalize($json['variables'][self::AGS_CLAIMS]);
273        }
274    }
275
276    /**
277     * @param string $key
278     * @return mixed|null
279     */
280    public function getCustomParameter($key)
281    {
282        return isset($this->customParams[$key]) ? $this->customParams[$key] : null;
283    }
284
285    /**
286     * Get all custom parameters provided during launch.
287     *
288     * @return array
289     */
290    public function getCustomParameters()
291    {
292        return $this->customParams;
293    }
294
295    /**
296     * Get all lti variables provided during launch.
297     *
298     * @return array
299     */
300    public function getVariables()
301    {
302        return $this->variables;
303    }
304
305    /**
306     * @return mixed
307     * @throws LtiVariableMissingException
308     */
309    public function getResourceLinkID()
310    {
311        return $this->getVariable(self::RESOURCE_LINK_ID);
312    }
313
314    /**
315     * @param $key
316     * @return mixed
317     * @throws LtiVariableMissingException
318     */
319    public function getVariable($key)
320    {
321        if (isset($this->variables[$key])) {
322            return $this->variables[$key];
323        } else {
324            throw new LtiVariableMissingException($key);
325        }
326    }
327
328    /**
329     * @param string $key
330     * @return boolean mixed
331     *
332     * @throws LtiException
333     * @throws LtiVariableMissingException
334     */
335    public function getBooleanVariable($key)
336    {
337        $original = $this->getVariable($key);
338        $var = is_string($original) ? mb_strtolower($original) : null;
339
340        if ($var === 'true') {
341            return true;
342        } elseif ($var === 'false') {
343            return false;
344        } else {
345            throw new LtiInvalidVariableException(
346                'Invalid value of `' . $key . '` variable, boolean string expected.',
347                LtiErrorMessage::ERROR_INVALID_PARAMETER
348            );
349        }
350    }
351
352    /**
353     * @return mixed|string
354     * @throws LtiVariableMissingException
355     */
356    public function getResourceLinkTitle()
357    {
358        if ($this->hasVariable(self::RESOURCE_LINK_TITLE)) {
359            return $this->getVariable(self::RESOURCE_LINK_TITLE);
360        } else {
361            return __('link');
362        }
363    }
364
365    public function hasVariable($key)
366    {
367        return isset($this->variables[$key]);
368    }
369
370    /**
371     * @return mixed
372     * @throws LtiVariableMissingException
373     */
374    public function getUserID()
375    {
376        return $this->getVariable(self::USER_ID);
377    }
378
379    /**
380     * @return mixed
381     */
382    public function getUserGivenName()
383    {
384        if ($this->hasVariable(static::LIS_PERSON_NAME_GIVEN)) {
385            return $this->getVariable(static::LIS_PERSON_NAME_GIVEN);
386        }
387    }
388
389    /**
390     * @return mixed
391     */
392    public function getUserFamilyName()
393    {
394        if ($this->hasVariable(static::LIS_PERSON_NAME_FAMILY)) {
395            return $this->getVariable(static::LIS_PERSON_NAME_FAMILY);
396        }
397    }
398
399    /**
400     * @return mixed
401     * @throws LtiVariableMissingException
402     */
403    public function getUserFullName()
404    {
405        if ($this->hasVariable(self::LIS_PERSON_NAME_FULL)) {
406            return $this->getVariable(self::LIS_PERSON_NAME_FULL);
407        }
408    }
409
410    /**
411     * @return mixed
412     * @throws LtiVariableMissingException
413     */
414    public function getUserEmail()
415    {
416        return $this->getVariable(self::LIS_PERSON_CONTACT_EMAIL_PRIMARY);
417    }
418
419    /**
420     * @return array
421     * @throws LtiVariableMissingException
422     */
423    public function getUserRoles()
424    {
425        return explode(',', $this->getVariable(self::ROLES));
426    }
427
428    public function hasLaunchLanguage()
429    {
430        return $this->hasVariable(self::LAUNCH_PRESENTATION_LOCALE);
431    }
432
433    /**
434     * @return mixed
435     * @throws LtiVariableMissingException
436     */
437    public function getLaunchLanguage()
438    {
439        return $this->getVariable(self::LAUNCH_PRESENTATION_LOCALE);
440    }
441
442    /**
443     * Tries to return the tool consumer name
444     *
445     * Returns null if no name found
446     *
447     * @return string
448     * @throws LtiVariableMissingException
449     */
450    public function getToolConsumerName()
451    {
452        $consumerName = null;
453
454        if ($this->hasVariable(self::TOOL_CONSUMER_INSTANCE_NAME)) {
455            $consumerName = $this->getVariable(self::TOOL_CONSUMER_INSTANCE_NAME);
456        }
457
458        if (
459            $consumerName === null
460            && $this->hasVariable(self::TOOL_CONSUMER_INSTANCE_DESCRIPTION)
461        ) {
462            $consumerName = $this->getVariable(self::TOOL_CONSUMER_INSTANCE_DESCRIPTION);
463        }
464
465        return $consumerName;
466    }
467
468    /**
469     * @return core_kernel_classes_Resource
470     * @throws LtiVariableMissingException
471     */
472    public function getLtiConsumer()
473    {
474        if (is_null($this->ltiConsumer)) {
475            $dataStore = new \tao_models_classes_oauth_DataStore();
476            $this->ltiConsumer = $dataStore->findOauthConsumerResource($this->getOauthKey())->getUri();
477        }
478
479        return new \core_kernel_classes_Resource($this->ltiConsumer);
480    }
481
482    /**
483     * @return mixed
484     * @throws LtiVariableMissingException
485     */
486    public function getOauthKey()
487    {
488        return $this->getVariable(self::OAUTH_CONSUMER_KEY);
489    }
490
491    /**
492     * @throws LtiVariableMissingException
493     */
494    public function getLtiForUserId(): string
495    {
496        return $this->getVariable(self::LTI_FOR_USER_ID);
497    }
498
499    /**
500     * @throws LtiVariableMissingException
501     */
502    public function getLtiForUserEmail(): string
503    {
504        return $this->getVariable(self::LTI_FOR_USER_EMAIL);
505    }
506
507    /**
508     * @throws LtiVariableMissingException
509     */
510    public function getLtiForUserFamilyName(): string
511    {
512        return $this->getVariable(self::LTI_FOR_USER_FAMILY_NAME);
513    }
514
515    /**
516     * @throws LtiVariableMissingException
517     */
518    public function getLtiForUserGivenName(): string
519    {
520        return $this->getVariable(self::LTI_FOR_USER_GIVEN_NAME);
521    }
522
523    /**
524     * @throws LtiVariableMissingException
525     */
526    public function getLtiForUserName(): string
527    {
528        return $this->getVariable(self::LTI_FOR_USER_NAME);
529    }
530
531    /**
532     * @throws LtiVariableMissingException
533     */
534    public function getLtiForUserPersonSourcedId(): string
535    {
536        return $this->getVariable(self::LTI_FOR_USER_PERSON_SOURCED_ID);
537    }
538
539    /**
540     * @throws LtiVariableMissingException
541     */
542    public function getLtiForUserRoles(): array
543    {
544        return $this->getVariable(self::LTI_FOR_USER_ROLES);
545    }
546
547    /**
548     * @return bool
549     * @throws LtiException
550     */
551    public function hasReturnUrl()
552    {
553        if ($this->hasVariable(self::LAUNCH_PRESENTATION_RETURN_URL)) {
554            $returnUrl = $this->getReturnUrl();
555
556            if (!empty($returnUrl)) {
557                if (filter_var($returnUrl, FILTER_VALIDATE_URL)) {
558                    return true;
559                } else {
560                    $this->logWarning("Invalid LTI Return URL '${returnUrl}'.");
561                }
562            }
563        }
564
565        return false;
566    }
567
568    /**
569     * Return the returnUrl to the tool consumer
570     *
571     * @return string
572     * @throws LtiException
573     */
574    public function getReturnUrl()
575    {
576        return $this->getVariable(self::LAUNCH_PRESENTATION_RETURN_URL);
577    }
578
579    /**
580     * Specify data which should be serialized to JSON
581     * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
582     * @return mixed data which can be serialized by <b>json_encode</b>,
583     * which is a value of any type other than a resource.
584     * @since 5.4.0
585     */
586    public function jsonSerialize(): array
587    {
588        return [
589            'variables' => array_map(
590                fn ($var) => $var instanceof AgsClaim ? $var->normalize() : $var,
591                $this->variables
592            ),
593            'customParams' => $this->customParams,
594        ];
595    }
596}