<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>iTopCN – 编写自己的扩展</title><link>/docs/customization/your-extension/</link><description>Recent content in 编写自己的扩展 on iTopCN</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Fri, 27 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="/docs/customization/your-extension/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: CRUD 事件</title><link>/docs/customization/your-extension/events-crud/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/events-crud/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 iTop 中通过事件实现 CRUD（创建、更新、删除）扩展性的机制，包括各事件的触发时机、可用数据和 API。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:extensibility:events_crud">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>CRUD 扩展性指在对象被创建、更新或删除时实现特定行为。这需要通过多种方式注入 PHP 代码。&lt;/p>
&lt;ul>
&lt;li>对比：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:event">Events、iApplicationObjectExtension 与 DBObject 方法&lt;/a>&lt;/li>
&lt;li>如何&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:event">用事件替换旧方案&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="事件监听器">事件监听器&lt;/h2>
&lt;p>为不同的 CRUD 事件注册监听器，可以在 ORM 操作期间添加功能。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>事件&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>创建/更新&lt;/th>
&lt;th>删除&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>计算值&lt;/td>
&lt;td>对象修改时计算字段值（可在 CRUD 外调用）&lt;/td>
&lt;td>EVENT_DB_COMPUTE_VALUES&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入前&lt;/td>
&lt;td>操作开始前调用一次&lt;/td>
&lt;td>EVENT_DB_BEFORE_WRITE&lt;/td>
&lt;td>3.1.2 新增 EVENT_DB_ABOUT_TO_DELETE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入检查&lt;/td>
&lt;td>检查请求的操作对该对象是否有效&lt;/td>
&lt;td>EVENT_DB_CHECK_TO_WRITE&lt;/td>
&lt;td>EVENT_DB_CHECK_TO_DELETE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入后&lt;/td>
&lt;td>数据库操作已完成（状态 OK）&lt;/td>
&lt;td>EVENT_DB_AFTER_WRITE&lt;/td>
&lt;td>EVENT_DB_AFTER_DELETE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>链接修改&lt;/td>
&lt;td>创建/更新/删除 n-n 链接时，通知该链接指向的所有对象&lt;/td>
&lt;td>EVENT_DB_LINKS_CHANGED&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>注册事件可以通过多种方式完成：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:events_api">如何添加事件并监听以做出响应&lt;/a>。&lt;/p>
&lt;h2 id="类上的事件">类上的事件&lt;/h2>
&lt;p>要查找给定类上可用的完整事件列表，请进入 iTop 的数据模型，打开类并转到 &amp;ldquo;events&amp;rdquo; 选项卡。&lt;/p>
&lt;h2 id="示例">示例&lt;/h2>
&lt;ul>
&lt;li>教程：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:check-to-write">使用事件实现 CheckToWrite&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="各事件详解">各事件详解&lt;/h2>
&lt;h3 id="计算值compute-values">计算值（Compute Values）&lt;/h3>
&lt;p>对象修改时计算字段值（可在 CRUD 外调用）。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：&lt;code>EVENT_DB_COMPUTE_VALUES&lt;/code>&lt;/li>
&lt;li>&lt;strong>修改对象&lt;/strong>：可以修改当前对象，无需调用 &lt;code>DBWrite()&lt;/code> 保存修改，因为对象即将写入数据库&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'object'&lt;/code>：当前对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：当前对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>）&lt;/li>
&lt;li>&lt;strong>可用 API&lt;/strong>：
&lt;ul>
&lt;li>当前更改可通过 &lt;code>DBObject::ListChanges()&lt;/code> 获取&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="写入前before-write">写入前（Before Write）&lt;/h3>
&lt;p>在请求的操作开始前。在此阶段，可以修改当前对象。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>EVENT_DB_BEFORE_WRITE&lt;/code>&lt;/li>
&lt;li>&lt;code>EVENT_DB_ABOUT_TO_DELETE&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>修改对象&lt;/strong>：
&lt;ul>
&lt;li>对于&lt;strong>创建&lt;/strong>和&lt;strong>更新&lt;/strong>：可以修改对象值&lt;/li>
&lt;li>对于&lt;strong>删除&lt;/strong>：对象保证会从数据库中删除。它仍然可用，但只读&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'is_new'&lt;/code>：如果对象正在创建&lt;/li>
&lt;li>&lt;code>'object'&lt;/code>：当前对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：当前对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>）&lt;/li>
&lt;/ul>
&lt;h3 id="写入检查check-to-write">写入检查（Check to Write）&lt;/h3>
&lt;p>检查请求的操作对该对象是否有效。这可以在 CRUD 之外由控制台调用以检查唯一性规则。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>EVENT_DB_CHECK_TO_WRITE&lt;/code>&lt;/li>
&lt;li>&lt;code>EVENT_DB_CHECK_TO_DELETE&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>修改对象&lt;/strong>：
&lt;ul>
&lt;li>此时&lt;strong>不得&lt;/strong>修改对象&lt;/li>
&lt;li>尝试对任何对象字段调用 &lt;code>Set()&lt;/code> 将抛出 &lt;code>CoreException&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'object'&lt;/code>：当前对象&lt;/li>
&lt;li>&lt;code>'is_new'&lt;/code>：如果对象正在创建&lt;/li>
&lt;li>&lt;code>'deletion_plan'&lt;/code>：当前 &lt;code>DeletionPlan&lt;/code>，用于 &lt;code>EVENT_DB_CHECK_TO_DELETE&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：当前对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>）&lt;/li>
&lt;li>&lt;strong>可用 API&lt;/strong>：
&lt;ul>
&lt;li>当前更改可通过 &lt;code>DBObject::ListChanges()&lt;/code> 获取&lt;/li>
&lt;li>可以使用 &lt;code>DBObject::AddCheckIssue()&lt;/code> 或 &lt;code>DBObject::AddDeleteIssue()&lt;/code> 报告问题，这将阻止对象写入或从数据库删除&lt;/li>
&lt;li>可以使用 &lt;code>DBObject::AddCheckWarning()&lt;/code> 报告警告，这&lt;strong>不会&lt;/strong>阻止对象写入或从数据库删除&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="写入后after-write">写入后（After Write）&lt;/h3>
&lt;p>请求的操作已在数据库中完成。在此阶段，可以将修改传播到其他对象。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>EVENT_DB_AFTER_WRITE&lt;/code>&lt;/li>
&lt;li>&lt;code>EVENT_DB_AFTER_DELETE&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>修改对象&lt;/strong>：
&lt;ul>
&lt;li>对于&lt;strong>创建&lt;/strong>和&lt;strong>更新&lt;/strong>：可以再次修改对象值，无需调用 &lt;code>DBWrite()&lt;/code> 保存修改，因为如果在此阶段修改了对象，将调用延迟的 &lt;code>DBUpdate()&lt;/code>。在这种情况下，将重新运行完整的&lt;strong>更新&lt;/strong>过程（包括所有事件）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'changes'&lt;/code>：本次更新期间所做的更改数组（用于&lt;strong>更新&lt;/strong>）&lt;/li>
&lt;li>&lt;code>'is_new'&lt;/code>：如果对象正在创建&lt;/li>
&lt;li>&lt;code>'object'&lt;/code>：当前对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：当前对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>）&lt;/li>
&lt;li>&lt;strong>可用 API&lt;/strong>：
&lt;ul>
&lt;li>对于&lt;strong>更新&lt;/strong>，当前更改也可通过 &lt;code>ListPreviousValuesForUpdatedAttributes()&lt;/code> 获取&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="链接修改link-modified">链接修改（Link Modified）&lt;/h3>
&lt;p>当创建、更新或删除 n-n 链接时，所有链接的对象（以及修改情况下之前链接的对象）都会收到事件通知。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：&lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>&lt;/li>
&lt;li>&lt;strong>修改对象&lt;/strong>：
&lt;ul>
&lt;li>此时可以修改对象，需要调用 &lt;code>DBUpdate()&lt;/code> 来持久化修改&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'object'&lt;/code>：链接的对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：链接对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>）&lt;/li>
&lt;/ul>
&lt;h2 id="applystimulus-相关事件">ApplyStimulus 相关事件&lt;/h2>
&lt;h3 id="事件">事件&lt;/h3>
&lt;p>&lt;strong>3.1.2 新增&lt;/strong>&lt;/p>
&lt;p>管理允许的转换。仅允许减少当前对象状态中允许的转换。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>事件&lt;/strong>：&lt;code>EVENT_ENUM_TRANSITIONS&lt;/code>&lt;/li>
&lt;li>&lt;strong>事件数据&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>'object'&lt;/code>：刺激目标的对象&lt;/li>
&lt;li>&lt;code>'allowed_stimuli'&lt;/code>：当前状态中可用的刺激列表&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>事件源&lt;/strong>：当前对象的所有父类（直到 &lt;code>cmdbAbstractObject&lt;/code>），具有生命周期&lt;/li>
&lt;li>&lt;strong>可用 API&lt;/strong>：
&lt;ul>
&lt;li>唯一可能的操作是使用 &lt;code>DBObject::DenyTransition()&lt;/code> 拒绝转换&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:extensibility:events_crud">https://www.itophub.io/wiki/page?id=3_2_0:extensibility:events_crud&lt;/a>&lt;/p>
&lt;p>版本：3_2_0/extensibility/events_crud.txt · Last modified: 2025/05/19 16:06 by 127.0.0.1&lt;/p></description></item><item><title>Docs: CRUD 序列</title><link>/docs/customization/your-extension/sequence-crud/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/sequence-crud/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档详细描述 iTop ORM 中 CRUD 操作（创建、更新、删除）的方法调用序列。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:sequence_crud">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>这是 iTop ORM CRUD 栈（创建、更新、删除操作）中方法调用的映射。要了解更多关于扩展性的信息，请参阅&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">扩展性 API&lt;/a>。&lt;/p>
&lt;h3 id="对象层次结构">对象层次结构&lt;/h3>
&lt;p>iTop 核心中有 3 个类可以扩展用于新的 iTop 对象：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>DBObject&lt;/strong>：数据库中的持久化对象&lt;/li>
&lt;li>&lt;strong>CMDBObject&lt;/strong>：添加历史跟踪&lt;/li>
&lt;li>&lt;strong>cmdbAbstractObject&lt;/strong>：可编辑对象&lt;/li>
&lt;/ul>
&lt;p>所有默认数据模型对象都是 &lt;code>cmdbAbstractObject&lt;/code> 的子类。&lt;/p>
&lt;h3 id="栈修改历史">栈修改历史&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>2.7.0&lt;/strong>：事务（参见：N°679, N°2456）&lt;/li>
&lt;li>&lt;strong>3.1.0&lt;/strong>：事件（N°4756, N°5906）&lt;/li>
&lt;/ul>
&lt;h2 id="图例说明">图例说明&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>粗体&lt;/strong>：可覆盖的回调（在代码中标记为 &lt;code>overwritable-hook&lt;/code> PHPDoc 标签）&lt;/li>
&lt;li>&lt;strong>红色&lt;/strong>：直接对数据库执行的操作&lt;/li>
&lt;li>&lt;strong>蓝色&lt;/strong>：父方法&lt;/li>
&lt;li>&lt;strong>绿色&lt;/strong>：事件&lt;/li>
&lt;li>&lt;strong>紫色&lt;/strong>：对象访问修改&lt;/li>
&lt;/ul>
&lt;h2 id="dbinsert创建对象">DBInsert（创建对象）&lt;/h2>
&lt;p>从 &lt;code>DBObject::DBInsert&lt;/code> 开始：&lt;/p>
&lt;ol>
&lt;li>&lt;code>cmdbAbstractObject::DBInsert&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DBInsertNoReload&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DoComputeValues&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventComputeValues&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_COMPUTE_VALUES)&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>DBObject::ComputeValues&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::OnInsert&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventBeforeWrite&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' =&amp;gt; true])&lt;/code>（自 3.1.0 N°6324）&lt;/li>
&lt;li>&lt;code>DBObject::CheckToWrite&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>MetaModel::SkipCheckToWrite&lt;/code> 则返回&lt;/li>
&lt;li>&lt;code>DBObject::SetReadOnly&lt;/code>：从此处起不允许 &lt;code>DBObject::Set&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventCheckToWrite&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' =&amp;gt; true])&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>DBObject::SetReadWrite&lt;/code>：从此处起允许 &lt;code>DBObject::Set&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::DoCheckToWrite&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>CheckToWrite&lt;/code> 返回 false，则抛出 &lt;code>CoreCannotSaveObjectException&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::GetState&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>MetaModel::ListAttributeDefs&lt;/code>，如果 &lt;code>$oAttDef instanceof AttributeStopWatch&lt;/code>
&lt;ul>
&lt;li>&lt;code>ormStopWatch::Start&lt;/code>&lt;/li>
&lt;li>&lt;code>ormStopWatch::ComputeDeadlines&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::Set&lt;/code>：设置具有修改值的属性&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('START TRANSACTION')&lt;/code>（自 2.7.0 N°679）&lt;/li>
&lt;li>&lt;code>DBObject::DBInsertSingleTable($sRootClass)&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DBInsertSingleTable($sClass)&lt;/code>：在叶类上调用插入&lt;/li>
&lt;li>遍历 &lt;code>MetaModel::EnumParentClasses&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::DBInsertSingleTable($sParentClass)&lt;/code>：在类层次结构上调用插入&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::OnObjectKeyReady&lt;/code>&lt;/li>
&lt;li>&lt;code>InlineImage::FinalizeInlineImages&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DBWriteLinks&lt;/code>
&lt;ul>
&lt;li>遍历每个修改的 linkset&lt;/li>
&lt;li>&lt;code>ormLinkSet::DBWrite&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::WriteExternalAttributes&lt;/code>
&lt;ul>
&lt;li>&lt;code>\AttributeCustomFields::WriteExternalValues&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>CMDBObject::RecordObjCreation&lt;/code>
&lt;ul>
&lt;li>删除删除记录&lt;/li>
&lt;li>删除 2.0.3 之前遗留的任何其他更改跟踪信息&lt;/li>
&lt;li>&lt;code>DBObject::RecordObjCreation&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::RecordLinkSetListChange&lt;/code>&lt;/li>
&lt;li>创建新的 &lt;code>CMDBChangeOpCreate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('COMMIT')&lt;/code>（异常时 ROLLBACK）&lt;/li>
&lt;li>重置元信息：&lt;code>m_bIsInDB = true&lt;/code>，&lt;code>m_bDirty = false&lt;/code>，设置 &lt;code>m_aOrigValues[$sAttCode]&lt;/code>&lt;/li>
&lt;li>&lt;code>MetaModel::StartReentranceProtection()&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::PostInsertActions&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::PostInsertActions&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventAfterWrite&lt;/code>&lt;/li>
&lt;li>&lt;code>\cmdbAbstractObject::NotifyAttachedObjectsOnLinkClassModification&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;li>&lt;code>\cmdbAbstractObject::FireEventDbLinksChangedForCurrentObject&lt;/code> - &lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_DB_AFTER_WRITE, ['changes' =&amp;gt; [], 'is_new' =&amp;gt; true])&lt;/code>（自 3.1.0 N°6324）&lt;/li>
&lt;li>&lt;code>DBObject::AfterInsert&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>搜索触发器：
&lt;ul>
&lt;li>&lt;code>$sClassList = MetaModel::EnumParentClasses&lt;/code>&lt;/li>
&lt;li>&lt;code>SELECT TriggerOnObjectCreate AS t WHERE t.target_class IN ('$sClassList')&lt;/code>&lt;/li>
&lt;li>遍历查询结果：&lt;code>Trigger::DoActivate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::ActivateOnMentionTriggers&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>iApplicationObjectExtension&lt;/code>（在 &lt;code>cmdbAbstractObject::DBInsert&lt;/code> 中）
&lt;ul>
&lt;li>&lt;code>iApplicationObjectExtension::OnDBInsert&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>MetaModel::StopReentranceProtection(REENTRANCE_TYPE_UPDATE)&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>如果对象被修改：&lt;code>DBObject::DBUpdate&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>finally&lt;/code>：如果 &lt;code>static::IsCrudStackEmpty()&lt;/code>
&lt;ul>
&lt;li>&lt;code>\cmdbAbstractObject::FireEventDbLinksChangedForAllObjects&lt;/code> - &lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>返回 &lt;code>DBObject::$m_iKey&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="dbupdate更新对象">DBUpdate（更新对象）&lt;/h2>
&lt;ol>
&lt;li>&lt;code>cmdbAbstractObject::DBUpdate&lt;/code>&lt;/li>
&lt;li>如果没有更改则返回&lt;/li>
&lt;li>&lt;code>DBObject::DBUpdate&lt;/code>&lt;/li>
&lt;li>&lt;code>MetaModel::StartReentranceProtection&lt;/code>（已在更新中则退出）（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>DBObject::DoComputeValues&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventComputeValues&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_COMPUTE_VALUES)&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>DBObject::ComputeValues&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::GetState&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>$sState != ''&lt;/code>，遍历 &lt;code>MetaModel::ListAttributeDefs&lt;/code>
&lt;ul>
&lt;li>如果 &lt;code>$oAttDef instanceof AttributeStopWatch&lt;/code>：&lt;code>ormStopWatch::ComputeDeadlines&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::OnUpdate&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventBeforeWrite&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' =&amp;gt; false])&lt;/code>（自 3.1.0 N°6324）&lt;/li>
&lt;li>&lt;code>DBObject::InitPreviousValuesForUpdatedAttributes&lt;/code>（自 2.7.0 N°2293）&lt;/li>
&lt;li>将 &lt;code>$this-&amp;gt;ListChanges()&lt;/code> 保存到 &lt;code>$aChanges&lt;/code>&lt;/li>
&lt;li>冻结更改&lt;/li>
&lt;li>如果 &lt;code>count($this-&amp;gt;ListChanges()) == 0&lt;/code>
&lt;ul>
&lt;li>&lt;code>MetaModel::StopReentranceProtection(REENTRANCE_TYPE_UPDATE)&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>返回 &lt;code>DBObject::$m_iKey&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::CheckToWrite&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>MetaModel::SkipCheckToWrite&lt;/code> 则返回&lt;/li>
&lt;li>&lt;code>DBObject::SetReadOnly&lt;/code>：从此处起不允许 &lt;code>DBObject::Set&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventCheckToWrite&lt;/code> - &lt;code>EventService::FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' =&amp;gt; false])&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>DBObject::SetReadWrite&lt;/code>：从此处起允许 &lt;code>DBObject::Set&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::DoCheckToWrite&lt;/code>&lt;/li>
&lt;li>如果返回 false，则抛出 &lt;code>CoreCannotSaveObjectException&lt;/code>&lt;/li>
&lt;li>保存原始值：&lt;code>DBObject::$m_aOrigValues&lt;/code> 到 &lt;code>$aOriginalValues&lt;/code> 变量&lt;/li>
&lt;li>从 &lt;code>$aChanges&lt;/code> 中为 &lt;code>\AttributeDefinition::IsBasedOnDBColumns == true&lt;/code> 的字段设置 &lt;code>$aDBChanges&lt;/code>&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('START TRANSACTION')&lt;/code>（自 2.7.0 N°679）&lt;/li>
&lt;li>如果 &lt;code>!MetaModel::DBIsReadOnly()&lt;/code>
&lt;ul>
&lt;li>更新每个层次键的左右索引&lt;/li>
&lt;li>更新标量属性&lt;/li>
&lt;li>使用当前类名和 id 创建新的 &lt;code>DBObjectSearch&lt;/code>，并 &lt;code>AllowAllData()&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObjectSearch::MakeUpdateQuery&lt;/code> 使用 &lt;code>$aDBChanges&lt;/code>&lt;/li>
&lt;li>&lt;code>\CMDBSource::Query&lt;/code> 执行更新查询&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::DBWriteLinks&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::WriteExternalAttributes&lt;/code>
&lt;ul>
&lt;li>&lt;code>\AttributeCustomFields::WriteExternalValues&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>属性重置：&lt;code>m_bDirty&lt;/code>（设为 false），&lt;code>m_aTouchedAtt&lt;/code>，&lt;code>m_aModifiedAtt&lt;/code> =&amp;gt; 这导致 &lt;code>ListChanges()&lt;/code> 返回空数组&lt;/li>
&lt;li>&lt;code>DBObject::RecordAttChanges&lt;/code> 使用 &lt;code>$aChanges&lt;/code>&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('COMMIT')&lt;/code>（异常时 ROLLBACK）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::PostUpdateActions&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::PostUpdateActions&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventAfterWrite&lt;/code>&lt;/li>
&lt;li>&lt;code>\cmdbAbstractObject::NotifyAttachedObjectsOnLinkClassModification&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;li>&lt;code>\cmdbAbstractObject::FireEventDbLinksChangedForCurrentObject&lt;/code> - &lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_DB_AFTER_WRITE, ['changes' =&amp;gt; $aChanges, 'is_new' =&amp;gt; false])&lt;/code>（自 3.1.0 N°6324）&lt;/li>
&lt;li>&lt;code>DBObject::AfterUpdate&lt;/code>（自 2.7.0 N°2293，更改可使用 &lt;code>DBObject::ListPreviousValuesForUpdatedAttributes&lt;/code> 访问）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>重置 &lt;code>DBObject::$m_aOrigValues&lt;/code>&lt;/li>
&lt;li>&lt;code>TriggerOnObjectUpdate&lt;/code>：遍历使用当前类或其父类的所有触发器
&lt;ul>
&lt;li>&lt;code>TriggerOnObjectUpdate::DoActivate&lt;/code>（自 3.0.0 N°3245 和 3.0.2 N°5389，&lt;code>TriggerOnObjectUpdate&lt;/code> 移至数据库更新之后）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>重入调用（N°1073）：&lt;code>unset(DBObject.aUpdateReentrance[$sKey])&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::ActivateOnMentionTriggers&lt;/code>&lt;/li>
&lt;li>搜索 &lt;code>iApplicationObjectExtension&lt;/code> 实现
&lt;ul>
&lt;li>&lt;code>iApplicationObjectExtension::OnDBUpdate&lt;/code>（自 2.7.0 N°2293，更改可使用 &lt;code>DBObject::ListPreviousValuesForUpdatedAttributes&lt;/code> 访问）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>MetaModel::StopReentranceProtection()&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>如果对象被修改且不是无限循环（自 3.1.0）：&lt;code>DBObject::DBUpdate&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>返回 &lt;code>$this-&amp;gt;m_iKey&lt;/code>&lt;/li>
&lt;li>&lt;code>$this-&amp;gt;SetWarningsAsSessionMessages('update')&lt;/code>&lt;/li>
&lt;li>&lt;code>finally&lt;/code>：如果 &lt;code>static::IsCrudStackEmpty()&lt;/code>
&lt;ul>
&lt;li>&lt;code>\cmdbAbstractObject::FireEventDbLinksChangedForAllObjects&lt;/code> - &lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>返回 &lt;code>$res&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="dbdelete删除对象">DBDelete（删除对象）&lt;/h2>
&lt;ol>
&lt;li>&lt;code>cmdbAbstractObject::DBDelete&lt;/code>&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::DBDeleteTracked_Internal&lt;/code>&lt;/li>
&lt;li>搜索 &lt;code>iApplicationObjectExtension&lt;/code> 实现
&lt;ul>
&lt;li>&lt;code>iApplicationObjectExtension::OnDBDelete&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>CMDBObject::DBDeleteTracked_Internal&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DBDelete&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::MakeDeletionPlan&lt;/code>&lt;/li>
&lt;li>&lt;code>DeletionPlan::AddToDelete&lt;/code>：将当前对象添加到待删除列表&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_DB_CHECK_TO_DELETE, ['deletion_plan' =&amp;gt; $oDeletionPlan])&lt;/code>（自 3.1.0 N°4756）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::DoCheckToDelete&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DoCheckToDelete&lt;/code>（@internal，但具有 DeletionPlan 参数）&lt;/li>
&lt;li>如果 &lt;code>$this-&amp;gt;InSyncScope()&lt;/code> 则执行某些操作&lt;/li>
&lt;li>&lt;code>iApplicationObjectExtension::OnCheckToDelete&lt;/code>（可覆盖的钩子，但无法访问 DeletionPlan 实例）&lt;/li>
&lt;li>&lt;code>UserRights::IsActionAllowed&lt;/code>&lt;/li>
&lt;li>&lt;code>DeletionPlan::SetDeletionIssues&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::GetReferencingObjects&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>$aDependentObjects&lt;/code>
&lt;ul>
&lt;li>&lt;code>DeletionPlan::AddToUpdate&lt;/code>：如果 ext key 有 &lt;code>is_null_allowed=true&lt;/code> 参数&lt;/li>
&lt;li>&lt;code>DBObject::MakeDeletionPlan&lt;/code>：否则&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DeletionPlan::ComputeResults&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>$oDeletionPlan-&amp;gt;FoundStopper()&lt;/code>：抛出 &lt;code>DeleteException&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>$oDeletionPlan-&amp;gt;ListDeletes()&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::DBDeleteSingleObject&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::SetReadOnly&lt;/code>：从此处起不允许 &lt;code>DBObject::Set&lt;/code>（自 3.1.2 N°7118）&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_DB_ABOUT_TO_DELETE)&lt;/code>（自 3.1.2 N°7118）&lt;/li>
&lt;li>&lt;code>DBObject::OnDelete&lt;/code>&lt;/li>
&lt;li>&lt;code>$aParams = array('class_list' =&amp;gt; MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL))&lt;/code>&lt;/li>
&lt;li>&lt;code>SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)&lt;/code>
&lt;ul>
&lt;li>遍历查询结果：&lt;code>Trigger::DoActivate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>CMDBObject::RecordObjDeletion&lt;/code>
&lt;ul>
&lt;li>删除 &lt;code>CMDBChangeOp&lt;/code> 对象&lt;/li>
&lt;li>&lt;code>DBObject::RecordObjDeletion&lt;/code>&lt;/li>
&lt;li>&lt;code>\DBObject::RecordLinkSetListChange&lt;/code>&lt;/li>
&lt;li>创建并持久化新的 &lt;code>CMDBChangeOpDelete&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>遍历 &lt;code>MetaModel::ListAttributeDefs(get_class($this))&lt;/code>
&lt;ul>
&lt;li>如果 &lt;code>$oAttDef-&amp;gt;IsHierarchicalKey()&lt;/code>：更新每个层次键的左右索引&lt;/li>
&lt;li>否则如果 &lt;code>!$oAttDef-&amp;gt;LoadFromDB()&lt;/code>：&lt;code>AttributeCustomFields::DeleteValue&lt;/code>&lt;/li>
&lt;li>&lt;code>TemplateFieldsHandler::DeleteValues&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::DBDelete&lt;/code>&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('START TRANSACTION')&lt;/code>（自 2.7.0 N°679）&lt;/li>
&lt;li>遍历 &lt;code>MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL)&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::DBDeleteSingleTable&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>CMDBSource::Query('COMMIT')&lt;/code>（异常时 ROLLBACK）&lt;/li>
&lt;li>&lt;code>cmdbAbstractObject::FireEventAfterDelete&lt;/code>
&lt;ul>
&lt;li>&lt;code>\cmdbAbstractObject::NotifyAttachedObjectsOnLinkClassModification&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_DB_AFTER_DELETE)&lt;/code>（自 3.1.0 N°6324）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>DBObject::AfterDelete&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>$oDeletionPlan-&amp;gt;ListUpdates()&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::Set&lt;/code>：重置 ext key&lt;/li>
&lt;li>&lt;code>DBObject::DBUpdate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>finally&lt;/code>：如果 &lt;code>static::IsCrudStackEmpty()&lt;/code>
&lt;ul>
&lt;li>&lt;code>\cmdbAbstractObject::FireEventDbLinksChangedForAllObjects&lt;/code> - &lt;code>EVENT_DB_LINKS_CHANGED&lt;/code>（自 3.1.0 N°5906）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="applystimulus应用刺激">ApplyStimulus（应用刺激）&lt;/h2>
&lt;p>没有重载，起始点在 DBObject 中。&lt;/p>
&lt;ol>
&lt;li>&lt;code>DBObject::ApplyStimulus&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::EnumTransitions()&lt;/code>&lt;/li>
&lt;li>&lt;code>EventService::FireEvent(EVENT_ENUM_TRANSITIONS)&lt;/code>（自 3.1.2）&lt;/li>
&lt;li>如果刺激在当前状态无效果：返回 true&lt;/li>
&lt;li>&lt;code>DBObject::Set&lt;/code>：设置新状态&lt;/li>
&lt;li>遍历 &lt;code>$aTransitionDef['actions']&lt;/code>
&lt;ul>
&lt;li>如果 &lt;code>is_string($actionHandler)&lt;/code>：&lt;code>call_user_func($aActionCallSpec, $sStimulusCode)&lt;/code>（pref 2.1.0 模块：直接调用方法）&lt;/li>
&lt;li>否则遍历 &lt;code>$aActionHandler['params']&lt;/code>
&lt;ul>
&lt;li>根据参数类型设置 &lt;code>$value&lt;/code>&lt;/li>
&lt;li>&lt;code>$bRet = call_user_func_array($aCallSpec, $aParams)&lt;/code>&lt;/li>
&lt;li>如果 &lt;code>$bRet === false&lt;/code>：&lt;code>IssueLog::Info&lt;/code> + &lt;code>$bSuccess = false&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>如果 &lt;code>$bSuccess&lt;/code>
&lt;ul>
&lt;li>秒表：启动或停止&lt;/li>
&lt;li>如果 &lt;code>!$bDoNotWrite&lt;/code>：&lt;code>DBObject::DBWrite&lt;/code>
&lt;ul>
&lt;li>如果 &lt;code>$this-&amp;gt;m_bIsInDB&lt;/code>：&lt;code>DBObject::DBUpdate&lt;/code>&lt;/li>
&lt;li>否则：&lt;code>DBObject::DBInsert&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>搜索当前类及其父类的所有 &lt;code>TriggerOnStateLeave&lt;/code>
&lt;ul>
&lt;li>&lt;code>Trigger::DoActivate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>搜索当前类及其父类的所有 &lt;code>TriggerOnStateEnter&lt;/code>
&lt;ul>
&lt;li>&lt;code>Trigger::DoActivate&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>返回 &lt;code>$bSuccess&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="dochecktowrite">DoCheckToWrite&lt;/h2>
&lt;ol>
&lt;li>&lt;code>cmdbAbstractObject::DoCheckToWrite&lt;/code>：&lt;strong>警告&lt;/strong>不为空，覆盖时不要忘记调用父类！&lt;/li>
&lt;li>&lt;code>\DBObject::DoCheckToWrite&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DoComputeValues&lt;/code>&lt;/li>
&lt;li>设置未初始化的 &amp;ldquo;null-not-allowed&amp;rdquo; 日期时间（和日期）&lt;/li>
&lt;li>&lt;code>DBObject::ComputeValues&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::DoCheckUniqueness&lt;/code>（参见专门章节）&lt;/li>
&lt;li>&lt;code>DBObject::ListChanges&lt;/code>&lt;/li>
&lt;li>遍历 &lt;code>$aChanges&lt;/code>
&lt;ul>
&lt;li>&lt;code>DBObject::CheckValue&lt;/code>&lt;/li>
&lt;li>&lt;code>DBObject::CheckConsistency&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>如果 &lt;code>DBObject::$m_bIsInDB &amp;amp;&amp;amp; DBObject::InSyncScope &amp;amp;&amp;amp; (count($aChanges) &amp;gt; 0)&lt;/code>
&lt;ul>
&lt;li>为每个因 datasynchro 锁定（&lt;code>OPT_ATT_SLAVE&lt;/code>）的属性在 &lt;code>DBObject::$m_aCheckIssues&lt;/code> 中添加错误&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>遍历 &lt;code>iApplicationObjectExtension&lt;/code>
&lt;ul>
&lt;li>&lt;code>iApplicationObjectExtension::OnCheckToWrite&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>如果 &lt;code>!$this-&amp;gt;bAllowWrite&lt;/code>
&lt;ul>
&lt;li>&lt;code>UserRights::IsActionAllowedOnAttribute&lt;/code>：检查每个更改是否允许（如果不允许，将错误添加到 &lt;code>DBObject::$m_aCheckIssues&lt;/code>）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:sequence_crud">https://www.itophub.io/wiki/page?id=3_2_0:customization:sequence_crud&lt;/a>&lt;/p>
&lt;p>2025/01/13 13:29 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/sequence_crud.txt · Last modified: 2025/01/13 13:29 by 127.0.0.1&lt;/p></description></item><item><title>Docs: Events API</title><link>/docs/customization/your-extension/events-api/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/events-api/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 iTop 3.1 引入的 Events API，这是一种通过事件机制扩展 iTop 功能的全新方式。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:events_api">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>Events 服务是 iTop 3.1 中的新功能，属于&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">扩展性 API&lt;/a> 的一部分。&lt;/p>
&lt;p>该服务通过事件提供扩展性。主要行为是&lt;strong>事件提供者&lt;/strong>注册他们要触发的事件，&lt;strong>事件监听器&lt;/strong>注册当事件触发时要调用的回调函数。&lt;/p>
&lt;h2 id="示例">示例&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:count-linkset-indirect">存储 n:n 关系计数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:add-log-entry">添加附件时添加日志条目&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="事件发布者">事件发布者&lt;/h2>
&lt;h3 id="注册">注册&lt;/h3>
&lt;p>提供者必须在 PHP 中触发事件之前，先在 XML 中注册事件。注册需要：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>event&lt;/strong>：大致上是一个唯一的名称&lt;/li>
&lt;li>&lt;strong>event_data&lt;/strong>：与事件一起提供的参数&lt;/li>
&lt;li>&lt;strong>sources&lt;/strong>：监听器可用的过滤条件，可选且特定于事件&lt;/li>
&lt;/ul>
&lt;h4 id="事件-id">事件 ID&lt;/h4>
&lt;p>事件 ID 应遵循常量命名规范（即全大写，仅使用下划线字符），并建议事件名称包含 &amp;lsquo;EVENT&amp;rsquo;。&lt;/p>
&lt;ul>
&lt;li>当然，它在 iTop 和所有扩展中必须是唯一的&lt;/li>
&lt;li>在以下示例中，我们将事件命名为 &lt;code>EVENT_DOWNLOAD_DOCUMENT&lt;/code>&lt;/li>
&lt;li>将自动创建一个与名称相同的字符串常量&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">EVENT_DOWNLOAD_DOCUMENT&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;EVENT_DOWNLOAD_DOCUMENT&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="事件数据">事件数据&lt;/h4>
&lt;p>事件数据由事件提供者在事件触发时提供给所有监听器。它是与事件一起提供的信息，以便监听器可以在有足够信息的情况下采取行动。&lt;/p>
&lt;ul>
&lt;li>数据特定于提供者，由扁平的值列表组成&lt;/li>
&lt;li>值可以是任何 PHP 值，如对象或数组，必须在声明中指定&lt;/li>
&lt;li>事件数据及其类型在提供者事件注册中声明&lt;/li>
&lt;li>它们特定于每个事件&lt;/li>
&lt;/ul>
&lt;p>在以下示例中，声明了 5 个 event_datum：&lt;/p>
&lt;ul>
&lt;li>&lt;code>object&lt;/code>：DBObject&lt;/li>
&lt;li>&lt;code>document&lt;/code>：ormDocument&lt;/li>
&lt;li>&lt;code>att_code&lt;/code> 和 &lt;code>content_disposition&lt;/code>：字符串&lt;/li>
&lt;li>&lt;code>debug_info&lt;/code>：字符串，在 Combodo 创建的事件中始终存在&lt;/li>
&lt;/ul>
&lt;h4 id="事件源">事件源&lt;/h4>
&lt;p>事件源是一种根据事件来源过滤事件的方式。&lt;/p>
&lt;p>例如，对于数据模型对象上的事件，对象的类是监听器可以使用的源。实际上，所有父类都被添加到事件源中，因此监听器可以在类树的任何级别过滤事件。监听器可以指定监听 &amp;ldquo;Ticket&amp;rdquo; 事件。在继承自 Ticket 的对象上触发的所有事件都将发送给此监听器。&lt;/p>
&lt;h4 id="xml-结果">XML 结果&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;events&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;EVENT_DOWNLOAD_DOCUMENT&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>A document has been downloaded from the GUI&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;sources&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;source&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;Document&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>Document&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/source&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/sources&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_data&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_datum&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;object&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>The object containing the document&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>DBObject&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_datum&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_datum&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;document&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>The document downloaded&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>ormDocument&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_datum&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_datum&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;att_code&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>Attribute code of the object, containing the document&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>string&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_datum&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_datum&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;content_disposition&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>To differentiate if the document was displayed in preview or truly downloaded&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>string&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_datum&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_datum&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;debug_info&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;description&amp;gt;&lt;/span>Debug string&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>string&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_datum&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_data&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/events&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="所有-itop-事件">所有 iTop 事件&lt;/h4>
&lt;ul>
&lt;li>所有 iTop 核心事件声明可以在 &lt;code>&amp;lt;itop&amp;gt;/code/datamodel.application.xml&lt;/code> 中找到&lt;/li>
&lt;li>当然，这不包括由 iTop 扩展带来的事件&lt;/li>
&lt;/ul>
&lt;h3 id="事件触发">事件触发&lt;/h3>
&lt;p>此部分用 PHP 编写。由事件提供者在适合触发事件时执行。必须提供：&lt;/p>
&lt;ul>
&lt;li>与注册中声明的 &lt;code>id&lt;/code> 和 &lt;code>type&lt;/code> 一致的事件数据&lt;/li>
&lt;li>适用的事件源&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">static&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">DownloadDocument&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">WebPage&lt;/span> &lt;span style="color:#000">$oPage&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sClass&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$id&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sContentDisposition&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;attachment&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sSecretField&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87;font-weight:bold">null&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sSecretValue&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87;font-weight:bold">null&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">$aEventData&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87;font-weight:bold">array&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;debug_info&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$oDocument&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">GetFileName&lt;/span>&lt;span style="color:#000;font-weight:bold">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$oObj&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;document&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$oDocument&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;att_code&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;content_disposition&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$sContentDisposition&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$aEventSources&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000;font-weight:bold">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">foreach&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">MetaModel&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">EnumParentClasses&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">get_class&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#000">ENUM_PARENT_CLASSES_ALL&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87;font-weight:bold">false&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">$sClass&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$aEventSources&lt;/span>&lt;span style="color:#000;font-weight:bold">[]&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$sClass&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Combodo\iTop\Service\Events\EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">FireEvent&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">new&lt;/span> &lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">EVENT_DOWNLOAD_DOCUMENT&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$aEventSources&lt;/span> &lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$aEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="事件监听器">事件监听器&lt;/h2>
&lt;p>监听器将指定，如果特定事件在其指定的过滤器匹配的源上触发，则执行特定的 PHP 代码。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>event&lt;/strong>：事件的字符串名称&lt;/li>
&lt;li>&lt;strong>callback&lt;/strong>：匹配过滤条件的事件发生时执行的代码&lt;/li>
&lt;li>&lt;strong>filter&lt;/strong>：一组字符串。如果事件的 &lt;code>$aEventSources&lt;/code> 中有&lt;strong>任何一个&lt;/strong>过滤字符串，则执行回调代码。对于许多触发器，这些字符串对应于类名。此参数可选。&lt;/li>
&lt;li>&lt;strong>rank&lt;/strong>：如果您不是唯一响应该事件的扩展，可以指定一个 rank，以确保在另一个您知道其 rank 的扩展之前或之后执行&lt;/li>
&lt;/ul>
&lt;p>注册可以通过多种方式完成。&lt;/p>
&lt;h3 id="在类中的-xml-方式">在类中的 XML 方式&lt;/h3>
&lt;p>您可以在 &lt;code>UserRequest&lt;/code> 类中定义一个监听器，为此指定：&lt;/p>
&lt;ul>
&lt;li>&lt;code>event_listener id&lt;/code>：给它任何您喜欢的名称，在类中必须唯一&lt;/li>
&lt;li>&lt;code>event&lt;/code>：在 XML 标签 &lt;code>event&lt;/code> 下，如 &lt;code>EVENT_DB_AFTER_WRITE&lt;/code>&lt;/li>
&lt;li>&lt;code>callback&lt;/code>：在 XML 标签 &lt;code>callback&lt;/code> 下，提供此类中方法的名称&lt;/li>
&lt;li>&lt;code>rank&lt;/code>：在 XML 标签 &lt;code>rank&lt;/code> 下&lt;/li>
&lt;li>&lt;code>filter&lt;/code>：在集合 &lt;code>filters/filter&lt;/code> 下，但在类中声明时是隐式的&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;class&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;UserRequest&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_created_in=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;itop-request-mgmt&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;must_exist&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listener&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;OnUserRequestUpdateDone&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event&amp;gt;&lt;/span>EVENT_DB_AFTER_WRITE&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;callback&amp;gt;&lt;/span>OnUserRequestUpdateDone&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/callback&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;rank&amp;gt;&lt;/span>0&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/rank&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注册 &lt;code>EVENT_DB_AFTER_WRITE&lt;/code> 事件：&lt;/p>
&lt;ul>
&lt;li>类似于重写 &lt;code>UserRequest::OnUpdate&lt;/code>，但没有被另一个定制覆盖的风险，因为事件的所有监听器按顺序调用执行各自的工作&lt;/li>
&lt;li>类似于实现 &lt;code>iObjectApplicationExtension OnDBUpdate()&lt;/code>，但对于许多事件，等效的接口方法不存在，而且使用接口无法控制执行顺序&lt;/li>
&lt;/ul>
&lt;h3 id="在-xml-中使用直接代码">在 XML 中使用直接代码&lt;/h3>
&lt;p>此处 XML 声明在任何类之外，在 &lt;code>itop_design&lt;/code> XML 树的根级别。&lt;/p>
&lt;p>注意：&lt;/p>
&lt;ul>
&lt;li>需要 XML &lt;code>filter&lt;/code> 标签来声明适用的类&lt;/li>
&lt;li>如果 &lt;code>event&lt;/code> 对 &lt;code>filter&lt;/code> 中的类不适用，不会失败，不会显示警告，只是回调永远不会被调用&lt;/li>
&lt;li>方法没有名称&lt;/li>
&lt;li>您无法访问 &lt;code>$this&lt;/code>，当前 &lt;code>$oObject&lt;/code> 由事件提供（&lt;code>$oEventData-&amp;gt;Get('object')&lt;/code>）&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listener&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;OnAttachmentDownloadActivateTriggers&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event&amp;gt;&lt;/span>EVENT_DOWNLOAD_DOCUMENT&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;filters&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;filter&amp;gt;&lt;/span>Attachment&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/filter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/filters&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;rank&amp;gt;&lt;/span>0&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/rank&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;code&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> function(Combodo\iTop\Service\Events\EventData $oEventData)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> if ($oEventData-&amp;gt;Get(&amp;#39;content_disposition&amp;#39;) !== ormDocument::ENUM_CONTENT_DISPOSITION_ATTACHMENT) {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> return;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $oObject = $oEventData-&amp;gt;Get(&amp;#39;object&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $sAttCode = $oEventData-&amp;gt;Get(&amp;#39;att_code&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $oDocument = $oEventData-&amp;gt;Get(&amp;#39;document&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="回调">回调&lt;/h3>
&lt;p>如果您在 XML 中的类里注册了监听器，则在类上创建回调作为 PHP 方法。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnUserRequestUpdateDone&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sEvent&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">GetEvent&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$aChange&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;changes&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// 执行您的处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在回调代码中：&lt;/p>
&lt;ul>
&lt;li>您可以通过 &lt;code>$oEventData-&amp;gt;Get('xx')&lt;/code> 获取事件发布者注册的事件数据（其中 &amp;lsquo;xx&amp;rsquo; 是 XML 标签中 &amp;rsquo;event_datum&amp;rsquo; 的 &amp;lsquo;id&amp;rsquo;）&lt;/li>
&lt;li>在某些事件中，对象上可以使用标准方法，如 &lt;code>DBObject::AddCheckIssue()&lt;/code>、&lt;code>DBObject::ListChanges()&lt;/code> 等&lt;/li>
&lt;li>当事件和回调方法在类本身上声明时，可以使用 &lt;code>$this&lt;/code> 代替 &lt;code>$oEventData-&amp;gt;Get('object')&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="示例人员变更时">示例：人员变更时&lt;/h3>
&lt;p>在标准 iTop 中，我们通过此事件机制&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:form_prefill">防止门户用户在人员组织变更时失去访问权限&lt;/a>。&lt;/p>
&lt;h3 id="php-中的监听器">PHP 中的监听器&lt;/h3>
&lt;p>如果您在 PHP 中进行注册和回调，最简单的方式是声明一个实现 &lt;code>iEventServiceSetup&lt;/code> 的新类。在一个地方执行 &lt;code>RegisterEventsAndListeners()&lt;/code> 中的注册，并指定回调代码。&lt;/p>
&lt;p>监听器可以指定它们要监听的源。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">AttributeBlobEventListener&lt;/span> &lt;span style="color:#204a87;font-weight:bold">implements&lt;/span> &lt;span style="color:#000">iEventServiceSetup&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">RegisterEventsAndListeners&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EVENT_DOWNLOAD_DOCUMENT&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnAttributeBlobDownloadActivateTrigger&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnAttributeBlobDownloadActivateTrigger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">void&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// 回调代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">EventListener&lt;/span> &lt;span style="color:#204a87;font-weight:bold">implements&lt;/span> &lt;span style="color:#000">iEventServiceSetup&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">RegisterEventsAndListeners&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EVENT_DOWNLOAD_DOCUMENT&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnAttachmentDownloadActivateTriggers&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;Attachment&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnAttachmentDownloadActivateTriggers&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">void&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;content_disposition&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">!==&lt;/span> &lt;span style="color:#000">ormDocument&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">ENUM_CONTENT_DISPOSITION_ATTACHMENT&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sAttCode&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;att_code&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oDocument&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;document&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="现有事件">现有事件&lt;/h2>
&lt;p>要获取更多关于 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:extensibility:events_crud">CRUD 事件&lt;/a> 的详细信息：&lt;/p>
&lt;ul>
&lt;li>它们的名称和用法&lt;/li>
&lt;li>它们的数据&lt;/li>
&lt;li>回调代码中可调用的 DBObject 方法&lt;/li>
&lt;/ul>
&lt;h3 id="查找与数据模型类关联的事件">查找与数据模型类关联的事件&lt;/h3>
&lt;p>在数据模型页面中，可以查看为特定类注册的所有事件和监听器。&lt;/p>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:events_api">https://www.itophub.io/wiki/page?id=3_2_0:customization:events_api&lt;/a>&lt;/p>
&lt;p>版本：3_2_0/customization/events_api.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1&lt;/p></description></item><item><title>Docs: Interfaces API</title><link>/docs/customization/your-extension/extensions-api-interfaces/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/extensions-api-interfaces/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 iTop 的扩展性接口，这是定制 iTop 行为的一种方式。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>扩展性接口是定制 iTop 行为的一种方式。要了解其他方式，请查看&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">扩展性 API&lt;/a>。&lt;/p>
&lt;h2 id="接口参考文档">接口参考文档&lt;/h2>
&lt;p>有多个接口可用于扩展 iTop。每个接口对应一种特定类型的扩展。以下是一些示例：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>接口&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>iApplicationUIExtension&lt;/code>&lt;/td>
&lt;td>实现此接口以更改某些对象的 GUI 行为（显示详情或编辑对象时）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>iApplicationObjectExtension&lt;/code>&lt;/td>
&lt;td>在 CRUD 期间对对象执行特定操作的旧接口，已被事件处理取代&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>iPageUIBlockExtension&lt;/code>&lt;/td>
&lt;td>实现此接口以向任何 iTopWebPage 添加内容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>iPortalUIExtension&lt;/code>&lt;/td>
&lt;td>实现此接口以向任何门户页面添加内容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>iPopupMenuExtension&lt;/code>&lt;/td>
&lt;td>在控制台和门户中的 iTop 对象和列表上添加菜单项&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>iRestServiceProvider&lt;/code>&lt;/td>
&lt;td>向 REST API 添加动词&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些接口和抽象类定义在 &lt;code>application/applicationextension.inc.php&lt;/code> 文件中。&lt;/p>
&lt;p>参考文档：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">扩展的 API 参考&lt;/a>&lt;/p>
&lt;h2 id="ipopupmenuextension-示例">iPopupMenuExtension 示例&lt;/h2>
&lt;p>假设我们有一个基于 Web 的应用程序，提供关于服务器可用性的高级报告。我们希望提供一个超链接，以便最终用户可以快速从 iTop 中的服务器详情跳转到监控应用程序中的相应报告。&lt;/p>
&lt;p>一种可能的解决方案是在所有服务器的 &amp;ldquo;Actions&amp;rdquo; 弹出菜单中显示指向监控应用程序的超链接。&lt;/p>
&lt;p>实现方式是实现 &lt;code>iPopupMenuExtension&lt;/code> 接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MyPopupExtension&lt;/span> &lt;span style="color:#204a87;font-weight:bold">implements&lt;/span> &lt;span style="color:#000">iPopupMenuExtension&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">static&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">EnumItems&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$iMenuId&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$param&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$iMenuId&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">==&lt;/span> &lt;span style="color:#000">self&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">MENU_OBJDETAILS_ACTIONS&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$param&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#000">instanceof&lt;/span> &lt;span style="color:#000">Server&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sUID&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;MyPopupExtension-Monitoring&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// 确保每个菜单项都有唯一的 &amp;#34;ID&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">$sLabel&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;Monthly report&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sURL&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;http://myapp/show_report?server_fqdn=&amp;#39;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">$oObject&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;name&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sTarget&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;_blank&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oMenuItem&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87;font-weight:bold">new&lt;/span> &lt;span style="color:#000">URLPopupMenuItem&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sUID&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sLabel&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sURL&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sTarget&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span> &lt;span style="color:#204a87;font-weight:bold">array&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$oMenuItem&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span> &lt;span style="color:#204a87;font-weight:bold">array&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>EnumItems&lt;/code> 方法将在多种情况下被 iTop 调用。显示对象详情时，&lt;code>$params&lt;/code> 是目标对象。&lt;/p>
&lt;p>由于我们的方法将对任何类型的对象调用，因此必须过滤掉不相关的对象类。&lt;/p>
&lt;p>如您所见，同一个扩展可以处理多种类型的对象和多种类型的菜单（取决于 &lt;code>$iMenuId&lt;/code> 和 &lt;code>$param&lt;/code>）。&lt;/p>
&lt;p>此插件的结果是在任何服务器的详情页面上添加一个额外的菜单条目。&lt;/p>
&lt;h2 id="其他示例">其他示例&lt;/h2>
&lt;p>以下教程使用 iTop 接口 API：&lt;/p>
&lt;ul>
&lt;li>&lt;code>iPopupMenuExtension&lt;/code>：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:add-menu-sample">在&amp;quot;其他操作&amp;quot;弹出菜单中添加条目&lt;/a>&lt;/li>
&lt;li>&lt;code>iPopupMenuExtension&lt;/code>：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:add-menu-sample">在列表上添加操作&lt;/a>&lt;/li>
&lt;li>&lt;code>iBackofficeStyleExtension&lt;/code>：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:hide-history">从活动面板中隐藏历史记录&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces">https://www.itophub.io/wiki/page?id=3_2_0:customization:extensions_api:interfaces&lt;/a>&lt;/p>
&lt;p>版本：3_2_0/customization/extensions_api/interfaces.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1&lt;/p></description></item><item><title>Docs: iTop 编码标准</title><link>/docs/customization/your-extension/coding-standards/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/coding-standards/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 Combodo 的 iTop 编码规范（主要针对 PHP）。这些规范与 PSR-12 标准有很多共通之处，但也存在一些显著差异，请注意区分。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:coding_standards">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="文件编码">文件编码&lt;/h2>
&lt;ul>
&lt;li>源代码文件使用 UTF-8 编码（文件开头不含 BOM）。&lt;/li>
&lt;li>换行符必须为 LF。&lt;/li>
&lt;li>PHP 文件必须使用长标签 &lt;code>&amp;lt;?php&lt;/code> 开头，但不能包含 PHP 结束标签。这可以防止在文件末尾意外添加空白字符。&lt;/li>
&lt;/ul>
&lt;h2 id="空白与缩进">空白与缩进&lt;/h2>
&lt;p>缩进方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PHP 文件&lt;/strong>：使用制表符（配置编辑器将制表符显示为 4 个字符宽度）&lt;/li>
&lt;li>&lt;strong>SCSS&lt;/strong>：2 个空格&lt;/li>
&lt;li>&lt;strong>Twig&lt;/strong>：4 个空格&lt;/li>
&lt;/ul>
&lt;p>&lt;code>if/else&lt;/code>、&lt;code>try/catch&lt;/code>、&lt;code>while&lt;/code>、&lt;code>switch&lt;/code>、匿名类的开括号必须位于行尾，闭括号必须位于主体后的下一行。类和函数的开括号保持在下一行。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">NiceWebPage&lt;/span> &lt;span style="color:#204a87;font-weight:bold">extends&lt;/span> &lt;span style="color:#000">WebPage&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">SetContentType&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sContentType&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sContentType&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">==&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Do something
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span> &lt;span style="color:#204a87;font-weight:bold">else&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Do something different
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>函数调用时，参数紧跟在函数名后的括号内：函数名与参数列表之间没有空格。每个参数列表中的逗号后跟一个空格。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sValue&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">Utils&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">ReadParam&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;param1&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;default_value&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>二元运算符（&lt;code>==&lt;/code>、&lt;code>+&lt;/code>、&lt;code>&amp;amp;&amp;amp;&lt;/code>）两侧有空格。&lt;/p>
&lt;p>例外：连接运算符 &lt;code>.&lt;/code> 两侧没有空格，因为这样可以保持代码更紧凑，且在使用等宽字体时不会降低可读性。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">isset&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$aPerson&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">iPersonId&lt;/span>&lt;span style="color:#000;font-weight:bold">][&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$aPerson&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">iPersonId&lt;/span>&lt;span style="color:#000;font-weight:bold">][&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">!=&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">sSynchronizeOrganization&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">==&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;yes&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$sName&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$aPerson&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">iPersonId&lt;/span>&lt;span style="color:#000;font-weight:bold">][&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;first_name&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#4e9a06">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">$aPerson&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">iPersonId&lt;/span>&lt;span style="color:#000;font-weight:bold">][&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;last_name&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="字符串分隔符">字符串分隔符&lt;/h2>
&lt;p>可以使用任何您喜欢的分隔符，主要目标是保持代码可读性。&lt;/p>
&lt;p>使用 heredoc/nowdoc 时，建议使用允许 JetBrains 语言注入的分隔符。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sHtml&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;&amp;lt;a href=&amp;#34;http://www.combodo.com&amp;#34;&amp;gt;&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sFooBarBaz&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;&lt;/span>&lt;span style="color:#4e9a06">{&lt;/span>&lt;span style="color:#000">$foo&lt;/span>&lt;span style="color:#4e9a06">}&lt;/span>&lt;span style="color:#4e9a06">-&lt;/span>&lt;span style="color:#4e9a06">{&lt;/span>&lt;span style="color:#000">$bar&lt;/span>&lt;span style="color:#4e9a06">}&lt;/span>&lt;span style="color:#4e9a06"> &lt;/span>&lt;span style="color:#4e9a06">{&lt;/span>&lt;span style="color:#000">$baz&lt;/span>&lt;span style="color:#4e9a06">}&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sJs&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;lt;&amp;lt;&amp;lt;&lt;/span>&lt;span style="color:#4e9a06">JS&lt;/span>&lt;span style="color:#4e9a06">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">console.debug(&amp;#34;this is some JS code&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">console.debug(&amp;#34;having the right delimiter keyword will help language injection !&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">&lt;/span>&lt;span style="color:#4e9a06">JS&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="变量命名">变量命名&lt;/h2>
&lt;p>变量、函数参数和对象属性使用&lt;strong>驼峰命名法&lt;/strong>（即每个单词首字母大写，其余小写），并使用&lt;strong>前缀字母作为类型提示&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>前缀&lt;/th>
&lt;th>类型&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>a&lt;/code>&lt;/td>
&lt;td>哈希或数组&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>i&lt;/code>&lt;/td>
&lt;td>整数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>o&lt;/code>&lt;/td>
&lt;td>PHP 对象&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>s&lt;/code>&lt;/td>
&lt;td>字符串&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>f&lt;/code>&lt;/td>
&lt;td>浮点数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>r&lt;/code> 或 &lt;code>h&lt;/code>&lt;/td>
&lt;td>资源句柄&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$iCount&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sText&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;This is a text&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$oPage&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87;font-weight:bold">new&lt;/span> &lt;span style="color:#000">WebPage&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;test&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$fRank&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">1.25&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$hDir&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">opendir&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;/opt&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>没有前缀的变量被认为是混合类型（可以是字符串、数组或对象）。&lt;/p>
&lt;h3 id="允许的例外">允许的例外&lt;/h3>
&lt;ul>
&lt;li>循环计数器使用的局部变量（如 &lt;code>$i&lt;/code>、&lt;code>$j&lt;/code>）可以没有前缀。&lt;/li>
&lt;li>在涉及大量数值变量的数学计算中，可以省略数值变量的前缀（如 &lt;code>$x&lt;/code>、&lt;code>$y&lt;/code>、&lt;code>$xMax&lt;/code>）。&lt;/li>
&lt;li>由于历史原因，&lt;code>WebPage&lt;/code> 类（及相关类）以及 &lt;code>MetaModel&lt;/code> 内部某些部分使用不同的约定：变量全部小写，用下划线分隔单词。对象属性使用 &lt;code>m_&lt;/code> 前缀。&lt;/li>
&lt;/ul>
&lt;h3 id="数组键命名">数组键命名&lt;/h3>
&lt;p>数组键使用&lt;strong>蛇形命名法&lt;/strong>（snake_case）。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$aMyArray&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;value_raw&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$sMyValue&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;input_id&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000">$iInputId&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">];&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>请使用适当的 PHPDoc 语法记录键（见下文）。&lt;/p>
&lt;h3 id="配置参数命名">配置参数命名&lt;/h3>
&lt;p>使用小写，单词之间用下划线分隔。&lt;/p>
&lt;p>当多个配置参数与同一主题相关时，可以使用以点号结尾的前缀进行分组。&lt;/p>
&lt;p>例如：&lt;code>email_transport_smtp.host&lt;/code>&lt;/p>
&lt;h2 id="函数命名phpjs">函数命名（PHP、JS）&lt;/h2>
&lt;p>函数和对象方法使用&lt;strong>驼峰命名法&lt;/strong>。即使 PHP 对函数名不区分大小写，这也有助于区分项目中的函数和 PHP 内置函数（通常为小写）。函数名通常基于表示正在执行的动作的动词。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">DisplayWelcomePopup&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">WebPage&lt;/span> &lt;span style="color:#000">$oP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Code goes here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="类型提示">类型提示&lt;/h2>
&lt;p>自 iTop 3.0.0 起（最低 PHP 7.1.3），可以使用 PHP 类型提示（在方法签名中添加类型），但：&lt;/p>
&lt;ul>
&lt;li>仅在新代码中使用 PHP 类型提示，不要修改现有方法。&lt;/li>
&lt;li>注意遵循最低 PHP 版本的能力（例如，联合类型仅在 PHP 8.0.0 起可用）。&lt;/li>
&lt;li>在流式方法上，不要添加返回类型，否则会导致继承问题。&lt;/li>
&lt;/ul>
&lt;h2 id="比较-vs-">比较（== vs ===）&lt;/h2>
&lt;p>自 2022/12/22 起，严格比较（&lt;code>===&lt;/code>）成为所有新开发的标准规则。&lt;/p>
&lt;p>例外：&lt;/p>
&lt;ul>
&lt;li>如果不确定比较数据的类型，则优先使用严格比较（&lt;code>===&lt;/code>）并添加显式转换。&lt;/li>
&lt;li>如果这不可能，软比较（&lt;code>==&lt;/code>）可以被容忍，但前提是必须同时包含抑制 PHPStorm 警告的注释和解释为什么软比较是唯一方式的注释。&lt;/li>
&lt;/ul>
&lt;h2 id="应避免的模式">应避免的模式&lt;/h2>
&lt;h3 id="使用-empty-检查字符串是否为空">使用 empty() 检查字符串是否为空&lt;/h3>
&lt;p>检查字符串是否为空时，不要使用 &lt;code>empty()&lt;/code>，而应使用 &lt;code>strlen() &amp;gt; 0&lt;/code>（或更好的 &lt;code>\utils::IsNullOrEmptyString()&lt;/code>，如果您的代码面向 iTop 3.0.2+），因为在某些情况下变量可能包含 &lt;code>&amp;quot;0&amp;quot;&lt;/code>，而 &lt;code>empty(&amp;quot;0&amp;quot;)&lt;/code> 会将其评估为 true。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$sFoo&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// 不要这样做
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">empty&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sFoo&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Some stuff
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// 应该这样做
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// - iTop 2.7+
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">strlen&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sFoo&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">===&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Some stuff
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// - iTop 3.0.2+
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">\utils&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">IsNullOrEmptyString&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sFoo&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Some stuff
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="phpdoc">PhpDoc&lt;/h2>
&lt;p>编写 PhpDoc 的规则在仓库本身中有描述：参见 &lt;code>/.doc/README.md&lt;/code>。&lt;/p>
&lt;h3 id="since">@since&lt;/h3>
&lt;p>我们还使用 &lt;code>@since&lt;/code> 标签来指示类/属性/方法的引入或修改。这在&lt;strong>所有类、API 上都是强制性的&lt;/strong>。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @since 2.5.2 2.6.0 new &amp;#39;transaction_id&amp;#39; filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @since 2.7.0 new &amp;#39;element_identifier&amp;#39; filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @since 3.0.0 new utils::ENUM_SANITIZATION_* const
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new &amp;#39;url&amp;#39; filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">protected&lt;/span> &lt;span style="color:#204a87;font-weight:bold">static&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">Sanitize_Internal&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$value&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$sSanitizationFilter&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="bool-vs-boolean">Bool VS boolean&lt;/h3>
&lt;p>请在 PHPDoc 中使用 &lt;code>bool&lt;/code>，因为：&lt;/p>
&lt;ul>
&lt;li>&lt;code>bool&lt;/code> 和 &lt;code>boolean&lt;/code> 在 PHPDoc 中都有效。&lt;/li>
&lt;li>只有 &lt;code>bool&lt;/code> 在 PHP 中有效，&lt;code>boolean&lt;/code> 会导致问题。&lt;/li>
&lt;/ul>
&lt;h3 id="数组psalmphpstan-语法">数组：psalm/phpstan 语法&lt;/h3>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @return array&amp;lt;string, \CheckResult&amp;gt; full path as key, CheckResult error as value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="带命名键的数组array-shapes">带命名键的数组：array Shapes&lt;/h3>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * @param array{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * languageCode: string,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * entries: array{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * code: string,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * value: string
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * } $aDictData
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">static&lt;/span> &lt;span style="color:#000">$aDictData&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="字典条目">字典条目&lt;/h2>
&lt;p>这是一个独立的主题，请参阅专门的 wiki 页面：&lt;a href="../../translations/">如何翻译&lt;/a>。&lt;/p>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:coding_standards">https://www.itophub.io/wiki/page?id=3_2_0:customization:coding_standards&lt;/a>&lt;/p>
&lt;p>版本：3_2_0/customization/coding_standards.txt · Last modified: 2026/01/28 10:40 by 127.0.0.1&lt;/p></description></item><item><title>Docs: iTop 定制机制</title><link>/docs/customization/your-extension/datamodel/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/datamodel/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 iTop 的定制机制，包括数据模型扩展、模块与扩展的概念、开发工作流以及工具包的使用。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:datamodel">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>iTop 构建在 ORM（对象关系映射）抽象层之上，该层依赖于&amp;quot;元数据模型&amp;quot;的定义，由 PHP 类组成。从 iTop 2.0 开始，元数据模型可以用 XML 描述，然后在应用程序安装过程中&amp;quot;编译&amp;quot;成 PHP 类。&lt;/p>
&lt;p>iTop 架构中可定制的部分（橙色框）包括：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>定制类型&lt;/th>
&lt;th>方式&lt;/th>
&lt;th>文档&lt;/th>
&lt;th>目标受众&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>扩展或修改数据模型并调整控制台行为&lt;/td>
&lt;td>XML 文件&lt;/td>
&lt;td>XML 参考&lt;/td>
&lt;td>iTop 顾问、ITIL 专家&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>通过插件扩展通用用户界面&lt;/td>
&lt;td>PHP 文件&lt;/td>
&lt;td>扩展性 API&lt;/td>
&lt;td>PHP 开发人员&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>通过插件扩展数据模型行为&lt;/td>
&lt;td>XML 和 PHP 文件&lt;/td>
&lt;td>Events API&lt;/td>
&lt;td>PHP 开发人员&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>创建新的用户界面页面以实现新的 Web 服务、专用导出或特定任务导向的用户界面&lt;/td>
&lt;td>PHP 文件&lt;/td>
&lt;td>ORM API&lt;/td>
&lt;td>PHP 开发人员&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>创建自己的安全方案&lt;/td>
&lt;td>PHP 文件&lt;/td>
&lt;td>用户权限 API&lt;/td>
&lt;td>PHP 开发人员&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>更改客户门户或开发新门户&lt;/td>
&lt;td>XML 文件&lt;/td>
&lt;td>定制客户门户&lt;/td>
&lt;td>iTop 顾问、ITIL 专家、Web 设计师、PHP 开发人员&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="itop-文件结构">iTop 文件结构&lt;/h2>
&lt;p>iTop 文件和文件夹的布局如下：&lt;/p>
&lt;h3 id="特殊文件夹">特殊文件夹&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>文件夹&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>conf&lt;/code>&lt;/td>
&lt;td>包含配置文件，每个环境一个子文件夹&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>data&lt;/code>&lt;/td>
&lt;td>包含应用程序生成的数据，如对象生命周期图像（如果系统上可用 graphviz）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>datamodels&lt;/code>&lt;/td>
&lt;td>包含元数据模型定义，每个 iTop 主要版本一个子文件夹&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>env-xxxx&lt;/code>&lt;/td>
&lt;td>这些文件夹（每个环境一个）包含&amp;quot;编译后的&amp;quot;数据模型。&lt;code>env-production&lt;/code> 文件夹在每次重新安装或升级时完全重新创建&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>extensions&lt;/code>&lt;/td>
&lt;td>放置不属于 iTop 标准分发的扩展模块&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>log&lt;/code>&lt;/td>
&lt;td>包含应用程序的日志文件：&lt;code>setup.log&lt;/code> 和 &lt;code>error.log&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>所有其他文件夹应被视为应用程序源代码的一部分，通常不应修改。应用程序永远不会写入这些文件夹，它们可以标记为对 Web 服务器进程只读。&lt;/p>
&lt;h3 id="环境">环境&lt;/h3>
&lt;p>iTop 2.0 引入的新概念是&amp;quot;环境&amp;quot;。环境是运行相同代码库但可能具有自己的数据模型和配置文件的 iTop 实例。一个环境由以下部分组成：&lt;/p>
&lt;ul>
&lt;li>存储在 &lt;code>conf/name_of_the_environment/config-itop.php&lt;/code> 的配置文件&lt;/li>
&lt;li>存储在 &lt;code>env-name_of_the_environment&lt;/code> 的数据模型运行时&lt;/li>
&lt;/ul>
&lt;p>工具包会自动创建一个单独的环境（名为 toolkit），以便在不影响&amp;quot;生产&amp;quot;环境的情况下编译 XML 数据模型并测试其一致性。当更改确认无误后，可以指示工具包将验证后的更改应用到&amp;quot;生产&amp;quot;环境。&lt;/p>
&lt;h2 id="扩展与模块">扩展与模块&lt;/h2>
&lt;h3 id="模块">模块&lt;/h3>
&lt;p>iTop 中数据模型定义的基本单元称为&lt;strong>模块&lt;/strong>。iTop 的安装过程将每个模块视为一个整体。&lt;/p>
&lt;p>模块将交付给定功能所需的所有文件组合在一起：XML 中的数据模型定义、PHP 类、Javascript 和 CSS 文件、PHP 页面、图像等。一个模块至少包含一个文件：模块定义文件，始终命名为 &lt;code>module.name_of_the_module.php&lt;/code>。&lt;/p>
&lt;p>虽然您始终可以修补源代码，但定制 iTop 的最佳方式是编写自己的模块。这样可以创建整洁的定制包，并便于部署或升级时重新安装。&lt;/p>
&lt;h3 id="扩展">扩展&lt;/h3>
&lt;p>&lt;strong>扩展&lt;/strong>的概念随 iTop 2.4 引入。扩展是&lt;strong>一个或多个模块&lt;/strong>的集合。安装期间向最终用户提出的安装选项基于在 iTop 的&amp;quot;extensions&amp;quot;文件夹中找到的扩展；为了向后兼容，如果在扩展之外找到模块，则该模块将直接列为安装选项。&lt;/p>
&lt;p>扩展通过每个扩展根文件夹中的 &lt;code>extension.xml&lt;/code> 文件来标识。&lt;/p>
&lt;p>当扩展由单个模块组成时，预期的结构是一个单独的文件夹，所有文件（&lt;code>extension.xml&lt;/code>、&lt;code>module.xxxx.php&lt;/code>、&lt;code>datamodel.xxxx.xml&lt;/code> 等）在同一级别。&lt;/p>
&lt;p>当扩展由多个模块组成时，预期的结构是一个根文件夹，只包含 &lt;code>extension.xml&lt;/code> 文件，每个模块一个子文件夹（包含 &lt;code>module.xxxx.php&lt;/code>、&lt;code>datamodel.xxxx.xml&lt;/code> 等）。&lt;/p>
&lt;p>如果多个扩展包含相同的模块（通过其代码标识），安装过程将只安装一个模块副本：版本最高的那个。&lt;/p>
&lt;h3 id="php-与-xml-数据模型定义">PHP 与 XML 数据模型定义&lt;/h3>
&lt;p>在 iTop 1.x 版本中，数据模型定义被编写为普通 PHP 类。iTop 2.0 支持 PHP 和 XML 两种数据模型定义。&lt;/p>
&lt;p>XML 定义相比 PHP 定义有一个主要优势：一个模块中的 XML 定义可以修改另一个模块中定义的数据模型。例如，可以创建一个扩展模块，在安装时向标准类&amp;quot;Server&amp;quot;添加一个属性，而无需替换整个 &amp;ldquo;itop-config-management&amp;rdquo; 模块。&lt;/p>
&lt;h2 id="模块内容">模块内容&lt;/h2>
&lt;p>如果 &lt;code>my-module&lt;/code> 是您的模块名称，模块文件夹将包含以下文件：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>文件名&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>extension.xml&lt;/code>&lt;/td>
&lt;td>iTop 2.4 新增：XML 定义文件，用于 iTop 2.4 及以上版本。如果模块位于已包含 &lt;code>extension.xml&lt;/code> 文件的文件夹中，则此文件的内容被忽略&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>module.my-module.php&lt;/code>&lt;/td>
&lt;td>模块定义文件。&lt;strong>必需&lt;/strong>。包含模块的描述（名称、版本、对其他模块的依赖等）及其组件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>datamodel.my-module.xml&lt;/code>&lt;/td>
&lt;td>XML 数据模型。安装时&amp;quot;编译&amp;quot;将基于 XML 定义生成 &lt;code>model.xxxxx.php&lt;/code> 文件。XML 文件可以包含类、菜单和角色的定义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>model.my-module.php&lt;/code>&lt;/td>
&lt;td>如果选择直接用 PHP 定义数据模型，则在此文件中放置此类定义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>main.my-module.php&lt;/code>&lt;/td>
&lt;td>PHP 代码和工具。对于包含大量 PHP 代码的模块，将代码提取到此单独文件中比将代码嵌入 XML 更容易&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>images&lt;/code>&lt;/td>
&lt;td>将图像（类图标等）存储在各自的子文件夹中是一个好习惯&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>en.dict.my-module.php&lt;/code>&lt;/td>
&lt;td>字典文件，如果需要一些本地化字符串&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="处理本地化">处理本地化&lt;/h3>
&lt;p>所有字符串都应包含在字典文件中。请参阅专门的 wiki 页面：&lt;a href="../../translations/">如何翻译&lt;/a>。&lt;/p>
&lt;h2 id="创建自己的扩展">创建自己的扩展&lt;/h2>
&lt;p>填写以下表单并点击&amp;quot;生成&amp;quot;以生成一个空扩展，作为定制的起点。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字段&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>公司名称&lt;/td>
&lt;td>&lt;strong>必需&lt;/strong>，仅使用字母字符，无空格。用于 Composer 和 PHP 命名空间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>扩展名称&lt;/td>
&lt;td>仅使用小写字母字符和短横线（-），必须唯一。以 &amp;ldquo;itop-&amp;rdquo; 和 &amp;ldquo;combodo-&amp;rdquo; 开头的名称保留给 Combodo 使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>扩展标签&lt;/td>
&lt;td>安装期间显示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>版本&lt;/td>
&lt;td>格式：x.y.z&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>依赖项&lt;/td>
&lt;td>模块名称/版本的逗号分隔列表&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="查找依赖项">查找依赖项&lt;/h3>
&lt;p>当您的模块修改另一个模块定义的类时，该模块必须在安装和工具包 XML 编译中先存在并处理，以执行 PHP 代码生成。&lt;/p>
&lt;p>XML 数据模型树是逐步构建的，一个接一个地添加模块，因此带有 &lt;code>_delta=&amp;quot;redefine&amp;quot;&lt;/code> 指令的 XML 标签如果该标签尚未存在于树中，则会失败。&lt;/p>
&lt;ul>
&lt;li>首先在 &lt;code>&amp;lt;my-itop&amp;gt;/datamodels/2.x/&lt;/code> 下的模块中搜索您要修改或嫁接的标签。您的模块将依赖于这些找到的模块。&lt;/li>
&lt;li>然后检索您依赖的模块的名称和版本，在 &lt;code>module.xxxx.php&lt;/code> 文件中查找。对于属于 iTop Community 的模块，模块版本遵循 iTop 主要版本。&lt;/li>
&lt;/ul>
&lt;h2 id="安装工具包">安装工具包&lt;/h2>
&lt;ul>
&lt;li>下载工具包 zip 文件：https://github.com/Combodo/itop-toolkit-community/releases&lt;/li>
&lt;li>解压 zip 文件内容，在开发 iTop 实例的根目录下创建 &amp;ldquo;toolkit&amp;rdquo; 目录&lt;/li>
&lt;li>在浏览器中访问 &lt;code>http://&amp;lt;your_itop&amp;gt;/toolkit&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="开发工作流">开发工作流&lt;/h2>
&lt;ol>
&lt;li>创建一个空模块&lt;/li>
&lt;li>安装 iTop 开发实例，将您的空模块放在 &amp;ldquo;extensions&amp;rdquo; 文件夹中&lt;/li>
&lt;li>在开发实例上安装工具包&lt;/li>
&lt;li>编辑您的扩展模块，并使用工具包验证它&lt;/li>
&lt;li>将扩展模块的更改应用到&amp;quot;生产&amp;quot;环境&lt;/li>
&lt;li>使用一些示例数据测试您的模块。如有问题，回到第 4 步迭代修复&lt;/li>
&lt;/ol>
&lt;p>当扩展完成后，可以通过以下方式部署到生产系统：&lt;/p>
&lt;ul>
&lt;li>将包含扩展模块的文件夹复制到生产系统的 &amp;ldquo;extensions&amp;rdquo; 文件夹&lt;/li>
&lt;li>将配置文件标记为读/写&lt;/li>
&lt;li>再次运行安装程序，在交互式安装末尾的&amp;quot;扩展&amp;quot;列表中选择您的模块&lt;/li>
&lt;/ul>
&lt;h2 id="使用工具包">使用工具包&lt;/h2>
&lt;p>工具包安装后，在浏览器中访问：&lt;code>http://&amp;lt;your_itop&amp;gt;/toolkit&lt;/code>。&lt;/p>
&lt;p>第一个选项卡执行一些基本的一致性检查并验证数据模型定义。每次数据模型定义更改后，可以使用&amp;quot;刷新&amp;quot;按钮重新执行验证。此选项卡中的检查在特定的&amp;quot;toolkit&amp;quot;环境上工作，因此不会影响使用&amp;quot;生产&amp;quot;环境的实际 iTop 实例。&lt;/p>
&lt;p>如果收到如下错误：&lt;/p>
&lt;pre tabindex="0">&lt;code>XML datamodel loader: could not find node for class/XXXX
&lt;/code>&lt;/pre>&lt;p>这很可能意味着您的模块缺少对定义类 XXXX 的模块的依赖。&lt;/p>
&lt;p>当第一个选项卡没有显示任何错误时，可以转到第二个选项卡：&lt;/p>
&lt;ul>
&lt;li>检查数据库架构的更改&lt;/li>
&lt;li>点击&amp;quot;更新 iTop 代码&amp;quot;将数据库架构和数据模型定义的更改应用到&amp;quot;生产&amp;quot;iTop 环境&lt;/li>
&lt;/ul>
&lt;p>工具包的第三个选项卡可用于更新数据同步源（如果有任何受数据模型更改影响的），并检查为层次键维护的内部数据是否存在差异。此选项卡直接在&amp;quot;生产&amp;quot;环境上操作。&lt;/p>
&lt;h2 id="生命周期图像">生命周期图像&lt;/h2>
&lt;p>iTop 的&amp;quot;数据模型&amp;quot;页面（&amp;ldquo;生命周期&amp;quot;选项卡）中显示对象生命周期的图形表示。如果修改了对象的生命周期，则需要在 Web 服务器上安装 Graphviz 才能重新计算这些图像。有关安装 Graphviz 的更多信息，请参阅&lt;a href="../../../install/">安装 iTop&lt;/a>。&lt;/p>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:datamodel">https://www.itophub.io/wiki/page?id=3_2_0:customization:datamodel&lt;/a>&lt;/p>
&lt;p>Last modified: 2024/09/10 10:25 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/datamodel.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1&lt;/p></description></item><item><title>Docs: Wiki / 插入 XML 语法</title><link>/docs/customization/your-extension/insert-xml/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/insert-xml/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍教程中使用的简化 XML 语法，以及如何将其扩展为符合 iTop XML 树结构的完整格式。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:insert-xml">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>本文档解释如何扩展教程中提供的 XML 代码片段，使其符合 iTop XML 树结构。&lt;/p>
&lt;h2 id="添加-xml-代码">添加 XML 代码&lt;/h2>
&lt;h3 id="简化语法">简化语法&lt;/h3>
&lt;p>为了提高教程的可读性并突出需要更改的 XML 节点，周围的 XML 结构以简化的路径语法提供。&lt;/p>
&lt;ul>
&lt;li>每个层级用 &lt;code>/&lt;/code> 分隔&lt;/li>
&lt;li>&lt;code>itop_design&lt;/code> 是唯一可能的顶层标签，有时在 wiki 语法中可能省略，但在 XML 文件中不要省略&lt;/li>
&lt;li>末尾带 &lt;code>s&lt;/code> 的标签通常是集合&lt;/li>
&lt;li>在集合下，通常可以找到不带 &lt;code>s&lt;/code> 的相同标签，并用 &lt;code>id&lt;/code> 来区分节点&lt;/li>
&lt;li>&lt;code>brick@toto&lt;/code> 表示 &lt;code>&amp;lt;brick id=&amp;quot;toto&amp;quot;&amp;gt;&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>以下是最短的可能语法示例，其中&lt;strong>只需修改一个节点&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;default&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;redefine&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>tree&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/default&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们不会在 XML 标签中逐个打开和关闭每个层级，而是只提供一个表示树结构的字符串，并将其放在代码块的标题中。&lt;/p>
&lt;p>通常为了可读性会压缩得更少：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;brick&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;services&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;browse_modes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;default&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;redefine&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>tree&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/default&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/browse_modes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/brick&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整-xml-结构">完整 XML 结构&lt;/h3>
&lt;p>以下是将上述代码块扩展为 iTop 所需的完整 XML 结构的方式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;itop_design&lt;/span> &lt;span style="color:#c4a000">version=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;1.6&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;module_designs&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;module_design&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;itop-portal&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;bricks&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;brick&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;services&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;browse_modes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">&amp;lt;!-- 在此处插入代码块内容 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/browse_modes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/brick&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/bricks&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/module_design&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/module_designs&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/itop_design&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此外，这两行必须放在 XML 文件顶部：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;itop_design&lt;/span> &lt;span style="color:#c4a000">version=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;1.6&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>version=&amp;quot;1.6&amp;quot;&lt;/code> 对应扩展所需的最低 iTop XML 版本。XML 版本自 iTop 2.0 起可用，至少到 iTop 2.6，可以从 iTop 版本猜测，将第一个数字减 1。&lt;/p>
&lt;ul>
&lt;li>例如：所有 iTop 2.6.x 版本使用 XML 版本 1.6&lt;/li>
&lt;li>如果您使用 iTop 2.5 中添加的 XML 标签，则 XML 版本不要低于 1.5&lt;/li>
&lt;/ul>
&lt;h3 id="测试">测试&lt;/h3>
&lt;p>将简化的 XML 扩展为完整结构后，将其复制到扩展的 &lt;code>datamodel.my-extension.xml&lt;/code> 文件中（my-extension 是您的真实扩展名称），然后运行安装程序或工具包查看结果。&lt;/p>
&lt;h2 id="添加-php-代码">添加 PHP 代码&lt;/h2>
&lt;p>插入方法只能在 2 个位置进行：类中或模块中。&lt;/p>
&lt;ul>
&lt;li>对于类，我们只在代码块标题中提供需要声明方法的类：&lt;code>class:UserRequest&lt;/code>&lt;/li>
&lt;li>对于模块，没有特定的语法&lt;/li>
&lt;/ul>
&lt;h3 id="在现有类上的方法">在现有类上的方法&lt;/h3>
&lt;p>这部分解释如何在类上定义/覆盖 PHP 方法。&lt;/p>
&lt;ul>
&lt;li>多个教程会告诉您在类上编写 PHP 方法以实现 iTop 中的新行为&lt;/li>
&lt;li>它们甚至可能提供确切的 PHP 代码片段&lt;/li>
&lt;li>但它们不会解释如何将 PHP 代码插入 iTop XML 结构&lt;/li>
&lt;li>这种技术独立于方法本身，正是我们要解释的&lt;/li>
&lt;li>为了不那么抽象，我们使用一个真实的示例&lt;/li>
&lt;/ul>
&lt;h4 id="简化语法-1">简化语法&lt;/h4>
&lt;p>在代码块标题中，您会找到需要声明方法的类。例如：&lt;code>class:UserRequest&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">IncrementCounter&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iIncrement&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// 防御性编程，确保字段代码在当前类上有效
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">MetaModel&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">IsValidAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">get_class&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$iNew&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#000">$iIncrement&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iNew&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span> &lt;span style="color:#204a87;font-weight:bold">true&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>PHP 代码必须先扩展，然后才能粘贴到 &lt;code>datamodel.my-extension.xml&lt;/code> 文件中。&lt;/p>
&lt;h4 id="完整-xml-代码">完整 XML 代码&lt;/h4>
&lt;p>以下是其扩展方式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;itop_design&lt;/span> &lt;span style="color:#c4a000">version=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;1.3&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;classes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">&amp;lt;!-- 类名在简化语法框的标题中 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;class&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;UserRequest&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;must_exist&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;methods&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">&amp;lt;!-- 任何 &amp;#39;id&amp;#39; 都可以，但如果与函数名相同则更容易记住 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;method&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;IncrementCounter&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;static&amp;gt;&lt;/span>false&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/static&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;access&amp;gt;&lt;/span>public&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/access&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">&amp;lt;!-- comment 是可选的 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;comment&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * $sAttCode 应该是 UserRequest 类中 Integer 或 Decimal 类型的有效字段代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * $iIncrement 必须是整数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> ]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/comment&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;code&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">public function IncrementCounter($sAttCode, $iIncrement=1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> // 防御性编程，确保字段代码在当前类上有效
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> if (MetaModel::IsValidAttCode(get_class($this), $sAttCode))
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $iNew = $this-&amp;gt;Get($sAttCode) + $iIncrement;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $this-&amp;gt;Set($sAttCode, $iNew);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> return true;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> ]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/method&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/methods&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/class&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/classes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/itop_design&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>解码方式的详细信息：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>源&lt;/th>
&lt;th>应翻译为&amp;hellip;&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>public function IncrementCounter()&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;access&amp;gt;public&amp;lt;/access&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>protected function ...&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;access&amp;gt;protected&amp;lt;/access&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>public function ...&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;static&amp;gt;false&amp;lt;/static&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>public static function ...&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;static&amp;gt;true&amp;lt;/static&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>public function IncrementCounter(&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;method id=&amp;quot;IncrementCounter&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="风险">风险&lt;/h4>
&lt;p>当您定义某个 DBObject 可覆盖方法之一时，请注意它可能已经在标准 iTop 数据模型中完成。如果是这种情况，在安装/工具包编译时，由于您使用 &lt;code>_delta=&amp;quot;define&amp;quot;&lt;/code> 添加方法，如果它已经存在则会失败。在这种情况下，您可以执行 &amp;ldquo;redefine&amp;rdquo;，但随后需要镜像现有代码，否则将破坏 iTop 的当前行为。&lt;/p>
&lt;h4 id="itsm-designer-特定要求">ITSM Designer 特定要求&lt;/h4>
&lt;p>如果满足以下条件，则需要 &lt;code>arguments&lt;/code> 标签：&lt;/p>
&lt;ul>
&lt;li>您的扩展将加载到 ITSM Designer 中&lt;/li>
&lt;li>&lt;strong>且&lt;/strong> 方法必须由转换上的操作调用&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;method&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;IncrementCounter&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt; ...&lt;/span> &lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;argument&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;1&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;mandatory&amp;gt;&lt;/span>true&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/mandatory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>attcode&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/argument&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;argument&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;2&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;mandatory&amp;gt;&lt;/span>false&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/mandatory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;type&amp;gt;&lt;/span>int&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/argument&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/method&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="在新-php-类中">在新 PHP 类中&lt;/h2>
&lt;p>最简单的方式是将此代码放入扩展的 &lt;code>main.my-extension.php&lt;/code> 文件中。&lt;/p>
&lt;p>但也可以放在 XML 结构中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;snippet&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;ScheduledStart&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;placement&amp;gt;&lt;/span>module&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/placement&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;module&amp;gt;&lt;/span>itop-request-mgmt&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;rank&amp;gt;&lt;/span>200&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/rank&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;content&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> /* 在此处输入包含类定义和方法的代码 */
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> ]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/content&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/snippet&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:insert-xml">https://www.itophub.io/wiki/page?id=3_2_0:customization:insert-xml&lt;/a>&lt;/p>
&lt;p>版本：3_2_0/customization/insert-xml.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1&lt;/p></description></item><item><title>Docs: XML 数据模型参考</title><link>/docs/customization/your-extension/xml-reference/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/xml-reference/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档是 iTop 模块中 &lt;code>datamodel.xxxx.xml&lt;/code> 文件所使用的 XML 格式的参考文档。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:xml_reference">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>XML 数据模型文件可用于定义：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>类&lt;/strong> - 使用安装程序或工具包&amp;quot;编译&amp;quot;时将生成实际 PHP 类&lt;/li>
&lt;li>&lt;strong>菜单&lt;/strong> - 插入 iTop 页面左侧的应用程序菜单中&lt;/li>
&lt;li>&lt;strong>角色&lt;/strong> - 管理对 iTop 对象的访问权限&lt;/li>
&lt;li>&lt;strong>品牌标识&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>要定制用户门户，请参阅&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:portal_xml">门户 XML 参考&lt;/a>。&lt;/p>
&lt;h2 id="基本原则">基本原则&lt;/h2>
&lt;p>XML 数据模型包含&lt;strong>初始声明&lt;/strong>和/或对在其他 XML 文件中做出的声明的&lt;strong>更改&lt;/strong>。&lt;/p>
&lt;p>iTop 编译器分两步工作：&lt;/p>
&lt;ol>
&lt;li>加载已安装模块的 XML 数据模型文件。XML 被合并为一个单一的 XML 定义。重要的是要理解，从一个空定义开始，iTop 将逐个合并每个 XML&lt;/li>
&lt;li>解释最终的合并定义&lt;/li>
&lt;/ol>
&lt;p>默认情况下，给定 XML 的内容被合并到合并定义中。&lt;/p>
&lt;h3 id="更改节点内容">更改节点内容&lt;/h3>
&lt;p>您可以使用 &lt;code>_delta&lt;/code> 属性定义更改。此属性指定给定节点（包括其子节点）应如何处理。&lt;/p>
&lt;p>&lt;code>_delta&lt;/code> 可以取以下值：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;code>_delta&lt;/code>&lt;/th>
&lt;th>含义&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>merge&lt;/code>（默认）&lt;/td>
&lt;td>如果此节点尚不存在，则创建它。然后检查其子节点中的 delta。这主要与结构节点相关&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>must_exist&lt;/code>&lt;/td>
&lt;td>确保此节点已存在。然后检查其子节点中的 delta&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>if_exists&lt;/code>&lt;/td>
&lt;td>2.3.0 新增 - 检查此节点是否已存在。如果是，检查其子节点中的 delta。否则忽略此节点。类似于 &lt;code>must_exist&lt;/code>，但跳过当前节点而不是停止整个过程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>define&lt;/code>&lt;/td>
&lt;td>这是此节点的首次定义。其子节点中不应包含任何标志&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>define_if_not_exists&lt;/code>&lt;/td>
&lt;td>如果节点尚不存在则定义它。否则忽略它&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>redefine&lt;/code>&lt;/td>
&lt;td>此节点的内容必须被 delta 节点的内容替换&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>delete&lt;/code>&lt;/td>
&lt;td>删除此节点。如果节点不存在则停止处理。不要在此节点下提供任何子节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>delete_if_exists&lt;/code>&lt;/td>
&lt;td>删除此节点。不要在此节点下提供任何子节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>force&lt;/code>&lt;/td>
&lt;td>2.4.0 新增 - 强制定义此节点，无论它是否存在&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>节点通过其标签名和 &lt;code>id&lt;/code> 属性来标识。如果没有给出 &lt;code>id&lt;/code>，则节点被标识为找到的第一个具有相同标签名的节点。&lt;/p>
&lt;p>当编译器遇到 &lt;code>define&lt;/code>/&lt;code>redefine&lt;/code> 时，它认为子树是纯内容定义。因此，该子树中找到的任何 &lt;code>_delta&lt;/code> 或 &lt;code>_rename_from&lt;/code> 属性都将被忽略。&lt;/p>
&lt;p>当单个节点下可以有多个相同标签的节点时，我们称之为集合。&lt;/p>
&lt;h3 id="重命名节点">重命名节点&lt;/h3>
&lt;p>另一个属性是 &lt;code>_rename_from&lt;/code>。使用它来指定项目（例如类字段）实际上是从 &lt;code>_rename_from&lt;/code> 重命名为 &lt;code>id&lt;/code> 的现有项目。&lt;/p>
&lt;h2 id="xml-通用结构">XML 通用结构&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>标签&lt;/th>
&lt;th>用法&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;itop_design xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; version=&amp;quot;3.0&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>结构节点。此标签前不允许有注释。与先前版本相比的变化，请参阅 Version_30 的详细信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;classes&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>声明的类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;class id=&amp;quot;name&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td>零个或多个&lt;/td>
&lt;td>类声明&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;parent&amp;gt;cmdbAbstractObject&amp;lt;/parent&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>父类。参见类属性继承&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;properties&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>属性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;is_link&amp;gt;1&amp;lt;/is_link&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>区分用于链接其他类的类。对于链接类设置为 1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;comment&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>添加到编译文件中的 PHP 注释，位于类声明之前&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;category&amp;gt;bizmodel,searchable&amp;lt;/category&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>此类的用途。参见类类别&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;abstract&amp;gt;false&amp;lt;/abstract&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>抽象类不能被实例化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;key_type&amp;gt;autoincrement&amp;lt;/key_type&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>始终设置为 &amp;ldquo;autoincrement&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;db_table&amp;gt;name&amp;lt;/db_table&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>此类使用的 MySQL 表名。此处给出的名称将自动添加安装时提供的后缀&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;db_key_field&amp;gt;id&amp;lt;/db_key_field&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>将标识符字段始终设置为 &amp;ldquo;id&amp;rdquo; 即可&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;db_final_class_field&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>如果类位于类层次结构的顶部，则必须定义哪个 MySQL 列将用于跟踪对象实例的实际类。将其设置为 &amp;ldquo;finalclass&amp;rdquo; 即可&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;naming&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义用于组成对象友好名称的属性。默认情况下，友好名称将渲染为给定属性的连接，以空格分隔。您可以通过添加字典条目 &lt;code>Class:&amp;lt;myClass&amp;gt;/Name&lt;/code> 指定任何其他格式。此格式以类 printf 格式表达式给出（仅支持 &lt;code>$s&lt;/code>。例如 &lt;code>'%1$s of %2$s'&lt;/code>）。由于此格式位于字典中，它可能被翻译，友好名称将取决于最终用户的语言&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;attributes&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>用于组成友好名称的属性列表。注意顺序很重要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;attribute id=&amp;quot;name&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td>至少一个&lt;/td>
&lt;td>属性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;complementary_attributes&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义用于补充对象友好名称以区分重复项的属性列表。默认情况下，补充为空。您可以通过添加字典条目 &lt;code>Class:&amp;lt;myClass&amp;gt;/ComplementaryName&lt;/code> 指定格式&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;fields_semantic&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义承载类某些方面的属性（例如图像、状态等）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;image_attribute&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>承载应代表对象的图像的属性代码（必须声明为 AttributeImage）。如果未定义，则在必要时使用类图标&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;state_attribute&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>承载对象状态的属性代码，如果有生命周期则必需，属性必须是当前类的枚举。如果此处未定义但在父类中定义，则继承&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;order&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义类的默认排序顺序，如果省略则按友好名称排序&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;columns&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>列&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>`&amp;lt;column id=&amp;ldquo;name&amp;rdquo; ascending=&amp;ldquo;true&lt;/td>
&lt;td>false&amp;rdquo;&amp;gt;`&lt;/td>
&lt;td>至少一个&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;style&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>指定类的样式（颜色和图标）。自 iTop 3.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;icon&amp;gt;../../images/user-request.png&amp;lt;/icon&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>为类指定图标（相对于模块根目录的路径）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;main_color&amp;gt;#4E79A5&amp;lt;/main_color&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>类的主色，可以是任何有效的 CSS 颜色（hexa、rgba、hsla，但不能是变量）或 SCSS 变量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;complementary_color&amp;gt;#FFFFFF&amp;lt;/complementary_color&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>类的互补色，可以是任何有效的 CSS 颜色或 SCSS 变量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;reconciliation&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>定义数据导入的默认协调方案&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;indexes&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义表索引&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;index id=&amp;quot;name&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td>零个或多个&lt;/td>
&lt;td>显式强制创建数据库索引，包含所有指定属性，按声明的顺序&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;archive&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>归档配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;enabled&amp;gt;true&amp;lt;/enabled&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>仅适用于根类。设置为 true 以使此类及其后代可归档（添加两个魔法属性：archive_flag 和 archive_date）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;obsolescence&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>过时配置。一旦为类定义了过时，就会添加两个魔法属性（obsolescence_flag 和 obsolescence_date），层次结构中的所有类都有该标志（默认为 &amp;ldquo;No&amp;rdquo;）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;condition&amp;gt;&amp;lt;![CDATA[status='closed']]&amp;gt;&amp;lt;/condition&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>定义对象是否过时的 OQL 表达式。此条件将由子类继承，除非被重新定义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;uniqueness_rules&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>保存对象时用于检查其唯一性的规则&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;fields&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>属性声明（与从父类继承的属性累积）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;lifecycle&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>生命周期：状态和转换。在 3.0 中，&amp;ldquo;lifecycle/attribute&amp;rdquo; 标签被替换为 &amp;ldquo;fields_semantic/attribute_state&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;highlight_scale&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>定义对象显示（列表中的背景颜色和图标）的高亮代码列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;presentation&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>展示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;details&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>定义对象的可视化和编辑表单的展示。可以被生命周期标志 &amp;ldquo;hidden&amp;rdquo; 覆盖&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;search&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>搜索屏幕的 &amp;ldquo;Add new criteria&amp;rdquo; 菜单的 &amp;ldquo;Most popular&amp;rdquo; 部分中显示的属性有序列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;default_search&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>搜索屏幕中显示的默认搜索条件有序列表。如果留空或不存在，则继承自父类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;list&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>默认在结果列表中显示的属性有序列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;summary&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>在外部键弹出概述中显示的属性有序列表。自 iTop 3.1 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;custom_presentations&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>扩展带来的自定义属性列表。自 iTop 3.1 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;menus&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>主 GUI 中显示的菜单声明（左窗格）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;user_rights&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>用户权限策略的实现：用户将拥有一个或多个角色，授予他们访问权限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;groups&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>组是类的集合。授权基于此分组 - 参见 &amp;ldquo;profiles&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;profiles&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>将在应用程序中列出的角色。用户可拥有一个或多个角色。&amp;ldquo;administrator&amp;rdquo; 角色始终存在且不可重新定义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;files&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>用于在 XML 中嵌入二进制文件（如图标）。自 iTop 2.0.1 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;dictionaries&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>用于在 XML 中添加本地化。自 iTop 2.0.1 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;branding&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>GUI 的外观。Logo 定义为 PNG 文件（相对于模块根目录的路径 = env-production），或 fileref 标签&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;main_logo&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>主 GUI 的 Logo（菜单展开时左上角）。尺寸：最大宽度 310px，最大高度 70px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;main_logo_compact&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>控制台菜单折叠时左上角的 Logo。尺寸：宽度 37px，高度 37px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;login_logo&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>登录页面的 Logo。尺寸：最大宽度 300px，最大高度 54px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;portal_logo&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>客户门户的 Logo。尺寸：最大宽度 192px，最大高度 50px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;main_favicon&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>主 GUI 和默认其他 GUI 使用的 Favicon（浏览器标签中的图标）。自 iTop 3.2.0 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;portal_favicon&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>最终用户门户使用的 Favicon。自 iTop 3.2.0 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;login_favicon&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>登录页面使用的 Favicon。自 iTop 3.2.0 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;themes&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>主题主节点，用于自定义 iTop 外观。自 iTop 2.7.0 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;themes_common&amp;gt;&lt;/code>&lt;/td>
&lt;td>必需&lt;/td>
&lt;td>&lt;strong>3.1 新增&lt;/strong> 全局主题规则，与 themes 节点中声明的每个主题一起编译。可以包含与 theme 节点相同的内容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;constants&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>要编译为 PHP defines 的常量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;portals&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>用户界面入口点的定义。自 iTop 2.2.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;module_parameters&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>模块特定参数的定义。自 iTop 2.2.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;module_designs&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>模块特定设计数据。自 iTop 2.3.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;snippets&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>要注入 PHP 模型文件中的 PHP 代码片段。自 iTop 2.2.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;meta&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>存储有关 XML 定义的元信息的根节点，编译器忽略&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;events&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;strong>3.1 新增&lt;/strong> 事件定义列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>&amp;lt;event_listeners&amp;gt;&lt;/code>&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;strong>3.1 新增&lt;/strong> 全局事件监听器&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="字段类型">字段类型&lt;/h2>
&lt;p>字段（也称为属性）是对象的实际数据成员。一个字段通常对应数据库中一个（或多个）表列。不同类型的字段如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>AttributeBlob&lt;/code>&lt;/td>
&lt;td>二进制大对象（最大 4GB）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeCaseLog&lt;/code>&lt;/td>
&lt;td>案例日志，讨论线程。每条消息标有说话者姓名和当前时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeCustomFields&lt;/code>&lt;/td>
&lt;td>自定义模块处理的自定义字段。自 iTop 2.3.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeDashboard&lt;/code>&lt;/td>
&lt;td>仅用于显示信息的仪表板字段。自 iTop 2.6.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeDate&lt;/code>&lt;/td>
&lt;td>日期（无时间）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeDateTime&lt;/code>&lt;/td>
&lt;td>日期和时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeDecimal&lt;/code>&lt;/td>
&lt;td>十进制值&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeDuration&lt;/code>&lt;/td>
&lt;td>持续时间，以秒存储，以天/小时/分钟/秒显示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeEmailAddress&lt;/code>&lt;/td>
&lt;td>电子邮件地址&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeEncryptedPassword&lt;/code>&lt;/td>
&lt;td>加密存储的密码字符串（最大 65535 字符），在 UI 中不可读。自 3.2.1 起&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeEncryptedString&lt;/code>&lt;/td>
&lt;td>加密存储的字符串（最大 255 字符），在 UI 中可读&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeEnum&lt;/code>&lt;/td>
&lt;td>固定值集合中的字符串&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeEnumSet&lt;/code>&lt;/td>
&lt;td>固定值集合中的字符串集合&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeExternalField&lt;/code>&lt;/td>
&lt;td>另一个对象持有的属性的别名（参见 &amp;ldquo;ExternalKey&amp;rdquo;）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeExternalKey&lt;/code>&lt;/td>
&lt;td>指向给定类对象的外部键&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeHierarchicalKey&lt;/code>&lt;/td>
&lt;td>指向同一类的外部键，用于构建对象层次结构&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeHTML&lt;/code>&lt;/td>
&lt;td>HTML 文本（最大 4GB）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeImage&lt;/code>&lt;/td>
&lt;td>图像（最大 4GB）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeInteger&lt;/code>&lt;/td>
&lt;td>正整数值&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeIPAddress&lt;/code>&lt;/td>
&lt;td>IP 地址&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeLinkedSet&lt;/code>&lt;/td>
&lt;td>指向当前对象的对象集合&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeLinkedSetIndirect&lt;/code>&lt;/td>
&lt;td>通过&amp;quot;链接类&amp;quot;与当前对象相关的对象集合&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeMetaEnum&lt;/code>&lt;/td>
&lt;td>在其他枚举字段之后计算的字符串，可在子类中定义。自 iTop 2.3.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeObjectKey&lt;/code>&lt;/td>
&lt;td>灵活的外部键：指向任何类对象的指针。自 iTop 2.2.0 起实验性支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributePercentage&lt;/code>&lt;/td>
&lt;td>百分比（0 到 100 之间的整数），以条形显示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributePhoneNumber&lt;/code>&lt;/td>
&lt;td>电话号码&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeRedundancySettings&lt;/code>&lt;/td>
&lt;td>关系冗余设置。自 iTop 2.2.0 起支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeStopWatch&lt;/code>&lt;/td>
&lt;td>累计在某些状态中花费的时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeString&lt;/code>&lt;/td>
&lt;td>字符串（限制为 255 字符的一行）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeSubitem&lt;/code>&lt;/td>
&lt;td>秒表中存在的某个状态信息。两者必须在同一类中定义！&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeTagSet&lt;/code>&lt;/td>
&lt;td>要与对象关联的标签列表。存储在 255 长度字符串上&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeText&lt;/code>&lt;/td>
&lt;td>多行文本（最大 64KB）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeLongText&lt;/code>&lt;/td>
&lt;td>大文本（最大 4GB）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AttributeURL&lt;/code>&lt;/td>
&lt;td>URL（http&amp;hellip;）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="生命周期">生命周期&lt;/h2>
&lt;h3 id="刺激stimuli">刺激（Stimuli）&lt;/h3>
&lt;p>每个从一个状态到另一个状态的转换（对于有生命周期的对象）在对象收到刺激时触发。不同类型的刺激如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>StimulusUserAction&lt;/code>&lt;/td>
&lt;td>最终用户决定的操作。刺激显示在 Actions 菜单中（如果对象处于考虑此刺激的状态）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>StimulusInternal&lt;/code>&lt;/td>
&lt;td>可以以编程方式触发的操作&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="转换上的操作">转换上的操作&lt;/h3>
&lt;p>有关可在 XML 中作为操作调用的现有方法，请参阅&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:actions-on-transitions">转换可用方法&lt;/a>。&lt;/p>
&lt;h2 id="菜单类型">菜单类型&lt;/h2>
&lt;p>不同类型的菜单如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>MenuGroup&lt;/code>&lt;/td>
&lt;td>顶级菜单。此菜单将始终保持可见。它是其他菜单的容器&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DashboardMenuNode&lt;/code>&lt;/td>
&lt;td>仪表板。内容可以通过 &amp;ldquo;export&amp;rdquo; 功能生成。内容可以在文件（definition_file）中或直接在此处（definition）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>NewObjectMenuNode&lt;/code>&lt;/td>
&lt;td>创建新对象的快捷方式&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SearchMenuNode&lt;/code>&lt;/td>
&lt;td>搜索对象的快捷方式&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>TemplateMenuNode&lt;/code>&lt;/td>
&lt;td>用于二级菜单组&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>OQLMenuNode&lt;/code>&lt;/td>
&lt;td>显示搜索结果的快捷方式，给定 OQL 查询&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:xml_reference">https://www.itophub.io/wiki/page?id=3_2_0:customization:xml_reference&lt;/a>&lt;/p>
&lt;p>2026/01/23 14:22 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/xml_reference.txt · Last modified: 2026/01/23 14:22 by 127.0.0.1&lt;/p></description></item><item><title>Docs: 对象操作 API</title><link>/docs/customization/your-extension/api-objects-manipulation/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/api-objects-manipulation/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍 iTop 中用于搜索和操作持久化对象的 API 类。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:api:objects-manipulation:start">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>iTop 提供了一组核心类用于在数据库中搜索和操作对象。这些类构成了 ORM（对象关系映射）层的基础。&lt;/p>
&lt;h2 id="核心类">核心类&lt;/h2>
&lt;h3 id="dbobjectsearch">DBObjectSearch&lt;/h3>
&lt;p>&lt;code>DBObjectSearch&lt;/code> 类用于在数据库中搜索对象。它允许您构建 OQL（对象查询语言）查询来检索满足特定条件的对象。&lt;/p>
&lt;h3 id="dbobjectset">DBObjectSet&lt;/h3>
&lt;p>&lt;code>DBObjectSet&lt;/code> 类表示一组持久化对象。它是执行查询后的结果集，提供了遍历和操作这些对象的方法。&lt;/p>
&lt;h3 id="dbunionsearch">DBUnionSearch&lt;/h3>
&lt;p>&lt;code>DBUnionSearch&lt;/code> 类用于执行多个 &lt;code>DBObjectSearch&lt;/code> 的并集操作。当您需要合并来自不同查询的结果时，可以使用此类。&lt;/p>
&lt;h2 id="参考">参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:api:objects-manipulation:start">iTop 官方文档 - 对象操作 API&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:api:objects-manipulation:start">https://www.itophub.io/wiki/page?id=3_2_0:customization:api:objects-manipulation:start&lt;/a>&lt;/p>
&lt;p>Last modified: 2024/09/10 10:25 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/api/objects-manipulation/start.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1&lt;/p></description></item><item><title>Docs: 方法与事件</title><link>/docs/customization/your-extension/event/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/event/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档对比 DBObject 方法、iApplicationObjectExtension 接口和 Events API（iTop 3.1+）三种扩展方式，帮助您了解如何从旧机制迁移到基于事件的扩展机制。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:event">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>iTop 提供了多种方式来扩展对象的行为。从 iTop 3.1 开始，推荐使用**事件（Events）**机制替代传统的 DBObject 方法重载和 &lt;code>iApplicationObjectExtension&lt;/code> 接口。&lt;/p>
&lt;h2 id="方法-vs-事件对比">方法 vs 事件对比&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>DBObject 方法&lt;/th>
&lt;th>iApplicationObjectExtension&lt;/th>
&lt;th>事件（iTop 3.1+）&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>ComputeValues&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_COMPUTE_VALUES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DoCheckToWrite&lt;/td>
&lt;td>OnCheckToWrite&lt;/td>
&lt;td>EVENT_DB_CHECK_TO_WRITE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DoCheckToDelete&lt;/td>
&lt;td>OnCheckToDelete&lt;/td>
&lt;td>EVENT_DB_CHECK_TO_DELETE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GetAttributeFlags&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_SET_ATTRIBUTES_FLAGS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GetInitialStateAttributeFlags&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_LINKS_CHANGED&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_ADD_ATTACHMENT_TO_OBJECT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_REMOVE_ATTACHMENT_FROM_OBJECT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>EnumTransitions&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_ENUM_TRANSITION&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OnInsert&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_BEFORE_WRITE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AfterInsert&lt;/td>
&lt;td>OnDBInsert&lt;/td>
&lt;td>EVENT_DB_AFTER_WRITE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OnUpdate&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_BEFORE_WRITE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AfterUpdate&lt;/td>
&lt;td>OnDBUpdate&lt;/td>
&lt;td>EVENT_DB_AFTER_WRITE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OnDelete&lt;/td>
&lt;td>OnDBDelete&lt;/td>
&lt;td>EVENT_DB_ABOUT_TO_DELETE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AfterDelete&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>EVENT_DB_AFTER_DELETE&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="不可替换的方法">不可替换的方法&lt;/h2>
&lt;p>以下方法无法通过事件替换：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>DBObject 方法&lt;/th>
&lt;th>iApplicationObjectExtension&lt;/th>
&lt;th>事件&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>PrefillSearchForm&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PrefillCreationForm&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PrefillTransitionForm&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DisplayBareRelations&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>n/a&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="从-dbobject-方法重载迁移到监听器">从 DBObject 方法重载迁移到监听器&lt;/h2>
&lt;p>在类中注册事件监听器，以在回调方法中执行代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;classes&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;class&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;UserRequest&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listener&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;HideTransitionsIfApprovalRule&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event&amp;gt;&lt;/span>EVENT_ENUM_TRANSITIONS&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;rank&amp;gt;&lt;/span>10&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/rank&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;callback&amp;gt;&lt;/span>HideTransitionsIfApprovalRule&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/callback&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;methods&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;method&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;HideTransitionsIfApprovalRule&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;comment&amp;gt;&lt;/span>/**
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> * If an approval rule could apply to the current object, hide all transitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> * As the service and service subcategory are not yet set, we can&amp;#39;t determine if the approval rule will really apply
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> * So we hide all transitions if the object is new and in a state where an approval rule could apply
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> */&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/comment&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;static&amp;gt;&lt;/span>false&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/static&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;access&amp;gt;&lt;/span>public &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/access&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;code&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[public function HideTransitionsIfApprovalRule(Combodo\iTop\Service\Events\EventData $oEventData)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> // PHP code here...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">}]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/method&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/methods&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>回调方法接收一个事件参数，您可以在签名中允许它为 null，以便重用该方法。&lt;/li>
&lt;/ul>
&lt;h2 id="从-iapplicationobjectextension-迁移到监听器">从 iApplicationObjectExtension 迁移到监听器&lt;/h2>
&lt;h3 id="纯-php-方式">纯 PHP 方式&lt;/h3>
&lt;p>首先考虑纯 PHP 方式处理事件。复制粘贴以下模板并根据需要调整：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * 模板：帮助从 iApplicationObjectExtension 钩子迁移到基于事件的监听器。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * 作为起点使用：
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * - 必须指定要监听的类列表（或省略参数以监听所有类）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * - 必须将旧钩子的代码复制/粘贴到适当的方法中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * - 必须删除不必要的监听器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * - 建议的事件与旧钩子最接近，但可能不是最合适的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * - 整个内容可以在 XML 中定义，而不是 PHP，允许在集成时进行更精细的控制（优先级等）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MyCustomHooks&lt;/span> &lt;span style="color:#204a87;font-weight:bold">implements&lt;/span> &lt;span style="color:#000">iEventServiceSetup&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">RegisterEventsAndListeners&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">void&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 删除不必要的监听器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">EVENT_DB_CHECK_TO_WRITE&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnDBCheckToWrite&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Class1&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;Class2&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">EVENT_DB_CHECK_TO_DELETE&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnDBCheckToDelete&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Class1&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;Class2&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">EVENT_DB_AFTER_WRITE&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnDBAfterWrite&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Class1&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;Class2&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">EventService&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#c4a000">RegisterListener&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">EVENT_DB_ABOUT_TO_DELETE&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;OnDBAboutToDelete&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Class1&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;Class2&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnDBCheckToWrite&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 将之前在 OnCheckToWrite 中实现的代码放在这里，使用 AddCheckIssue 报告问题，而不是将它们作为数组返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnDBCheckToDelete&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 将之前在 OnCheckToDelete 中实现的代码放在这里，使用 AddDeleteIssue 报告问题
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnDBAfterWrite&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;is_new&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 将之前在 OnDBInsert 中实现的代码放在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">else&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 将之前在 OnDBUpdate 中实现的代码放在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">OnDBAboutToDelete&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$oObject&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;object&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// TODO: 将之前在 OnDBDelete 中实现的代码放在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="xml-方式">XML 方式&lt;/h3>
&lt;p>在类内部或外部注册事件监听器以执行代码：&lt;/p>
&lt;p>示例（来自 &lt;code>combodo-autodispatch-ticket&lt;/code>，监听器适用于任何类）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event_listener&lt;/span> &lt;span style="color:#c4a000">id=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;HideTransitionsIfAutoDispatchRule&amp;#34;&lt;/span> &lt;span style="color:#c4a000">_delta=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;define&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;event&amp;gt;&lt;/span>EVENT_DB_AFTER_WRITE&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;filters/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;rank&amp;gt;&lt;/span>0&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/rank&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;code&amp;gt;&lt;/span>&lt;span style="color:#8f5902;font-style:italic">&amp;lt;![CDATA[function(Combodo\iTop\Service\Events\EventData $oEventData)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> $oObject = $oEventData-&amp;gt;Get(&amp;#39;object&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> if ($oEventData-&amp;gt;Get(&amp;#39;is_new&amp;#39;)) {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> // TODO: 将之前在 OnDBInsert 中实现的代码放在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> else {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> // TODO: 将之前在 OnDBUpdate 中实现的代码放在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> ]]&amp;gt;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">&amp;lt;/event_listeners&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="事件详解">事件详解&lt;/h2>
&lt;h3 id="event_db_compute_values">EVENT_DB_COMPUTE_VALUES&lt;/h3>
&lt;p>注册此事件以在对象修改时计算字段值（基于同一对象其他字段的值）。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">EvtComputeValues&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;vat&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.2&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">*&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;cost&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>您可以通过 &lt;code>$oEventData-&amp;gt;Get('stimulus_applied')&lt;/code> 判断是否处于特定转换中（null 表示不在转换中）。&lt;/li>
&lt;/ul>
&lt;p>在旧版本 iTop 和某些类中，您可能会遇到 &lt;code>ComputeValues()&lt;/code> 方法，该方法已弃用，但执行相同的工作。&lt;/p>
&lt;h3 id="event_db_set_initial_attributes_flags">EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS&lt;/h3>
&lt;p>有两个不同的事件：一个在显示对象的&lt;strong>创建&lt;/strong>表单之前，另一个在显示对象的&lt;strong>修改&lt;/strong>表单时。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">EvtSetInitialAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">AddInitialAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// 为字段添加标志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">ForceInitialAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// 强制字段标志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/* 标志常量值，使用位运算符 | 组合：
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_NORMAL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_HIDDEN
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_READONLY
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_MANDATORY
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_MUSTCHANGE
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> * OPT_ATT_MUSTPROMPT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="event_db_set_attributes_flags">EVENT_DB_SET_ATTRIBUTES_FLAGS&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#204a87;font-weight:bold">function&lt;/span> &lt;span style="color:#000">EvtSetAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Combodo\iTop\Service\Events\EventData&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">AddAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// 为字段添加标志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">ForceAttributeFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sAttCode&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">$iFlags&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// 强制字段标志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>GetAttributeFlags&lt;/code> 和 &lt;code>GetInitialStateAttributeFlags&lt;/code> 方法已弃用，不应再使用。&lt;/p>
&lt;h3 id="event_db_check_to_write">EVENT_DB_CHECK_TO_WRITE&lt;/h3>
&lt;p>此事件在对象写入数据库之前触发。&lt;/p>
&lt;ul>
&lt;li>允许在特定条件不满足时阻止记录当前对象&lt;/li>
&lt;li>也可以简单地警告用户非阻塞性问题&lt;/li>
&lt;li>在回调方法中添加 &lt;code>AddCheckIssue&lt;/code> 消息将停止对象的创建/修改/转换，并显示错误消息&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// 阻塞性问题
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">AddCheckIssue&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Some error message&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// 非阻塞性警告
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">AddCheckWarning&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;Some warning message&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>您可以通过 &lt;code>$oEventData-&amp;gt;Get('stimulus_applied')&lt;/code> 判断是否处于特定转换中（空字符串表示不在转换中）&lt;/li>
&lt;/ul>
&lt;p>此时修改对象本身已为时过晚。&lt;/p>
&lt;h3 id="event_db_before_write">EVENT_DB_BEFORE_WRITE&lt;/h3>
&lt;p>如果您想自动修改当前对象，通常正确的答案是 &lt;code>EVENT_DB_COMPUTE_VALUES&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>如果此更改仅在对象创建时发生，请使用 &lt;code>EVENT_DB_BEFORE_WRITE&lt;/code> 并通过 &lt;code>$oEventData-&amp;gt;Get('is_new')==$this-&amp;gt;IsNew()&lt;/code> 测试是否处于创建状态&lt;/li>
&lt;li>如果此更新资源密集且仅在某些字段被设置/修改时才应执行，则在 &lt;code>EVENT_DB_BEFORE_WRITE&lt;/code> 中检查正在进行的更改：&lt;code>$this-&amp;gt;ListChanges()&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="event_db_after_write">EVENT_DB_AFTER_WRITE&lt;/h3>
&lt;p>如果您想在某个对象被修改时自动修改其他对象，请使用 &lt;code>EVENT_DB_AFTER_WRITE&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>在当前对象保存到数据库后触发一次&lt;/li>
&lt;li>使用 &lt;code>$oEventData-&amp;gt;Get('is_new')===true&lt;/code> 测试对象是否刚刚创建&lt;/li>
&lt;li>使用 &lt;code>$oEventData-&amp;gt;Get('stimulus_applied')&lt;/code> 测试是否处于特定转换中&lt;/li>
&lt;li>检查已执行的更改：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$aChanges&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;changes&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$aChanges&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">ListPreviousValuesForUpdatedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="event_display_object_details">EVENT_DISPLAY_OBJECT_DETAILS&lt;/h3>
&lt;p>此事件在对象显示在用户门户之前触发。&lt;/p>
&lt;ul>
&lt;li>它不是设计用来作用于显示内容的，但允许您统计 FAQ 在用户门户中显示的次数，并可能将此计数存储在 FAQ 对象本身中。&lt;/li>
&lt;/ul>
&lt;h3 id="event_db_links_changed">EVENT_DB_LINKS_CHANGED&lt;/h3>
&lt;p>此事件在对象上触发，在其 LinkedSet（标记为&amp;quot;PHP 计算&amp;quot;）被修改之后。&lt;/p>
&lt;ul>
&lt;li>如果宿主对象本身被修改，&lt;strong>不会&lt;/strong>触发&lt;/li>
&lt;li>如果 PHP 代码更新对象的 LinkedSet，&lt;strong>不会&lt;/strong>触发&lt;/li>
&lt;li>如果 LinkedSetIndirect 的远程对象被修改（例如 Team.persons_list 中 Person 的状态变更），&lt;strong>不会&lt;/strong>触发&lt;/li>
&lt;li>如果 LinkedSet 中的子对象被单独添加、移除或修改，则会触发&lt;/li>
&lt;/ul>
&lt;p>在此回调执行期间，您不知道：&lt;/p>
&lt;ul>
&lt;li>什么发生了变化，甚至不知道哪个 LinkedSet&lt;/li>
&lt;li>当前对象是否刚刚创建或修改&lt;/li>
&lt;/ul>
&lt;p>适用场景：确保一旦链接被创建、修改或删除，您的代码就会执行。可用于存储关系计数或总和、重新排序、查找具有特定值的关系并将其作为 ExternalKey 存储在对象中。&lt;/p>
&lt;h3 id="event_enum_transitions">EVENT_ENUM_TRANSITIONS&lt;/h3>
&lt;p>此事件在对象显示之前触发。&lt;/p>
&lt;ul>
&lt;li>允许在某些条件下移除对象的转换&lt;/li>
&lt;li>例如，阻止非当前代理的用户将工单重新分配给其他团队&lt;/li>
&lt;li>如果移除不可用的转换，则无效果&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$this&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">DenyTransition&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">$sStimulusCode&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="event_add_attachment_to_object">EVENT_ADD_ATTACHMENT_TO_OBJECT&lt;/h3>
&lt;p>此事件在附件添加到对象之后触发。&lt;/p>
&lt;ul>
&lt;li>允许在案例日志中添加条目或测试附件的约束条件，并在某些条件下自动删除（例如有风险的文件类型）&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">$oEventData&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#c4a000">Get&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;attachment&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:event">https://www.itophub.io/wiki/page?id=3_2_0:customization:event&lt;/a>&lt;/p>
&lt;p>Last modified: 2025/10/31 12:23 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/event.txt · Last modified: 2025/10/31 12:23 by 127.0.0.1&lt;/p></description></item><item><title>Docs: 新扩展文件结构</title><link>/docs/customization/your-extension/new-extension/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid>/docs/customization/your-extension/new-extension/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>本文档介绍创建 iTop 扩展的推荐文件结构和编码规范。这些规范不是强制性的，但强烈建议遵循，以便他人理解扩展的工作原理。更多详细内容请参考 &lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:new_extension">iTop 官方文档&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>这些是新扩展的推荐指南，绝对不是强制性的，但强烈建议遵循，因为这将帮助其他人理解扩展的工作原理。&lt;/p>
&lt;h2 id="文件夹结构">文件夹结构&lt;/h2>
&lt;p>此结构由 Combodo 研发团队于 2019 年 5 月集体设计。&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;lt;extension-root&amp;gt;
asset
js
css
img
lib
bin
dictionaries // iTop 3.0+
doc
[legacy]
src
Controller
Helper
Service
&amp;lt;SomeService&amp;gt;
Hook
Model
tests
php-unit-tests
integration-tests
unitary-tests
phpunit.xml
ci_description.ini
templates
vendor
[compatibilitybridge.php]
composer.json
index.php
Jenkinsfile
module.&amp;lt;module&amp;gt;.php
datamodel.&amp;lt;extension&amp;gt;.xml
[extension.xml]
[license.&amp;lt;module&amp;gt;.xml]
&lt;/code>&lt;/pre>&lt;h2 id="文件与文件夹说明">文件与文件夹说明&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>文件夹/文件&lt;/th>
&lt;th>必需&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>asset&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>扩展的公共资源（图像、样式表、JS 脚本、库等）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>bin&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>在 CLI 中运行的脚本/可执行文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>dictionaries&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>模块的字典文件（例如 &lt;code>en.dict.&amp;lt;extension&amp;gt;.php&lt;/code>）。自 iTop 3.0.0 起可用，之前版本文件必须放在根文件夹中&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>doc&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>文档文件。可以是 README.md 的图像、markdown 文件等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>legacy&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>仅当扩展需要兼容引入破坏性更改的 iTop 版本时&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>src&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>PHP 文件。子文件夹应使用驼峰命名法并遵循文件的命名空间，以便自动加载器正常工作&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>src/Controller&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>使用 MVC 模式时的控制器类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>src/Hook&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>实现/扩展 iTop API 的 PHP 类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>src/Model&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>实体 PHP 类（来自数据模型或非数据模型）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>所有类型测试的结构文件夹&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests/php-unit-tests&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>所有 PHPUnit 测试的结构文件夹&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests/php-unit-tests/integration-tests&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>集成测试文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests/php-unit-tests/unitary-tests&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>单元测试文件，应与 &lt;code>/src&lt;/code> 文件夹的结构匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests/php-unit-tests/phpunit.xml&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>要运行的测试套件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tests/ci_description.ini&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>描述 CI 运行时要使用的参数（默认参数的重载）的文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>templates&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>扩展使用的模板（HTML、JS 等）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>vendor&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>不要在此放置任何内容，它将自动由 composer 构建（第三方库和自动加载器）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>compatibilitybridge.php&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>仅当扩展需要兼容引入破坏性更改的 iTop 版本时。将在 &lt;code>module.&amp;lt;extension&amp;gt;.php&lt;/code> 中加载，并负责根据 iTop 版本加载正确的文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>composer.json&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>放置扩展类命名空间和/或类映射；放置要包含的第三方库&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>index.php&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>MVC 使用的端点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Jenkinsfile&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>在 CI 中启用扩展的文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>module.&amp;lt;extension&amp;gt;.php&lt;/code>&lt;/td>
&lt;td>&lt;strong>是&lt;/strong>&lt;/td>
&lt;td>扩展定义文件，包含其代码、版本、依赖项、安装程序等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>datamodel.&amp;lt;extension&amp;gt;.xml&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>扩展的数据模型 XML 更改（参见下面关于相应 model.php 文件的说明）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>extension.xml&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>扩展的元数据（作者、兼容性、描述、URL 等。由 Designer、Hub 和 Setup 使用。将在构建期间由 Factory 生成）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>exclude.txt&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>包含要从扩展构建中排除的文件/文件夹列表。通常是 &lt;code>doc/&lt;/code> 文件夹和 &amp;ldquo;README.md&amp;rdquo; 文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>README.md&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>对于非官方扩展，描述其功能、使用方法和与 iTop 的兼容性。当扩展成为官方扩展时将被移除，其文档将移至 wiki&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>license.&amp;lt;module&amp;gt;.xml&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>许可证文件&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>注意：&lt;/p>
&lt;ul>
&lt;li>添加一个空的 &lt;code>model.&amp;lt;module&amp;gt;.php&lt;/code>！&lt;/li>
&lt;li>在 &lt;code>module.&amp;lt;module&amp;gt;.php&lt;/code> 的 &amp;lsquo;datamodel&amp;rsquo; 键中引用此文件&lt;/li>
&lt;/ul>
&lt;h2 id="编码规范">编码规范&lt;/h2>
&lt;h3 id="php">PHP&lt;/h3>
&lt;ul>
&lt;li>一个 PHP 文件应只包含&lt;strong>一个&lt;/strong>类&lt;/li>
&lt;li>PHP 文件应使用 PSR-4 约定命名，即对于 &amp;ldquo;MyCMDBObject&amp;rdquo; 类，文件应为 &amp;ldquo;MyCMDBObject.php&amp;rdquo; 而不是 &amp;ldquo;mycmdbobject.class.inc.php&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="自动加载器">自动加载器&lt;/h2>
&lt;p>不必通过在 &lt;code>module.&amp;lt;extension&amp;gt;.php&lt;/code> 文件的 &amp;lsquo;datamodels&amp;rsquo; 部分包含它们来在每个 iTop 页面中加载 PHP 文件，我们可以使用自动加载器仅在需要时加载它们。&lt;/p>
&lt;p>此自动加载器将作为 iTop 基础自动加载器的&lt;strong>补充&lt;/strong>。&lt;/p>
&lt;p>编辑 &lt;code>composer.json&lt;/code> 文件以放置命名空间（和/或类映射）后，生成自动加载器并将其添加到 &amp;lsquo;datamodels&amp;rsquo; 部分：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> &amp;lt;PATH_OF_THE_EXTENSION&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>composer.phar dump-autoload -a
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;datamodels&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#204a87;font-weight:bold">array&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Module&amp;#39;s autoloader
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;vendor/autoload.php&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Explicitly load APIs classes if any
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;src/Hook/MyConsoleUIExtension.php&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="安全性">安全性&lt;/h2>
&lt;p>如果您希望文件可以在没有任何认证的情况下调用（在大多数情况下&lt;strong>不推荐&lt;/strong>），您需要将文件添加到 &lt;code>model.&amp;lt;module&amp;gt;.php&lt;/code> 文件的 &lt;code>delegated_authentication_endpoints&lt;/code> 中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;delegated_authentication_endpoints&amp;#39;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&amp;gt;&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;index.php&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#39;open_access.php&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">],&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>原文：&lt;a href="https://www.itophub.io/wiki/page?id=3_2_0:customization:new_extension">https://www.itophub.io/wiki/page?id=3_2_0:customization:new_extension&lt;/a>&lt;/p>
&lt;p>2026/04/13 17:01 by127.0.0.1&lt;/p>
&lt;p>版本：3_2_0/customization/new_extension.txt · Last modified: 2026/04/13 17:01 by 127.0.0.1&lt;/p></description></item></channel></rss>