Skip to content
All projects
LGU Lead Developer & System Administrator 2016 – present

ACCESS — LGU HRIS & Payroll

The human resource and payroll backbone of a local government unit: personnel records, payroll processing, daily time records, service records and pre-audit document routing, used across dozens of offices every working day.

5,000+

Employees served

5.5 → 8.2

PHP version jump

0

Payroll runs missed

10+

Years in production

01 · The problem

What was wrong

ACCESS was a large CodeIgniter 3 application running on PHP 5.5 under WampServer on a Windows box, talking to Microsoft SQL Server. It worked, and that was precisely the problem — it worked well enough that nobody could afford to take it offline long enough to modernise it.

The stack was years past end of life. Security patches had stopped. Newer libraries would not install. And every module carried assumptions from a PHP 5 world: curly-brace string offsets, implicit type juggling, functions removed in PHP 7 and 8. A naive upgrade would have taken payroll down for an entire local government.

02 · The approach

What I did

I treated the upgrade as a migration campaign rather than a rewrite, moving in reversible steps and keeping the old environment bootable at every point.

  • Audited the codebase module by module for PHP 8 incompatibilities, rather than upgrading and fixing whatever crashed first.
  • Stood up a parallel Linux staging tier so the new PHP 8.2 behaviour could be compared against production on real data before any cutover.
  • Solved the MSSQL-from-Linux problem properly. Connecting via FreeTDS and pdo_dblib surfaced failures that never appeared on Windows: ANSI SET options default to OFF, so queries using SQL Server's XML methods silently returned empty results — an empty payroll register with no error anywhere. Fixing that meant setting the ANSI options per connection rather than patching each query.
  • Hardened date handling, after finding that driver-returned datetimes on the new server did not parse with date_create(), returning false and throwing a fatal TypeError on PHP 8 — a silent 500 and an empty grid to the user.
  • Kept deployment boring: git-based, with a documented rollback and an explicit restart step, after learning the hard way that a pull alone does not clear OPcache.

03 · The outcome

What changed

ACCESS runs on PHP 8.2 against SQL Server from Linux application servers, with the legacy Windows environment retired from the critical path. No payroll cycle was missed during the migration.

The less visible outcome matters more: the codebase is now on a supported runtime, which means it can keep receiving security updates and new modules instead of slowly becoming unmaintainable.

Highlights

  • Migrated a live 15-year-old codebase from PHP 5.5 to 8.2 with no missed payroll run
  • Diagnosed silent empty-result failures caused by FreeTDS ANSI SET defaults
  • Moved the application tier from Windows/WampServer to Linux without a rewrite
  • Built and documented the staging → production deployment runbook the team still uses

Built with

  • PHP 8.2
  • CodeIgniter 3
  • Microsoft SQL Server
  • FreeTDS / pdo_dblib
  • Apache
  • Linux
  • jQuery
  • Bootstrap
  • DataTables
  • Proxmox

Next project

AMD — Mobile Daily Time Record

See how it was built