TheWitnessandTheWitness 3ab4c69fb7 PHPStan Level 8 - Phase 1 Conversion (#8135)
* PHPStan Level 8 - Phase 1 Conversion

Reduces the real (no-baseline) PHPStan level 8 error count from ~2,724 to 425
and regenerates phpstan-baseline.neon accordingly (8,941 -> 1,921 lines).

Core type-safety changes
- lib/database.php: narrow db_fetch_row()/db_fetch_row_prepared() to return
  array (was bool|array) and db_fetch_assoc()/db_fetch_assoc_prepared() to
  return array (was mixed); the _prepared workers coerce the connection-failure
  path to an empty array so the declared type is honest. empty()/cacti_sizeof()
  checks are unaffected.
- lib/database.php: narrow $db_conn guards from is_object() to instanceof PDO in
  the transaction/quote/insert-id helpers so the PDO calls resolve.
- lib/mib_cache.php: the fluent MibCache methods mib()/object()/table()/row()
  now return self instead of a bare object so chained calls type-check.

Type-safety casts and guards (mechanical, behaviour-preserving)
- Cast string|false/|null builtin results feeding string parameters
  (json_decode, base64_encode, trim, htmle, preg_replace, explode, etc.).
- Cast int|string / int|false values feeding int id/pid parameters.
- Guard resource|false fopen()/popen() handles before fwrite/fclose/fread, and
  wrap foreach over array|false/list|false results with `?: []`.

Genuine bugs found and fixed
- cli/add_tree.php / lib/api_tree.php: site tree nodes could not be created via
  the CLI - the --site-id option was never parsed, $nodeTypes had no 'site'
  entry (passing null to api_tree_item_save(int $type) -> TypeError), and
  api_tree_item_save() never persisted site_id. All three fixed.
- tree.php: leaves_exist() is typed : int but returned db_fetch_assoc_prepared()
  (an array of rows) for a SELECT COUNT(*); now uses db_fetch_cell_prepared()
  and returns the scalar count.
- lib/mib_cache.php: $entries_per_object was a float (division / ceil) used as
  an array key (PHP 8.1 implicit float-to-int key deprecation); now cast to int.

Config
- .phpstan.neon: add notIdentical.alwaysTrue and booleanAnd.alwaysFalse to the
  existing always-true/false ignore family (same "PHPStan gets confused"
  dynamic-constant cause already covered by identical.alwaysTrue,
  notEqual.alwaysTrue, booleanAnd.leftAlwaysTrue, etc.).
- phpstan-baseline.neon: regenerated; the remaining 425 entries are the
  untouched long tail for Phase 2.

* Remove redundant @phpstan-ignore comments

Follow-up cleanup for the level 8 Phase 1 work. Removes 14 inline
@phpstan-ignore comments that no longer suppress any reported error:

- cli/repair_database.php: `@phpstan-ignore booleanAnd.alwaysFalse` is now
  covered by the global booleanAnd.alwaysFalse ignore added to .phpstan.neon.
- 13 others (host.php, include/global.php, include/session.php,
  lib/api_automation.php x2, lib/functions.php x2, lib/html_utility.php x2,
  lib/installer.php, lib/package.php, lib/reports.php, lib/rrd.php) no longer
  match any reported error after the type-safety fixes (e.g. the
  db_fetch_row_prepared() return-type narrowing removed the offset-assign
  errors the api_automation ignores were suppressing).

Verified with reportUnmatchedIgnoredErrors enabled: 0 unmatched ignores
remain and the full level 8 run still reports 0 errors.

* ci: satisfy validator ratchet and refresh security sink baselines

- lib/api_tree.php: give the new tree-item site_id a real numeric
  constraint ('^[-0-9]+$', matching html_reports.php) instead of an
  empty regex, so the validator constraint ratchet passes without
  bumping its baseline.
- tests/security/baselines: refresh the three sink/hotspot signatures
  whose code text changed from the (string) casts added in this branch
  (lib/poller.php setsid, cli/rrdresize.php json_encode, lib/import.php
  simplexml_load_string).

* ci(js): allow DOMPurify 3.4.16 and update sync_js pin test

The dependabot bump (#8134) moved package.json/package-lock.json to
DOMPurify 3.4.16 on develop but left tests/js/sync_js.test.mjs pinned to
3.4.15, so the PR merge commit fails the 'DOMPurify is pinned' contract.
Bring package.json/package-lock.json to 3.4.16 (matching develop) and
update the pin test to expect 3.4.16.

* test: update sql.php null-handling assertion for the (string) cast

This branch changed scripts/sql.php from trim(\ ?? '') to
trim((string) \) (the phpstan-safe form that coerces both null and
false from shell_exec()). Update SqlScriptsTest's null-return assertion
to match the new cast instead of the removed '?? ''.

* fix: address Copilot review - ownership IDs, fopen guards, mib_cache bound

- chown()/chgrp(): pass integer UID/GID from fileowner()/filegroup()
  (guarding the false lookup) instead of string-casting them, which made
  them resolve account/group names like '1001' and fail. Covers
  lib/api_device.php, lib/boost.php and poller_maintenance.php (x2).
- fopen() failure now short-circuits instead of reporting success:
  lib/package.php returns false, lib/clog_webapi.php and utilities.php
  render an error, and snmpagent_mibcachechild.php skips the cache write
  and sentinel unlink so a concurrent writer's lock is preserved.
- lib/mib_cache.php: keep the float quotient as the loop bound and cast
  only the array offset, so a non-even entries/columns split no longer
  truncates the loop and drops a cached row.

* revert: keep db_fetch_* returning false on error; baseline Level 8 instead

Per review, restoring the intentional fail-safe: db_fetch_row[_prepared]()
return bool|array and db_fetch_assoc[_prepared]() return mixed again, so a
genuine SQL error (not just a missing connection) is signalled as false and
the === false checks in cdef_is_in_use(), boost_process_* and aggregate.php
work as before. Dropped the is_array() ? : [] coercion.

Level 8 stays green by regenerating phpstan-baseline.neon to capture the
re-introduced bool|array/mixed call-site errors (the ~1,850 the narrowing
had eliminated). Tightening those callers to drop them from the baseline is
Phase 2 work.

---------

Co-authored-by: TheWitness <copilot@users.noreply.github.com>
2026-10-02 10:08:56 -04:00
2026-01-28 16:11:10 -05:00
2026-01-28 16:11:10 -05:00
2026-01-28 16:11:10 -05:00
2026-01-28 16:11:10 -05:00
2026-01-28 16:11:10 -05:00
2026-01-28 16:11:10 -05:00
2025-03-23 12:51:35 -04:00
2026-10-02 06:19:36 -04:00
2026-03-07 12:57:00 -05:00
2026-02-03 12:02:39 -05:00

Cacti

Cacti Commit Audit Project Status Translation Status Average issue resolution time Open issues


Overview

Cacti is an open-source network monitoring and graphing platform built on RRDtool.

It provides a scalable framework to collect, store, and visualize time-series data from network devices, servers, and applications.

Core capabilities include:

  • Automated device discovery
  • Local and remote data collection
  • Graph, data source, and RRA templating
  • SNMP polling (v1/v2/v3) and IPv6 support
  • Role-based access control (RBAC)
  • Plugin framework
  • Dynamic graph viewing and export options

Release Branches

Cacti maintains two primary branches:

Branch Purpose
1.2.x Stable long-lived release series
develop Active development toward Cacti 1.3.x

For the latest published version, see GitHub Releases.


System Requirements

Minimum supported dependencies by branch:

Dependency Cacti 1.2.x Cacti develop (1.3.x)
MariaDB 5.6+ 10.2.x+
MySQL 5.6+ 8.0+
PHP 8.1+ 8.3+
RRDtool 1.4+ 1.8+
Net-SNMP 5.5+ 5.8+

Notes:

  • RRDtool 1.9+ is recommended for newer dynamic graph features in 1.3.x.
  • Net-SNMP 5.9+ is recommended for broader SNMPv3 protocol coverage.
  • A web server with PHP support is required.
  • PHP should be available as CLI or CGI for scheduled polling and maintenance scripts.
  • php-snmp is optional; validate behavior carefully if you depend on IPv6 and SNMPv3.
  • To force the Net-SNMP binaries while php-snmp remains installed, set $php_snmp_support = false; in include/config.php.

Operating system guidance:

  • 1.2.x: RHEL/Rocky/Alma 8+ (or equivalent) is a common baseline.
  • 1.3.x: RHEL/Rocky/Alma 9+ or CentOS Stream 9+ is preferred for modern PHP packaging.
  • Debian and Ubuntu are also well supported.

Installation (Source Checkout)

Clone the repository:

git clone https://github.com/Cacti/cacti.git
cd cacti

Dependency management:

  • For a source checkout on both 1.2.x and develop, install dependencies with Composer before serving Cacti. Composer-managed packages under include/vendor are generated and intentionally not committed; the remaining legacy libraries in that directory are not Composer-managed and remain in the repository.
  • On develop, composer install uses the committed lock file resolved against Cacti's PHP 8.3 floor.
  • Use composer update only in an intentional dependency-update change, and commit the resulting composer.lock update.
  • Release packages must build include/vendor from the lock with composer install --no-dev and verify the real build host with composer check-platform-reqs --no-dev.
  • Tags under release/* build a deterministic archive, SHA-256 checksum, CycloneDX SBOM, and GitHub provenance attestation through the release-artifact workflow.

Install dependencies:

composer install

Windows users may need:

composer install --ignore-platform-req=ext-pcntl --ignore-platform-req=ext-posix

Run nmp install within Cacti installation directory so that to install the required cacti-frontend-assets:

sudo -u <cacti user> npm install
e.g.
sudo -u www-data npm install

Then configure your database and web server, and complete setup using the official docs:

https://github.com/Cacti/documentation


Database Upgrades and Schema Checks

When running from source (especially develop), schema updates may be required after pulling changes.

Upgrade the database schema:

sudo -u cacti php -q cli/upgrade_database.php --forcever=`cat include/cacti_version`

If needed, force a re-upgrade from an earlier version:

php -q cli/upgrade_database.php --forcever=<older_version>

Audit schema consistency:

php cli/audit_database.php --report

Data Collection and Polling

Cacti collects data through data sources, which can use:

  • SNMP
  • Scripts
  • Command output
  • Databases
  • Custom input methods

Polling engines:

Poller Description
PHP Poller Built-in polling engine
Spine High-performance C-based poller

Spine supports SNMPv1/v2 and SNMPv3 with IPv6, with some advanced protocol support depending on how Net-SNMP is compiled on your platform.


Features

  • Device discovery and automation workflows
  • Reusable templates for graphs and data sources
  • Distributed remote data collectors
  • Plugin architecture for extensibility
  • Dynamic graph interactions (time navigation, realtime view, CSV export)
  • Fine-grained user and group permissions using RBAC
  • Broad RRDtool graphing support (including VDEFs and stacked lines)

Documentation

Official documentation is maintained in a separate repository:

https://github.com/Cacti/documentation


Contributing

Contributions are welcome.

  1. Fork this repository.
  2. Create a branch for your change.
  3. Submit a pull request.

You can also help improve docs in:

https://github.com/Cacti/documentation


Community

Community support is available on the Cacti forums:

https://forums.cacti.net


Contributors

Thanks go to these wonderful people (emoji key):

Ian Berry
Ian Berry

💻🛡️📖🎨
Reinhard Scheck
Reinhard Scheck

💻🛡️📖
Tony Roman
Tony Roman

💻🛡️📖🌍⚠️🎨
Andy Blyler
Andy Blyler

💻
Larry Adams
Larry Adams

💻🛡️📖🚇🌍⚠️🎨🚧👀
Mark Brugnoli-Vinten
Mark Brugnoli-Vinten

💻🛡️📖🚇🌍⚠️🎨🚧👀
Jimmy Conner
Jimmy Conner

💻🛡️📖🚇🌍⚠️🎨🚧👀
Petr Macek
Petr Macek

💻🛡️📖👀
Andreas Braun
Andreas Braun

💻📖🌍🎨👀
Jing Chen
Jing Chen

💻🛡️📖👀
Thomas Vincent
Thomas Vincent

💻🛡️📖🚇🌍⚠️🚧👀
Sean Mancini
Sean Mancini

💻📖🚇⚠️👀
J.P. Pasnak, CD
J.P. Pasnak, CD

💻🛡️📖🌍⚠️
Chris Bell
Chris Bell

💻
Paul Gevers
Paul Gevers

💻📖🌍⚠️
Morten Stevens
Morten Stevens

💻
iankberry
iankberry

💻
ronytomen
ronytomen

💻👀
reboot1983
reboot1983

💻👀
micke2k
micke2k

💻🎨
EkaterinePapava
EkaterinePapava

💻🌍
hljtql
hljtql

💻🌍
yeager
yeager

💻
YongBoLiu
YongBoLiu

💻👀
arjanoosting
arjanoosting

💻
Coool
Coool

💻🌍
kim-fitness
kim-fitness

💻👀
earendilfr
earendilfr

💻📖
purplegrape
purplegrape

💻🌍
Сергей
Сергей

💻🌍
DavidLiedke
DavidLiedke

💻🛡️📖🌍
MSS970
MSS970

💻👀
SjonHortensius
SjonHortensius

💻
thurban
thurban

💻👀
riversdev0
riversdev0

💻👀
chilek
chilek

💻👀
MarcBanyard
MarcBanyard

💻
bernisys
bernisys

💻
ka1lie
ka1lie

💻
Dmitry
Dmitry

💻🌍
sysres-dev
sysres-dev

💻👀
rb83
rb83

💻👀
click0
click0

💻
smiles1969
smiles1969

💻
gadzet21
gadzet21

💻
rlaager
rlaager

💻
paulcalabro
paulcalabro

💻
mhoran
mhoran

💻👀
Javier
Javier

💻📖🌍
frontierliu
frontierliu

💻👀
xbolshe
xbolshe

💻
arno-st
arno-st

💻
c72578
c72578

💻
C00kiekiller
C00kiekiller

💻
nuno-silva
nuno-silva

💻
pautiina
pautiina

💻👀
interduo
interduo

💻🌍
seanmancini
seanmancini

💻⚠️
jav4
jav4

💻
fabriciotm
fabriciotm

💻
eriksejr
eriksejr

💻
hb9xar
hb9xar

💻
nightflyza
nightflyza

💻
nbz6
nbz6

💻
iaohkut
iaohkut

💻
Nakayama Kito
Nakayama Kito

💻🌍
SMark-Black
SMark-Black

💻
Givo29
Givo29

💻
GregOriol
GregOriol

💻
gvde
gvde

💻👀
dallenk
dallenk

💻👀
dvzrv
dvzrv

💻
ctrowat
ctrowat

💻
Beuc
Beuc

💻🛡️
panlinux
panlinux

💻
begemoti
begemoti

💻
dsclassen
dsclassen

💻
scline
scline

💻
sky93
sky93

💻
SergioChan
SergioChan

💻
ShaunR
ShaunR

💻
sebastienwarin
sebastienwarin

💻
tbriceno-hub
tbriceno-hub

💻
tyearke
tyearke

💻
Vladyslav78956785786576
Vladyslav78956785786576

💻
aliuzzz
aliuzzz

💻
UnAfraid
UnAfraid

💻
robwdwd
robwdwd

💻
Poslik
Poslik

💻
Xake
Xake

💻
Cosmologist
Cosmologist

💻
PVi1
PVi1

💻
OhWelp
OhWelp

💻
TheNetworkIsDown
TheNetworkIsDown

💻
tersmitten
tersmitten

💻
F-i-f
F-i-f

💻
terrellgf
terrellgf

💻⚠️
surfermarty
surfermarty

💻
aqr199
aqr199

💻
snsupersharp
snsupersharp

💻🌍
skyjou
skyjou

💻
skeletorgithub
skeletorgithub

💻
richud
richud

💻
oshiewan
oshiewan

💻
oderturm
oderturm

💻
stevenseeley
stevenseeley

💻
mcnc-clovett
mcnc-clovett

💻
m8522s
m8522s

💻
linusat
linusat

💻
kruegerj
kruegerj

💻
kotso
kotso

💻
jsgh
jsgh

💻
jkbzh
jkbzh

💻📖
itaxer
itaxer

💻
dwpaulx
dwpaulx

💻
ahouston
ahouston

💻
xavloose
xavloose

💻
evansa
evansa

💻
eduardomozart
eduardomozart

💻
edewillians10
edewillians10

💻
dimaslv
dimaslv

💻👀
simfishing
simfishing

💻
duhow
duhow

💻
dschall
dschall

💻
dschultzca
dschultzca

💻
kripper
kripper

💻
3bars
3bars

💻
kornrunner
kornrunner

💻
carryel
carryel

💻
dabeani
dabeani

💻
xubowin
xubowin

💻
barreljan
barreljan

💻👀
athos-ribeiro
athos-ribeiro

💻
apinheiro
apinheiro

💻
sashashura
sashashura

💻
A200K
A200K

💻
k0pak4
k0pak4

💻
TheBlueMatt
TheBlueMatt

💻
mchorsley
mchorsley

💻👀
luismrsilva
luismrsilva

💻
daniel-lucio
daniel-lucio

💻
Luhrel
Luhrel

💻
stslayer1
stslayer1

💻
KrisShannon
KrisShannon

💻
devsibwarra
devsibwarra

💻👀
kersten-lohmeyer
kersten-lohmeyer

💻
Harry-Junhua-Huang
Harry-Junhua-Huang

💻
jsoref
jsoref

💻⚠️👀
JonBogan
JonBogan

💻📖
jayaich
jayaich

💻
jab60
jab60

💻
jkrinke
jkrinke

💻
JamesTilt
JamesTilt

💻
neclimdul
neclimdul

💻
hmorandell
hmorandell

💻
gdsotirov
gdsotirov

💻
inode64
inode64

💻
00gh
00gh

👀
Dmitrii
Dmitrii

👀
Patrick
Patrick

💻🛡️📖🌍
Daniel Nylander
Daniel Nylander

💻🌍
Anton Zhuravlev
Anton Zhuravlev

💻🌍
John Horne
John Horne

💻🌍
Vladyslav V. Prodan
Vladyslav V. Prodan

💻🌍
Ronny Preiss
Ronny Preiss

💻🌍
Rostyslav Haitkulov
Rostyslav Haitkulov

💻🌍
Nicolas BUTIN
Nicolas BUTIN

💻🌍
olafhering
olafhering

💻
Mojo-OG
Mojo-OG

💻
abdulm5
abdulm5

💻
darkdi
darkdi

💻

This project follows the all-contributors specification. Contributions of any kind are welcome.


License

Cacti is licensed under the GNU General Public License v2.0.

See LICENSE for details.

Copyright (c) 2004-2026 The Cacti Group, Inc.

Languages
PHP 52%
JavaScript 34.7%
CSS 8.9%
SCSS 1.3%
Less 1.2%
Other 1.8%