news 2026/10/26 11:00:36

Moodle 内置 LTI 工具提供方类库(LTI Tool Provider Library PHP)技术解析与集成指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Moodle 内置 LTI 工具提供方类库(LTI Tool Provider Library PHP)技术解析与集成指南
  • 教育
  • 后端
  • 前端

【免费下载链接】moodle

Moodle - the world's open source learning platform

项目地址:https://gitcode.com/gh_mirrors/mo/moodle
点击查看免费下载

导读

Moodle 仓库内置的 public/lib/ltiprovider 目录,是一套面向 PHP 的 LTI(Learning Tools Interoperability®,学习工具互操作性)工具提供方(Tool Provider)实现类库。它封装了工具提供方与 LTI 工具消费方(Tool Consumer,如各种学习管理系统)通信所需的完整代码,支持 LTI 1.1、LTI 1.0 非官方扩展以及 LTI 2.0 的注册流程与服务。读完本文,你将掌握该库的核心架构、请求处理流程、持久化连接器设计,以及它在 Moodle 中被enrol_lti选课插件(LTI 发布为工具)实际消费的方式,为自行阅读、扩展或移植该库提供完整索引。

该库解决什么问题:LTI 工具提供方的通信抽象

public/lib/ltiprovider/README.md开篇即点明该库的定位:这一组 PHP 类封装了 LTI 合规的工具提供方与 LTI 工具消费方通信所需的全部代码。它并不仅覆盖某一种协议版本,而是同时涵盖:

  • LTI 1.1(正式消息规范);
  • LTI 1.0 的非官方扩展(包括 outcomes 结果服务、memberships 成员服务、setting 设置服务等ext_前缀扩展参数);
  • LTI 2.0 的注册流程与服务(Tool Proxy 注册、安全契约、服务发现)。

这些类源自 ceLTIc 项目创建的 LTI Tool Provider 类库,是其在 PHP 环境下的扩展实现。原文档强调,单纯"支持 LTI"并不困难,但借助这样一个类库,开发者能获得以下实质性收益:

  • 抽象层隔离:类库将 LTI 通信细节与应用业务代码彻底分离,工具提供方的业务逻辑不必感知底层 OAuth 签名、HTTP 交换等实现;
  • 可复用性:同一套类库代码可复用于多个不同的工具提供方应用;
  • 数据对象化与默认值填充:LTI 请求中的原始参数被转换为User、Context、ResourceLink等业务对象,缺失的数据会自动以合理的默认值补齐;
  • 结果服务自动降级:outcomes 服务会依据工具消费方实际支持的能力,自动选择 LTI 1.1 正式结果服务或非官方 outcomes 扩展;
  • 非官方扩展支持:memberships(成员名单)与 setting(工具设置)服务均有现成实现;
  • 附加管控能力:可启用/禁用某个消费方密钥、为每个消费方密钥设置访问的启用开始/结束时间窗口、支持"资源共享"(sharing)安排,使来自不同资源链接的用户能在同一个工具提供方链接内协作;
  • 平滑升级:工具提供方应用可在最小代码改动的前提下受益于 LTI 规范的后续更新。

源码结构:从命名空间看库的组织方式

类库遵循 PSR-4 自动加载规范(见 public/lib/ltiprovider/composer.json),命名空间根为IMSGlobal\LTI,对应src/目录,要求 PHP >= 5.6.0。其核心源码结构如下:

public/lib/ltiprovider/src/ ├── OAuth/ # OAuth 1.0 签名实现 │ ├── OAuthConsumer.php / OAuthRequest.php / OAuthServer.php │ ├── OAuthSignatureMethod.php │ ├── OAuthSignatureMethod_HMAC_SHA1.php │ └── OAuthSignatureMethod_HMAC_SHA256.php ├── Profile/ # LTI 2.0 工具/消费方画像描述 │ ├── Item.php / Message.php │ ├── ResourceHandler.php / ServiceDefinition.php ├── ToolProvider/ # 工具提供方核心对象 │ ├── ToolProvider.php # 请求处理中枢 │ ├── ToolConsumer.php / ToolProxy.php │ ├── Context.php / ResourceLink.php / User.php │ ├── Outcome.php / ContentItem*.php │ ├── ConsumerNonce.php / ResourceLinkShare*.php │ ├── DataConnector/ # 持久化抽象层 │ │ ├── DataConnector.php # 基类(默认无持久化) │ │ ├── DataConnector_mysql.php # MySQL 实现 │ │ ├── DataConnector_pdo.php # PDO 实现 │ │ └── DataConnector_pdo_sqlite.php# SQLite 实现 │ ├── MediaType/ # LTI 2.0 JSON 媒体类型 │ │ ├── ToolProxy.php / ToolProfile.php │ │ ├── SecurityContract.php / Message.php / ResourceHandler.php │ └── Service/ # LTI 2.0 服务调用 │ ├── Service.php / Membership.php / ToolSettings.php └── HTTPMessage.php # HTTP 消息封装

从源码结构可以看出三层职责划分:OAuth 层负责消息签名与校验(同时提供 HMAC-SHA1 与 HMAC-SHA256 两种签名方法);对象层负责把 LTI 参数映射为领域对象;DataConnector 层负责与数据库交互,让上层对象获得持久化能力。

请求处理流程:handleRequest 与消息类型分派

工具提供方的入口是 ToolProvider.php 中的handleRequest()方法,其处理管线清晰且可验证:

handleRequest() ├─ authenticate() // 校验 OAuth 签名、lti_message_type、lti_version 等 │ // (ToolProvider.php#L675 起) ├─ doCallback() // 按 lti_message_type 分派到对应回调方法 │ // (ToolProvider.php#L584 起) └─ result() // 输出结果:重定向、渲染 HTML 或返回错误信息 // (ToolProvider.php#L610 起)

authenticate()首先校验请求中是否包含lti_message_type且其值在受支持的消息类型集合内,然后校验lti_version必须是LTI-1p0或LTI-2p0之一(对应类常量LTI_VERSION1与LTI_VERSION2),对于basic-lti-launch-request还会强制要求resource_link_id非空。

库支持的三种消息类型及其分派目标定义于静态属性$MESSAGE_TYPES/$METHOD_NAMES(ToolProvider.php):

消息类型(lti_message_type)分派方法含义
basic-lti-launch-requestonLaunch()标准的 LTI 1.x 启动请求
ContentItemSelectionRequestonContentItem()LTI Content-Item(资源选择)请求
ToolProxyRegistrationRequestonRegister()LTI 2.0 工具代理注册请求

这些方法在基类中默认只调用onError()(即要求子类覆写实现业务逻辑),设计上正是"模板方法 + 回调"模式:基类完成认证、参数校验等通用工作,业务代码只需覆写对应回调即可。doCallback()还会在注册请求成功后自动调用$this->consumer->save()持久化消费方对象。

错误处理同样完善:若认证失败且消费方提供了launch_presentation_return_url(或content_item_return_url),result()会构造带lti_errormsg/lti_errorlog参数的重定向 URL 返回给消费方;custom_debug=true时还会输出带Debug error:前缀的详细原因。此外 ToolProvider.php 提供setParameterConstraint($name, $required, $maxLength, $messageTypes),允许应用为特定参数设置必填与长度约束,在启动时自动校验。

LTI 数据对象化与默认值机制

原文档强调"LTI 数据被转换为有用的对象,缺失数据自动替换为合理默认值",这一点在源码中体现得十分具体:

  • 领域对象:ToolConsumer(消费方)、Context(课程/上下文)、ResourceLink(资源链接)、User(用户)、Outcome(结果)分别封装 LTI 参数,例如消费方的密钥、上下文标题、用户的邮箱/姓名等均以对象属性形式访问。
  • 自定义参数替换变量:CUSTOM_SUBSTITUTION_VARIABLES(ToolProvider.php)维护了一张完整的 LTI 能力(capability)到消息参数名的映射表,涵盖User.id、Person.name.full、Person.email.primary、Context.id、Context.type、ResourceLink.title、Result.sourcedId、BasicOutcome.url等二十余项,使工具提供方能够自动完成 LTI 2.0 画像能力与 LTI 1.x 消息参数之间的相互转换。
  • ID 作用域:类常量ID_SCOPE_ID_ONLY、ID_SCOPE_GLOBAL、ID_SCOPE_CONTEXT、ID_SCOPE_RESOURCE(ToolProvider.php)定义了用户 ID 的生成策略——是仅用 ID 值,还是以消费方密钥、上下文 ID、资源 ID 为前缀(分隔符:)生成全局唯一 ID,这也是"用户来自不同资源链接可协作"特性的底层支撑。
  • 保留设置参数:LTI_CONSUMER_SETTING_NAMES、LTI_CONTEXT_SETTING_NAMES、LTI_RESOURCE_LINK_SETTING_NAMES三张表(ToolProvider.php)列出了从请求中截取并保留在消费方/上下文/资源链接设置属性中的参数名,如lis_result_sourcedid、lis_outcome_service_url、ext_ims_lis_memberships_url等,是结果服务与成员服务得以工作的数据基础。

持久化设计:DataConnector 抽象层

DataConnector.php 是库的持久化抽象基类,其默认行为是"无持久化"(loadToolConsumer()直接返回一个用默认值填充的消费方对象),具体数据库适配由子类实现。基类定义了七张标准表名常量:

常量表名存储内容
CONSUMER_TABLE_NAMElti2_consumer工具消费方
TOOL_PROXY_TABLE_NAMElti2_tool_proxy待处理的工具代理(LTI 2.0)
CONTEXT_TABLE_NAMElti2_context上下文(课程)
RESOURCE_LINK_TABLE_NAMElti2_resource_link资源链接
USER_RESULT_TABLE_NAMElti2_user_result用户与结果
RESOURCE_LINK_SHARE_KEY_TABLE_NAMElti2_share_key资源共享密钥
NONCE_TABLE_NAMElti2_nonceOAuth nonce 防重放

仓库随附三种实现:DataConnector_mysql.php、DataConnector_pdo.php(PDO 通用)与DataConnector_pdo_sqlite.php(SQLite),开发者可直接选用或仿照它们实现自己的数据库适配。这正是原文档所说"代码可在多个工具提供方之间复用"的落点:数据库差异被压缩在 DataConnector 一层内。

Moodle 集成实证:enrol_lti 如何消费该库

该库在 Moodle 中最典型的使用者是 LTI 选课插件enrol_lti(将 Moodle 课程"发布为 LTI 工具"供外部平台调用)。其数据连接器 public/enrol/lti/classes/data_connector.php 直接继承库的IMSGlobal\LTI\ToolProvider\DataConnector\DataConnector:

  • 构造函数以enrol_lti_作为数据库表名前缀,拼出enrol_lti_consumer、enrol_lti_context、enrol_lti_nonce、enrol_lti_resource_link、enrol_lti_share_key、enrol_lti_tool_proxy、enrol_lti_user_result等实际表名;
  • 覆写loadToolConsumer()等持久化方法,改由 Moodle 全局$DB数据对象访问记录,并按消费方密钥的 SHA-256 摘要(consumerkey256字段)查询,避免明文密钥直接参与索引查询。

这清晰印证了原文档所述"抽象层把 LTI 通信与应用程序代码分离"的价值:enrol_lti只需实现连接器接口,即可把 LTI 对象接入 Moodle 自身的数据库基础设施。

仓库内维护说明:Moodle 对上游库的本地化改动

仓库内随附的 public/lib/ltiprovider/readme_moodle.txt 记录了 Moodle 维护该第三方库时的本地改动清单,这些信息对任何打算集成或升级该库的开发者都极具参考价值:

  • Consumer 画像属性:为ToolConsumer类补充 consumer profile 成员变量,并为Context类新增context_type属性,当 POST 中提交context_type参数时予以设置;
  • 放宽必填约束:不再强制要求tool_consumer_instance_guid参数,并防止对发往提供方的请求进行再修改;
  • PHP 版本兼容:MDL-67034 的 PHP 7.4 兼容修复;MDL-78144 的 PHP 8.2 兼容——为避免动态属性弃用警告,在类声明上方批量添加#[\AllowDynamicProperties]特性(文档给出了基于sed的批量处理命令);
  • 网络层改造:MDL-71920 将上游的curl_exec直接调用迁移为 Moodle 自身的 curl 封装(见 HTTPMessage.php 中require_once($CFG->libdir . '/filelib.php')),以便统一遵守站点的安全设置;
  • 反向代理支持:MDL-64152 使用 Moodle 的$FULLME作为 OAuth 签名所用 URL,解决反向代理场景下的签名校验问题;
  • 升级注意:升级该库前应比对DataConnector.php与DataConnector_mysql.php的变更,若表结构有变化,需同步更新enrol_lti的data_connector.php、db/install.xml与db/upgrade.php。

另外值得注意的是,上游文档建议通过 Composer 安装依赖,而 Moodle 仓库选择直接捆绑源码(内含自动加载器),因此无需在 Moodle 内再执行 composer install。

结语与延伸阅读

public/lib/ltiprovider是一份结构清晰、协议覆盖完整(LTI 1.1 / LTI 1.0 扩展 / LTI 2.0 注册与服务)的工具提供方实现,其"对象化数据 + DataConnector 持久化抽象 + 回调分派"的设计使应用代码得以与 LTI 细节解耦。若需深入,建议按以下顺序阅读仓库源码:

  • 入口与分派:ToolProvider.php
  • 持久化抽象:DataConnector.php 及其 MySQL/PDO/SQLite 实现
  • 领域对象:src/ToolProvider/下的ToolConsumer.php、Context.php、ResourceLink.php、User.php
  • Moodle 集成实例:public/enrol/lti/classes/data_connector.php
  • 维护与升级说明:public/lib/ltiprovider/readme_moodle.txt

需要提醒的是,该库的正式文档与示例应用托管于上游项目(LTI-Tool-Provider-Library-PHP 的 wiki 与基于本库开发的 rating 示例工具提供方应用),本仓库内不包含这些外部文档,涉及协议规范细节时建议以 IMS Global 的 LTI 规范为准。

  • 教育
  • 后端
  • 前端

【免费下载链接】moodle

Moodle - the world's open source learning platform

项目地址:https://gitcode.com/gh_mirrors/mo/moodle
点击查看免费下载
上一篇:OpenClaw 客户端遇到 AUTH_RATE_LIMITED 认证限流锁定时如何理解与缓解?
下一篇:5分钟玩转Pyecharts主题切换:从内置主题到个性化定制全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 5:18:13

TortoiseGit连接被拒?从TCP握手到SSH配置的排查指南

用 TortoiseGit 拉代码或推代码的时候,弹出一个红色报错框:“Network error: Connection refused”。这个场景我见过太多次了,说真的,第一次遇到时我也懵过——感觉网络明明是通的,浏览器也能打开网页,怎么…

作者头像 李华
网站建设 2026/10/25 8:16:09

终极指南:RAD Debugger开发环境搭建详细步骤

终极指南:RAD Debugger开发环境搭建详细步骤 【免费下载链接】raddebugger A native, user-mode, multi-process, graphical debugger. 项目地址: https://gitcode.com/GitHub_Trending/ra/raddebugger 想要快速搭建RAD Debugger开发环境吗?作为一…

作者头像 李华
网站建设 2026/10/19 10:36:23

NVIDIA Olympus:服务器CPU单线程性能优化核心技术解析

1. Olympus不是显卡,是NVIDIA为服务器CPU性能天花板埋下的伏笔很多人看到“NVIDIA Olympus”第一反应是:又出新显卡了?点开搜索结果才发现,满屏都是“Olympus驱动安装失败”“Olympus控制面板打不开”“Olympus和GeForce冲突”——…

作者头像 李华
网站建设 2026/10/22 12:49:45

Floodfill算法详解:用DFS实现连通区域填充与经典例题剖析

算法奇妙屋写到第三十二期,这次聊聊一个很常见、但很多新手第一眼会懵的题目类型:floodfill。也就是所谓的“洪水填充”,给一个起点,像水漫开一样把满足条件的连通区域全部“染色”。无论是刷题碰到的图像渲染、岛屿数量&#xff…

作者头像 李华