Repository navigation
Dead-code audit with a code-graph tool (plouf.rs) - Things to improve so tools can better understand the codebase #20477
Description
I'm Claude (Anthropic's Claude Code assistant). @williamdes asked me to open this issue.
phpMyAdmin was used as a real-world test case while improving a static code-graph / dead-code tool, plouf.rs (Rust; PHP via Mago + JS via oxc; MPL-2.0). A first pass flagged 57 PHP symbols and 24 TS symbols as "unreferenced", and each was then checked against the source.
Most are false positives from a dispatch or registration mechanism a static pass cannot follow — but not all. The five methods this issue originally listed as live were in fact dead, and eight methods in total have since been removed (#20479, #20480). Below is the state of the list: removed entries moved to their own section, every remaining bullet re-verified against the tree. A checked box = verified live.
Removed — these were dead, not live
The bullet that stood here read: ExportRelationSchema::isOffline, PdfRelationSchema::isWithDataDictionary, Server\Plugin::getName, Server\SysInfo\Base::isSupported — "config/predicate accessors read through typed receivers or exercised only by the suite". That was an unchecked generalisation. All four were dead, as was DesignerTable::getTableEngine from the Designer bullet above them.
Server\SysInfo\Base::isSupported()— PR Refactoring of SysInfo #20479. No caller anywhere, and it returned a hardcodedtrue.SysInfo::get()calls only the platform subclasses' overrides (src/Server/SysInfo/SysInfo.php:49,55,61) and then doesreturn new Base();at:68.DesignerTable::getTableEngine(),ExportRelationSchema::isOffline(),PdfRelationSchema::isWithDataDictionary(),Server\Plugin::getName()— PR Remove unused getters #20480 "Remove unused getters", which also caughtDesignerTable::getDatabaseName()/getTableName()/getDisplayField().
Server\Plugin::getName() was the one case where "exercised only by the suite" did apply, but that is not production use: tests/unit/Server/PluginTest.php:33 was its only reference, production reads toArray(), and the Twig {{ plugin.name }} is array-key access on the array toArray() returns, not a getter call.
Eight methods in total across the two PRs. The failure mode was generalising one verified case across a group; the rest of this list had the same treatment and has now been re-checked individually.
PHP — verified live
- Schema drawing methods —
RelationStats*/TableStats*::relationDraw/tableDraw, andEps/Svg/Pdfline/rect/showXY/printElement/cellScale/lineScale/setXScale/setXyScale— called on an abstract-base-typed receiver ($relation->relationDraw(...)where$relationis aRelationStats), with the method defined only in subclasses, so no static call resolves to the concrete class.src/Plugins/Schema/Pdf/PdfRelationSchema.php:423,src/Plugins/Schema/Svg/SvgRelationSchema.php:257. -
Pdfoverrides —src/Pdf.phpError/Footer/_putpages,src/Plugins/Schema/Pdf/Pdf.phpHeader/Footer/cellScale/… — override a vendored base (class Pdf extends TCPDF,src/Pdf.php:24); TCPDF dispatches them, so there is no in-repo caller. -
Database/Designer/Common(addNewRelation,createNewPage,getAllKeys,getColumnsInfo,getTablesInfo,saveTablePositions, …) — called through typed-property / return-typed receivers from the Designer controllers. -
Partitioning/Partition(addSubPartition,getSubPartitions,hasSubPartitions,getDescription) —addSubPartitionis called on a same-class receiver (src/Partitioning/Partition.php:171); the other three are Twig calls, which a PHP call graph cannot see:partition.getDescription()(resources/templates/table/structure/display_partitions.twig:53),partition.getSubPartitions()(:152),first_partition.hasSubPartitions()(resources/templates/table/structure/display_structure.twig:586). Twig resolves those to method calls at render time. -
Gis—Ds\Point::isEqual(src/Gis/Ds/Point.php:64, called at:24andsrc/Gis/Ds/Polygon.php:34),GisGeometry::prepareRowAsOl(src/Gis/GisVisualization.php:483) — dispatched on aGisGeometry-typed receiver / value objects. -
Plugins/Import—ImportFormat::getClassName(src/Controllers/Import/ImportController.php:476),ShapeFileImport::readSHP/eofSHP— these override and are called by the vendored base class:vendor/phpmyadmin/shapefile/src/ShapeFile.phpdeclares them at:383/:397and callsreadSHPat:404and both at:481/:490, plusShapeRecord.php:177.ImportShppasses''(src/Plugins/Import/ImportShp.php:169) to select the in-memory-buffer path, so the overrides are reached at runtime with no in-repo call site. -
src/InsertEdit.phpanonymous class — anew class { … }closure holder, referenced where it is created.
TS — verified live
-
resources/js/table/gis_visualization.ts—SvgVisualization.on*(drag/wheel/zoom/resize/arrow/…),dispose,hide,onChoiceChange— bound as event handlers viathis.onX.bind(this)thenaddEventListener(:141-154,:637), plussuper.dispose()(:260,:623), jQuery.hide()(:51,:643) andoldVis?.hide()(:660). The methods are used as values, so no call site names them;disposeis also called fromresources/js/gis_data_editor.ts:19. -
resources/js/modules/functions.ts—autoPopulate,confirmDialog,getPostData,sortTable— registered as globals / jQuery plugins:$.fn.confirm = confirmDialog(:3739),$.fn.sortTable = sortTable(:3740),$.fn.getPostData = getPostData(:3741),window.pmaAutoPopulate = autoPopulate(:3745); andsortTableis called as$(x).sortTable('.name')(resources/js/server/databases.ts:72).
Where the tool stops (the plouf gaps this exposes)
Not phpMyAdmin bugs — the to-do list on the analyzer side:
- A method/function used as a value is a reference —
this.onX.bind(this),window.X = fn,$.fn.X = fn,[$obj, 'method']. Would clear the whole gis-visualization +functions.tsset. - Overriding a vendored (out-of-repo) base class (
Pdf extends TCPDF,ShapeFileImport extends PhpMyAdmin\ShapeFile\ShapeFile) — the override is dispatched, and often called, by the base; it should not read as dead.readSHP/eofSHPare the sharpest case: the vendored package both declares and calls them, and the call is simply not in the indexed tree. - Polymorphic dispatch on an abstract-base-typed receiver where the method is defined only in subclasses (the Schema drawing classes).
Filing under phpMyAdmin only so the audit trail lives with the code it examined. Nothing further to action here.
Activity
[-]Dead-code audit with a code-graph tool (plouf.rs) — no dead code found (transparency report)[/-][+]Dead-code audit with a code-graph tool (plouf.rs) - Things to improve so tools can better understand the codebase[/+]kamil-tekiela commentedon Sep 8, 2026
williamdes commentedon Sep 8, 2026
williamdes commentedon Sep 22, 2026
I make DeepSeek update this report continuing the session Athropic had started.