Skip to content
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "professional-wiki/semantic-wikibase",
"name": "baillyk/semantic-wikibase",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Package metadata changes may cause issues if merged upstream.

The package name and homepage have been changed to point to a personal fork, but the support URLs (lines 32-34) still reference the original ProfessionalWiki/SemanticWikibase repository. This creates an inconsistency.

If this PR is intended to be merged back to the upstream repository, consider reverting these changes to maintain the original package identity. If this is a permanent fork, update the support URLs as well for consistency.

Also applies to: 15-15

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composer.json` at line 2, The composer metadata is inconsistent: the "name"
(baillyk/semantic-wikibase) and "homepage" were changed to a personal fork but
the "support" URLs still point to ProfessionalWiki/SemanticWikibase; either
revert the "name" and "homepage" changes to match upstream or update the
"support" entries to the fork's URLs so package metadata is consistent. Locate
the "name" and "homepage" keys and the "support" object in composer.json and
make them all refer to the same package identity (either the original
ProfessionalWiki/SemanticWikibase values or the fork's values) before merging.

"type": "mediawiki-extension",
"description": "MediaWiki extension that makes Wikibase data available in Semantic MediaWiki",
"keywords": [
Expand All @@ -12,7 +12,7 @@
"semantic web",
"wikidata"
],
"homepage": "https://professional.wiki/en/extension/semantic-wikibase",
"homepage": "https://github.com/baillyk/SemanticWikibase",
"license": "GPL-2.0-or-later",
"authors": [
{
Expand Down
4 changes: 3 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@
"callback": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onExtensionRegistration",

"Hooks": {
"ParserFirstCallInit":"MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onParserFirstCallInit",
"SMW::Property::initProperties": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwInitProperties",
"SMW::SQLStore::AddCustomFixedPropertyTables": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwAddCustomFixedPropertyTables",
"SMWStore::updateDataBefore": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwUpdateDataBefore"
"SMW::SQLStore::BeforeDataUpdateComplete": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwUpdateDataBefore",
"MultiContentSave": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onMultiContentSave"
},

"config": {
Expand Down
47 changes: 46 additions & 1 deletion src/EntryPoints/HookHandlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@

namespace MediaWiki\Extension\SemanticWikibase\EntryPoints;

use MediaWiki\Revision\RenderedRevision;
use MediaWiki\User\UserIdentity;
use CommentStoreComment;
use Status;
use MediaWiki\MediaWikiServices;
use MediaWiki\TitleFactory;
use MediaWiki\Extension\SemanticWikibase\SemanticWikibase;
use SMW\Services\ServicesFactory as ApplicationFactory;
use SMW\PropertyRegistry;
use SMW\SemanticData;
use SMW\DIWikiPage;
use SMW\StoreFactory;
use SMW\Store;

use Parser;

class HookHandlers {

public static function onExtensionRegistration(): void {
Expand All @@ -17,18 +28,52 @@ public static function onExtensionRegistration(): void {
$smwgNamespacesWithSemanticLinks[WB_NS_PROPERTY] = true;
}

public static function onParserFirstCallInit( Parser $parser ) {
# getInstance() will call initProperties() which aktivates existing hook onSmwInitProperties()
wfDebug( __METHOD__ . "SWB: onParserFirstCallInit" );
PropertyRegistry::getInstance();
}

public static function onSmwInitProperties( PropertyRegistry $propertyRegistry ): void {
SemanticWikibase::getGlobalInstance()->registerProperties( $propertyRegistry );
wfDebug( __METHOD__ . "SWB: onSmwInitProperties..." );
SemanticWikibase::getGlobalInstance()->registerProperties( $propertyRegistry );
}

public static function onSmwAddCustomFixedPropertyTables( array &$customFixedProperties, array &$fixedPropertyTablePrefix ): void {
wfDebug( __METHOD__ . "SWB: onSmwAddCustomFixedPropertyTables" );
SemanticWikibase::getGlobalInstance()->getFixedProperties()
->registerFixedTables( $customFixedProperties, $fixedPropertyTablePrefix );
}

public static function onSmwUpdateDataBefore( Store $store, SemanticData $semanticData ): void {
wfDebug( __METHOD__ . "SWB: onSmwUpdateDataBefore" );
SemanticWikibase::getGlobalInstance()->getSemanticDataUpdate()
->run( $semanticData );

}

public static function onPageSaveComplete( WikiPage $wikiPage, MediaWiki\User\UserIdentity $user, string $summary, int $flags, MediaWiki\Revision\RevisionRecord $revisionRecord, MediaWiki\Storage\EditResult $editResult ) {
wfDebug( __METHOD__ . "SWB: onPageSaveComplete" );
// Access the semantic data of the page via the Semantic MediaWiki API
$semanticData = \SMW\MediaWiki\Hooks\ParserHooks::getSemanticDataForPage($wikiPage);

onSmwUpdateDataBefore(null, $semanticData);
}
Comment on lines +55 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find the HookHandlers.php file and check its imports and structure
find . -name "HookHandlers.php" -type f

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 111


🏁 Script executed:

# Check imports and full content of HookHandlers.php
head -30 "src/EntryPoints/HookHandlers.php"

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 821


🏁 Script executed:

# Search for onSmwUpdateDataBefore method definition in the file
rg "onSmwUpdateDataBefore" "src/EntryPoints/HookHandlers.php" -A 5 -B 2

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 755


🏁 Script executed:

# Search for WikiPage import
rg "use.*WikiPage" "src/EntryPoints/HookHandlers.php"

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 96


🏁 Script executed:

# Check if ParserHooks class exists and search for getSemanticDataForPage
find . -name "*.php" -type f | xargs rg "ParserHooks" | head -20

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 204


🌐 Web query:

SMW Semantic MediaWiki ParserHooks getSemanticDataForPage API documentation

💡 Result:

getSemanticDataForPage is not documented as a public SMW “external API” endpoint; if you’re trying to retrieve a page’s semantic facts via HTTP, the documented SMW APIs to use are:

  • api.php?action=browsebysubject&subject=Page title (returns the facts for a subject, incl. special properties and subobjects) [2]
  • api.php?action=ask / api.php?action=askargs (run SMW queries and get serialized results) [2]

If you meant an internal PHP API (e.g., from an extension / parser hook callback) to get semantic data for the current parse, SMW’s documented pattern is to create ParserData from the Title and ParserOutput, then call $parserData->getSemanticData() (and optionally push it back via pushSemanticDataToParserOutput()) [1].

SMW parser functions (“parser hooks”) themselves are documented here (e.g., #set, #subobject, #ask, etc.) [3].


Critical: onPageSaveComplete has multiple bugs that will cause runtime errors.

  1. Incorrect function call: Line 60 calls onSmwUpdateDataBefore(null, $semanticData) as a bare function. This method exists in the class (defined at line 38) but must be called as self::onSmwUpdateDataBefore(). Additionally, the method signature expects Store $store as the first parameter, not null.

  2. Non-existent method: \SMW\MediaWiki\Hooks\ParserHooks::getSemanticDataForPage($wikiPage) is not a documented public API in Semantic MediaWiki. The standard pattern is to use StoreFactory::getStore()->getSemanticData(DIWikiPage::newFromTitle($wikiPage->getTitle())), similar to line 72 in onMultiContentSave.

  3. Missing import: WikiPage is not imported but is used as a type hint on line 55.

  4. Redundant type hints: Parameters use fully-qualified names like MediaWiki\User\UserIdentity but UserIdentity is already imported at line 8.

🐛 Proposed fix
+use WikiPage;

-public static function onPageSaveComplete( WikiPage $wikiPage, MediaWiki\User\UserIdentity $user, string $summary, int $flags, MediaWiki\Revision\RevisionRecord $revisionRecord, MediaWiki\Storage\EditResult $editResult ) {
+public static function onPageSaveComplete( WikiPage $wikiPage, UserIdentity $user, string $summary, int $flags, \MediaWiki\Revision\RevisionRecord $revisionRecord, \MediaWiki\Storage\EditResult $editResult ): void {
     wfDebug( __METHOD__ . "SWB: onPageSaveComplete" );
-    // Access the semantic data of the page via the Semantic MediaWiki API
-    $semanticData = \SMW\MediaWiki\Hooks\ParserHooks::getSemanticDataForPage($wikiPage);
-
-    onSmwUpdateDataBefore(null, $semanticData);
+    $semanticData = StoreFactory::getStore()->getSemanticData( DIWikiPage::newFromTitle( $wikiPage->getTitle() ) );
+    self::onSmwUpdateDataBefore( StoreFactory::getStore(), $semanticData );
 }
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 55-55: Avoid unused parameters such as '$user'. (undefined)

(UnusedFormalParameter)


[warning] 55-55: Avoid unused parameters such as '$summary'. (undefined)

(UnusedFormalParameter)


[warning] 55-55: Avoid unused parameters such as '$flags'. (undefined)

(UnusedFormalParameter)


[warning] 55-55: Avoid unused parameters such as '$revisionRecord'. (undefined)

(UnusedFormalParameter)


[warning] 55-55: Avoid unused parameters such as '$editResult'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/EntryPoints/HookHandlers.php` around lines 55 - 61, Fix
onPageSaveComplete: import MediaWiki\Page\WikiPage (add a use) and replace
fully-qualified parameter types like MediaWiki\User\UserIdentity with the
imported UserIdentity; obtain semantic data the same way as onMultiContentSave
by calling
StoreFactory::getStore()->getSemanticData(DIWikiPage::newFromTitle($wikiPage->getTitle()));
then call the class method self::onSmwUpdateDataBefore($store, $semanticData)
(where $store is the Store returned by StoreFactory::getStore()) instead of the
bare onSmwUpdateDataBefore(null, ...). Ensure symbols referenced are
onPageSaveComplete, onSmwUpdateDataBefore, StoreFactory, Store, DIWikiPage, and
WikiPage.


public static function onMultiContentSave( RenderedRevision $renderedRevision, UserIdentity $user, CommentStoreComment $summary, $flags, Status $hookStatus ) {
wfDebug( __METHOD__ . "SWB: onMultiContentSave" );
$revision = $renderedRevision->getRevision();

$titleFactory = MediaWikiServices::getInstance()->getTitleFactory();
$title = $titleFactory->newFromLinkTarget($revision->getPageAsLinkTarget());
wfDebug( __METHOD__ . "SWB: onMultiContentSave...title:".$title );
$subject = DIWikiPage::newFromTitle( $title );
#$new_content = $revision->getContent(SlotRecord::MAIN, RevisionRecord::RAW)->getNativeData();
$semanticData = StoreFactory::getStore()->getSemanticData( DIWikiPage::newFromTitle( $title ) );
wfDebug( __METHOD__ . "SWB: onMultiContentSave: ".json_encode($semanticData) );
SemanticWikibase::getGlobalInstance()->getSemanticDataUpdate()->run( $semanticData );

return true;
}

}
32 changes: 23 additions & 9 deletions src/SMW/SemanticEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,34 @@
use SMW\DIWikiPage;
use SMW\SemanticData;
use SMWDataItem;
use SMW\Subobject;

class SemanticEntity {

private array $dataItemsPerProperty = [];
private array $subObjectsPerProperty=[];

public function addPropertyValue( string $propertyId, SMWDataItem $dataItem ) {
$this->dataItemsPerProperty[$propertyId][] = $dataItem;
public function addPropertyValue( string $NumericPropertyId, SMWDataItem $dataItem ) {
$this->dataItemsPerProperty[$NumericPropertyId][] = $dataItem;
}

public function addSubobject( string $NumericPropertyId, SubObject $subobject ){
$this->subObjectsPerProperty[$NumericPropertyId][] = $subobject;
}
Comment on lines +22 to 24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Case mismatch in type hint: SubObject should be Subobject.

The import at line 11 is SMW\Subobject but the type hint here uses SubObject with capital 'O'. While PHP class names are case-insensitive, this inconsistency can cause confusion and should be corrected for code quality.

🐛 Proposed fix
-public function addSubobject( string $NumericPropertyId, SubObject $subobject ){
+public function addSubobject( string $NumericPropertyId, Subobject $subobject ): void {
 	$this->subObjectsPerProperty[$NumericPropertyId][] = $subobject;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function addSubobject( string $NumericPropertyId, SubObject $subobject ){
$this->subObjectsPerProperty[$NumericPropertyId][] = $subobject;
}
public function addSubobject( string $NumericPropertyId, Subobject $subobject ): void {
$this->subObjectsPerProperty[$NumericPropertyId][] = $subobject;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/SMW/SemanticEntity.php` around lines 22 - 24, Fix the inconsistent class
name casing in the addSubobject method: change the parameter type hint from
SubObject to match the imported class name Subobject so the signature public
function addSubobject(string $NumericPropertyId, Subobject $subobject) aligns
with the import; update any other occurrences in the method or nearby signatures
that mistakenly use SubObject to ensure consistent use of Subobject across the
file.


/**
* @param string $propertyId
* @param string $NumericPropertyId
* @return SMWDataItem[]
*/
public function getDataItemsForProperty( string $propertyId ): array {
return $this->dataItemsPerProperty[$propertyId] ?? [];
public function getDataItemsForProperty( string $NumericPropertyId ): array {
return $this->dataItemsPerProperty[$NumericPropertyId] ?? [];
}

public function toSemanticData( DIWikiPage $subject ): SemanticData {
$semanticData = new SemanticData( $subject );

foreach ( $this->dataItemsPerProperty as $propertyId => $dataItems ) {
$property = new DIProperty( $propertyId );
foreach ( $this->dataItemsPerProperty as $NumericPropertyId => $dataItems ) {
$property = new DIProperty( $NumericPropertyId );

foreach ( $dataItems as $dataItem ) {
$semanticData->addPropertyObjectValue(
Expand All @@ -39,6 +45,14 @@ public function toSemanticData( DIWikiPage $subject ): SemanticData {
}
}

foreach ( $this->subObjectsPerProperty as $NumericPropertyId => $subobjects ) {
$property = new DIProperty( $NumericPropertyId );

foreach ( $subobjects as $subobject ) {
$semanticData->addSubobject($subobject);
}
}
Comment on lines +48 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused variable $property in subobject loop.

The $property variable on line 49 is created but never used. The subobjects are added directly to $semanticData without referencing this property. Either remove the unused variable or use it if it was intended to associate subobjects with the property.

🐛 Proposed fix - remove unused variable
 foreach ( $this->subObjectsPerProperty as $NumericPropertyId => $subobjects ) {
-	$property = new DIProperty( $NumericPropertyId );
-
 	foreach ( $subobjects as $subobject ) {
 		$semanticData->addSubobject($subobject);
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
foreach ( $this->subObjectsPerProperty as $NumericPropertyId => $subobjects ) {
$property = new DIProperty( $NumericPropertyId );
foreach ( $subobjects as $subobject ) {
$semanticData->addSubobject($subobject);
}
}
foreach ( $this->subObjectsPerProperty as $NumericPropertyId => $subobjects ) {
foreach ( $subobjects as $subobject ) {
$semanticData->addSubobject($subobject);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/SMW/SemanticEntity.php` around lines 48 - 54, The loop creates a
DIProperty instance ($property = new DIProperty($NumericPropertyId)) that is
never used; either remove that line or actually use $property to associate
subobjects with the property before calling $semanticData->addSubobject. Locate
the loop over $this->subObjectsPerProperty in SemanticEntity::(method containing
this code), then either delete the unused $property creation (DIProperty) or
modify the inner loop to reference $property when adding subobjects (e.g.,
associate or pass property information to $semanticData->addSubobject) so
$property is not unused.


return $semanticData;
}

Expand All @@ -52,9 +66,9 @@ public function functionalMerge( self $entity ): self {
}

public function add( self $entity ): void {
foreach ( $entity->dataItemsPerProperty as $propertyId => $dataItems ) {
foreach ( $entity->dataItemsPerProperty as $NumericPropertyId => $dataItems ) {
foreach ( $dataItems as $dataItem ) {
$this->addPropertyValue( $propertyId, $dataItem );
$this->addPropertyValue( $NumericPropertyId, $dataItem );
}
}
}
Comment on lines 68 to 74
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Subobjects are not merged in the add() method.

The add() method merges dataItemsPerProperty from another entity but does not merge subObjectsPerProperty. This could lead to data loss when using functionalMerge() on entities that contain subobjects.

🐛 Proposed fix
 public function add( self $entity ): void {
 	foreach ( $entity->dataItemsPerProperty as $NumericPropertyId => $dataItems ) {
 		foreach ( $dataItems as $dataItem ) {
 			$this->addPropertyValue( $NumericPropertyId, $dataItem );
 		}
 	}
+	foreach ( $entity->subObjectsPerProperty as $NumericPropertyId => $subobjects ) {
+		foreach ( $subobjects as $subobject ) {
+			$this->addSubobject( $NumericPropertyId, $subobject );
+		}
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/SMW/SemanticEntity.php` around lines 68 - 74, The add() method in
SemanticEntity merges only dataItemsPerProperty but ignores
subObjectsPerProperty, causing subobjects to be lost during functionalMerge();
update SemanticEntity::add(self $entity) to also iterate over
$entity->subObjectsPerProperty and merge each subobject into
$this->subObjectsPerProperty (preserving keys/indices and avoiding duplicates),
using the existing addSubobject/merge helper if present or by calling the same
internal merge logic used elsewhere for subobjects so subObjectsPerProperty is
properly combined alongside dataItemsPerProperty.

Expand Down
9 changes: 7 additions & 2 deletions src/SemanticDataUpdate.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use SMW\SemanticData;
use Title;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Entity\PropertyId;
use Wikibase\DataModel\Entity\NumericPropertyId;
use Wikibase\DataModel\Services\Lookup\ItemLookup;
use Wikibase\DataModel\Services\Lookup\PropertyLookup;

Expand Down Expand Up @@ -63,8 +63,13 @@ private function newItemTranslator( Title $title ): ItemTranslator {
}

private function getSemanticEntityForPropertyTitle( Title $title ): SemanticEntity {
wfDebug(__METHOD__. "swb: getSemanticEntity:".json_encode($title));
wfDebug(__METHOD__. "swb: getSemanticEntity:".json_encode($title->getText()));
wfDebug(__METHOD__. "swb: getSemanticEntity:".json_encode(new NumericPropertyId( $title->getText() )));
wfDebug(__METHOD__. "swb: getSemanticEntity:".json_encode($this->propertyLookup->getPropertyForId( new NumericPropertyId( $title->getText() ) )));

return $this->newPropertyTranslator( $title )->translateProperty(
$this->propertyLookup->getPropertyForId( new PropertyId( $title->getText() ) )
$this->propertyLookup->getPropertyForId( new NumericPropertyId( $title->getText() ) )
);
}

Expand Down
9 changes: 6 additions & 3 deletions src/SemanticWikibase.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,18 @@ protected function getPropertyTypeLookup(): PropertyDataTypeLookup {
}

public function registerProperties( PropertyRegistry $propertyRegistry ) {
wfDebug("SWB: register properties ".json_encode($this->getAllProperties()));
foreach ( $this->getAllProperties() as $property ) {
$propertyRegistry->registerProperty(
$property->getId(),
$property->getType(),
$property->getLabel(),
true,
false
true, #is_visible
true, #is_annotable
false #is_declarative
);

wfDebug("SWB: register property ".$property->getId());
wfDebug("SWB: register property ".$property->getType());
foreach ( $property->getAliases() as $alias ) {
$propertyRegistry->registerPropertyAlias( $property->getId(), $alias );
}
Expand Down
89 changes: 85 additions & 4 deletions src/Translation/DataValueTranslator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,33 @@
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\EntityIdValue;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Entity\PropertyId;
use Wikibase\DataModel\Entity\NumericPropertyId;
use Wikibase\EDTF\Services\TimeValueBuilder;
use Wikibase\EDTF\EdtfValue;
use MediaWiki\Logger\LoggerFactory;
use EDTF\EdtfFactory;
use EDTF\Model\ExtDate;
use EDTF\Model\ExtDateTime;
use EDTF\Model\Interval;

class DataValueTranslator {

public function translate( TypedDataValue $typedValue ): SMWDataItem {


$value = $typedValue->getValue();

$propertyType = $typedValue->getPropertyType();
if( $value != null) {
wfDebug( 'swb: translate: '.get_class( $value ).' ptype: '.$propertyType );
}

if ( $value instanceof StringValue ) {
wfDebug( 'swb: translate: '. $typedValue->getValue()->getValue() );
Comment on lines +39 to +43
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid logging raw translated values.

Lines 39/43/129 log raw payload content and humanized values. This can leak sensitive imported data and create noisy debug logs.

Also applies to: 121-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Translation/DataValueTranslator.php` around lines 39 - 43, The debug
statements in DataValueTranslator.php are logging raw translated payloads (e.g.
wfDebug calls that print $typedValue->getValue()->getValue() and similar output
around the StringValue branch and lines ~121-130), which can leak sensitive
data; remove or replace those wfDebug calls so they do not include raw
values—either drop the value content entirely and only log safe context (like
get_class($value) or $propertyType) or log a non-reversible fingerprint (hash)
instead; update the StringValue branch and the other debug locations to use the
sanitized approach and keep the existing wfDebug context messages only.

if( $propertyType == 'edtf') {
return $this->translateEDTF("".$value->getValue());
}else if ($propertyType == 'localMedia') {
return $this->translateLocalMedia("".$value->getValue());
}
return $this->translateStringValue( $typedValue );
}
if ( $value instanceof BooleanValue ) {
Expand All @@ -44,10 +63,13 @@ public function translate( TypedDataValue $typedValue ): SMWDataItem {
if ( $value instanceof GlobeCoordinateValue ) {
return $this->translateGlobeCoordinateValue( $value );
}

if ( $value instanceof TimeValue ) {
wfDebug( 'swb: translate time: '. $$value.toString() );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/Translation/DataValueTranslator.php | sed -n '60,80p'

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 925


🏁 Script executed:

# Check the full context around line 68
head -n 80 src/Translation/DataValueTranslator.php | tail -n 25

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 880


🏁 Script executed:

# Search for TimeValue class definition to understand available methods
rg -n 'class TimeValue|interface TimeValue' --type php

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 59


🏁 Script executed:

# Verify the syntax - check if $$ pattern exists in the file
rg -n '\$\$' src/Translation/DataValueTranslator.php

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 140


🏁 Script executed:

# Find TimeValue class definition
rg -n 'class TimeValue|interface TimeValue' -A 10 --type php

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 59


🏁 Script executed:

# Check methods available on TimeValue
rg -n 'public function' -A 2 --type php | grep -A 2 -B 2 'getTime\|toString'

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 59


🏁 Script executed:

# Look for translateTimeValue method implementation
rg -n 'translateTimeValue' src/Translation/DataValueTranslator.php -A 15

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 1356


🏁 Script executed:

# Check the null return type issue mentioned in scratchpad (Line 140)
head -n 145 src/Translation/DataValueTranslator.php | tail -n 20

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 762


🏁 Script executed:

# Find the method containing line 140
sed -n '100,150p' src/Translation/DataValueTranslator.php | cat -n

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 2022


🏁 Script executed:

# Look for method signatures with return types in that range
rg -n 'private function|public function' src/Translation/DataValueTranslator.php | grep -E '(1[0-3][0-9]|14[0-5])'

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 296


🏁 Script executed:

# Get more context - find what method contains line 140
head -n 145 src/Translation/DataValueTranslator.php | tail -n 50

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 1708


🏁 Script executed:

# Get the complete translateEDTF method
sed -n '120,160p' src/Translation/DataValueTranslator.php | cat -n

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 1730


Fix invalid TimeValue debug expression on Line 68.

$$value.toString() uses invalid variable-variable syntax and will cause a fatal error at runtime when translating TimeValue. This should be replaced with a valid method call on the TimeValue object.

Proposed fix
-		if ( $value instanceof TimeValue ) {
-			wfDebug( 'swb: translate time: '. $$value.toString()   );
-			return $this->translateTimeValue( $value );
-		}
+		if ( $value instanceof TimeValue ) {
+			wfDebug( 'swb: translate time: ' . $value->getTime() );
+			return $this->translateTimeValue( $value );
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Translation/DataValueTranslator.php` at line 68, Replace the invalid
variable-variable expression `$$value.toString()` with a proper method call on
the TimeValue object: call the object's toString() via the PHP object operator
(e.g. $value->toString()) in the wfDebug call inside DataValueTranslator (where
TimeValue is logged) so the debug line uses a valid method invocation on $value.

return $this->translateTimeValue( $value );
}



throw new \RuntimeException( 'Support for DataValue type "' . get_class( $value ) . '" not implemented' );
}

Expand All @@ -66,6 +88,12 @@ private function translateGlobeCoordinateValue( GlobeCoordinateValue $globeValue
);
}

private function translateLocalMedia( String $imagePage): SMWDataItem {
return new DIWikiPage(
$imagePage,
NS_FILE
);
}
private function translateEntityIdValue( EntityIdValue $idValue ): SMWDataItem {
return new DIWikiPage(
$idValue->getEntityId()->getSerialization(),
Expand All @@ -78,7 +106,7 @@ private function entityIdToNamespaceId( EntityId $idValue ): int {
return WB_NS_ITEM;
}

if ( $idValue instanceof PropertyId ) {
if ( $idValue instanceof NumericPropertyId ) {
return WB_NS_PROPERTY;
}

Expand All @@ -89,6 +117,59 @@ public function translateDecimalValue( DecimalValue $value ): SMWDataItem {
return new \SMWDINumber( $value->getValueFloat() );
}

private function translateEDTF( String $value ): \SMWDITime {
wfDebug( 'swb: translate edtf' );
$tvb = new TimeValueBuilder( EdtfFactory::newParser() );
$tvArr = $tvb->edtfToTimeValues( $value );
$parser = \EDTF\EdtfFactory::newParser();
$parsingResult = $parser->parse($value);
wfDebug($parsingResult->isValid()); // true
$edtfValue = $parsingResult->getEdtfValue(); // \EDTF\EdtfValue
$humanizer = \EDTF\EdtfFactory::newHumanizerForLanguage( 'en' );
wfDebug($humanizer->humanize($edtfValue)); // string
wfDebug(get_class($edtfValue)); // string
$result = null;

if ( $edtfValue instanceof Interval ) {
if($edtfValue->hasStartDate()){
$edtfValue = $edtfValue->getStartDate();
} else if ($edtfValue->hasEndDate()){
$edtfValue = $edtfValue->getEndDate();
} else {
wfDebug('ERROR: unable to translate empty edtf interval to smw date');
return null;
}
Comment on lines +120 to +141
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, verify the function signature and null returns as suggested
cd src/Translation && rg -n -C3 'private function translateEDTF\(|return null;' DataValueTranslator.php

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 553


🏁 Script executed:

#!/bin/bash
# Also get the complete function to see all return statements and context
rg -n -A30 'private function translateEDTF' src/Translation/DataValueTranslator.php | head -60

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 1291


🏁 Script executed:

#!/bin/bash
# Check if there are any other return statements in this function
rg -n 'return' src/Translation/DataValueTranslator.php | grep -A5 -B5 'translateEDTF'

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 430


🏁 Script executed:

#!/bin/bash
# Get the complete function from translateEDTF
rg -n -A100 'private function translateEDTF' src/Translation/DataValueTranslator.php | head -120

Repository: ProfessionalWiki/SemanticWikibase

Length of output: 2560


Replace return null with a valid \SMWDITime object on line 140.

translateEDTF() declares a non-nullable return type \SMWDITime but returns null when an Interval has neither a start nor end date. This will throw a TypeError in strict mode.

Suggested fix
 			} else {
 				wfDebug('ERROR: unable to translate empty edtf interval to smw date');
-				return null;
+				return new \SMWDITime( SMWDITime::CM_GREGORIAN, 1970 );
 			}
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 123-123: Avoid unused local variables such as '$tvArr'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Translation/DataValueTranslator.php` around lines 120 - 141,
translateEDTF() declares a non-nullable \SMWDITime return but returns null
inside the Interval branch when neither hasStartDate() nor hasEndDate(); replace
that null with a valid \SMWDITime instance (created via the \SMWDITime
constructor or an available factory method such as a "zero/unknown" or epoch
sentinel) so translateEDTF always returns a \SMWDITime even for empty Interval
objects (update the code inside the Interval else branch that currently does
"return null").

}

if( $edtfValue instanceof ExtDate ) {
$result = new \SMWDITime(
SMWDITime::CM_GREGORIAN,
$edtfValue->getYear(),
$edtfValue->getMonth(),
$edtfValue->getDay()
);

} else if ( $edtfValue instanceof ExtDateTime ) {
$result = new \SMWDITime(
SMWDITime::CM_GREGORIAN,
$edtfValue->getYear(),
$edtfValue->getMonth(),
$edtfValue->getDay(),
$edtfValue->getHour(),
$edtfValue->getMinute(),
$edtfValue->getSecond()
);
} else {
$result = new \SMWDITime(
SMWDITime::CM_GREGORIAN,
1970
);

}

return $result;
}

private function translateTimeValue( TimeValue $value ): \SMWDITime {
$components = ( new TimeValueParser() )->parse( $value->getTime() );

Expand Down
4 changes: 2 additions & 2 deletions src/Translation/ItemTranslator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ public function __construct( FingerprintTranslator $fingerprintTranslator, State
$this->statementListTranslator = $statementListTranslator;
}

public function translateItem( Item $item ): SemanticEntity {
public function translateItem( ?Item $item ): SemanticEntity {
$semanticEntity = new SemanticEntity();

if ( $item->getId() === null ) {
if ( $item === null || $item->getId() === null ) {
return $semanticEntity;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Translation/PropertyTranslator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use MediaWiki\Extension\SemanticWikibase\SMW\SemanticEntity;
use Wikibase\DataModel\Entity\Property;
use Wikibase\DataModel\Entity\PropertyId;
use Wikibase\DataModel\Entity\NumericPropertyId;

class PropertyTranslator {

Expand All @@ -18,10 +18,10 @@ public function __construct( FingerprintTranslator $fingerprintTranslator, State
$this->statementListTranslator = $statementListTranslator;
}

public function translateProperty( Property $property ): SemanticEntity {
public function translateProperty( ?Property $property ): SemanticEntity {
$semanticEntity = new SemanticEntity();

if ( $property->getId() === null ) {
if ( $property === null || $property->getId() === null ) {
return $semanticEntity;
}

Expand All @@ -32,7 +32,7 @@ public function translateProperty( Property $property ): SemanticEntity {
return $semanticEntity;
}

private function addId( SemanticEntity $semanticEntity, PropertyId $itemId ): void {
private function addId( SemanticEntity $semanticEntity, NumericPropertyId $itemId ): void {
$semanticEntity->addPropertyValue(
FixedProperties::ID,
new \SMWDIBlob( $itemId->getSerialization() )
Expand Down
Loading