From 024f2536267af1c23c7071fa8ff15e6ae5d3829b Mon Sep 17 00:00:00 2001 From: Yoan Bozhilov Date: Thu, 14 May 2026 12:03:19 +0300 Subject: [PATCH] Add safety guards in Generate item flow for invalid references and non-existent classes The Generate item massive action threw fatal errors on GLPI 11 / PHP 8.3 under two scenarios: 1. PluginOrderReference::getReferenceItemID() (reference.class.php) - getItemForItemtype() returns false in HTTP context for some dynamically-generated classes (e.g. Glpi\CustomAsset\* in GLPI 11), causing "Call to a member function getFromDB() on false". - Existing references migrated from older itemtypes may have templates_id = 0, which makes getFromDB() fail silently and the subsequent getField() calls operate on a half-initialised object. 2. PluginOrderLink::generateItem() (link.class.php) - Toolbox::hasTrait() internally calls class_uses(). On PHP 8.3 with the Safe library wrapper, class_uses() on a non-existent class throws Safe\Exceptions\SplException instead of emitting a warning. - This is triggered by stale itemtype values in glpi_plugin_order_orders_items that reference classes from uninstalled plugins (e.g. PluginGenericobjectOther after migrating to native custom assets). Both call sites are now guarded: - reference.class.php: bail out early with return 0 when the item cannot be instantiated or templates_id is empty. - link.class.php: prefix both Toolbox::hasTrait() calls with class_exists() so non-existent classes are skipped cleanly. Tested on GLPI 11.0.7, PHP 8.3, Order plugin 2.12.6: - Order with valid native itemtypes (Computer, Monitor, etc.): unchanged. - Order containing custom asset references (Glpi\CustomAsset\otherAsset): Generate item form now renders fully and submits successfully. - Order containing stale itemtypes from uninstalled plugins (PluginGenericobjectOther): items are skipped without fatal error, remaining items render correctly. --- CHANGELOG.md | 1 + inc/link.class.php | 5 +++-- inc/reference.class.php | 9 ++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 233952fe40..8cdce0d51e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fix generate associated item massive action +- Add safety guards in Generate item flow for invalid references and non-existent classes (GLPI 11 / PHP 8.3) - Update locales diff --git a/inc/link.class.php b/inc/link.class.php index 8af76df2a8..9af3c3015a 100644 --- a/inc/link.class.php +++ b/inc/link.class.php @@ -148,7 +148,8 @@ public function showItemGenerationForm($params) $row['template_name'] = ""; } - if (Toolbox::hasTrait($itemtype, AssignableItem::class)) { + // GLPI 11 safety: skip non-existent classes (e.g. uninstalled plugins like GenericObject) + if (class_exists($itemtype) && Toolbox::hasTrait($itemtype, AssignableItem::class)) { $row['assignableitem'] = true; if (!is_array($row['groups_id'])) { $row['groups_id'] = $row['groups_id'] > 0 ? [$row['groups_id']] : []; @@ -174,7 +175,7 @@ public function showItemGenerationForm($params) 'active_entities' => $_SESSION['glpiactiveentities'] ?? [], 'item_rows' => $item_rows, 'order_web_dir' => $order_web_dir, - 'assignableitem' => Toolbox::hasTrait($itemtype, AssignableItem::class), + 'assignableitem' => class_exists($itemtype) && Toolbox::hasTrait($itemtype, AssignableItem::class), ]); return null; } diff --git a/inc/reference.class.php b/inc/reference.class.php index a54866540b..e7e5fb8c73 100644 --- a/inc/reference.class.php +++ b/inc/reference.class.php @@ -471,7 +471,14 @@ public function checkIfTemplateExistsInEntity($detailID, $itemtype, $entity) } else { $row = $result->current(); $item = getItemForItemtype($itemtype); - $item->getFromDB($row["templates_id"]); + // GLPI 11 safety: custom asset classes may fail to resolve in HTTP context + // and templates_id may be 0 for entries created before plugin migration. + if ($item === false || empty($row["templates_id"])) { + return 0; + } + if (!$item->getFromDB($row["templates_id"])) { + return 0; + } if ($item->getField('entities_id') == $entity || ($item->maybeRecursive() && $item->fields['is_recursive']