# Technical documentation — userModule

Evidence date: 2026-09-29. Revision: `master` `a8971a4` (2019-06-24).

Credential values are omitted. Host names, the database name, the MySQL user, and column names below are taken from source.

## What this repository is

A Bitbucket Git repository of a PHP/MySQL user form. Four pages share one mysqli connection. A Smart Admin 2.0 HTML theme is vendored beside them and is not wired to those pages.

There is one branch, `master`. 38 commits, from 2019-06-10 through 2019-06-24. 426 tracked files. 415 of those are under `template/`.

`sonar-project.properties` and `sonarqube_scan.bat` were listed by the onboarding scan and are not in git history.

Authors: Jaco Brits (`jaco@overdrive.co.za`) created the repository and added the template. Pieter (`pj@overdrive.co.za`, commit author `SILICONOVERDRIV\pieter`) edited the README styling. Banele Mlamleli wrote the PHP (`mlamlelibanele@yahoo.com`, then `banele@overdrive.co.za`).

## Repository layout

```text
usermodule/
  README.md                          Phase 1–4 task list. Title "Unity : userModule"
  .gitignore                         Ignores .vscode/ and settings.json
  .vscode/launch.json                Xdebug listen and launch, port 9000 (still tracked)
  dbconnection.php                   mysqli by SERVER_NAME
  projectFunction.php                print_rr
  formValidation.php                 Unused stub
  clientsideUserFormValidation.js    validation()
  login.php                          Email + MD5 compare
  registrationForm.php               Create user
  userForm.php                       Create user (second copy) and one-row echo
  editUser.php                       List, update, delete
  template/                          Smart Admin 2.0 static demo
```

No `composer.json`, `package.json`, `Dockerfile`, `bitbucket-pipelines.yml`, test directory, or `.sql` file.

`.gitignore` lists `.vscode/`, but `launch.json` was committed and remains tracked. Git does not drop a tracked file when a later ignore rule matches it.

## Runtime

| Piece | Evidence |
|---|---|
| Language | PHP, `mysqli_*` and one `new mysqli` in `dbconnection.php`. Short open tags are not used |
| PHP version | Not pinned |
| Database client | `mysqli`. The server product (MySQL or MariaDB) and version are **Unknown** |
| Front end | Bootstrap 4.3.1 CSS and JS from `maxcdn.bootstrapcdn.com`, jQuery 3.4.0 from `ajax.googleapis.com`, Popper 1.14.7 from `cdnjs.cloudflare.com` |
| Local script | `.\clientsideUserFormValidation.js` (a Windows-style relative path in the script tag) |
| Process model | One PHP request per page. No framework, router, or front controller. The form `action` is `<?php $_PHP_SELF ?>`, which prints nothing, so the browser posts back to the current URL |
| Sessions | Not used |
| Debugger | `.vscode/launch.json` port 9000, type `php` |

The pages assume the web server's document root is the repository root, because links are `./login.php`, `./userForm.php`, `./editUser.php`, and `./registrationForm.php`.

## Database

No schema is committed. Commit `8861c85` ("Created Database with tables and fields") does not add SQL. The column list is the insert in `userForm.php` and `registrationForm.php`, plus `intUserID`, which `editUser.php` prints and never writes.

| Column | Written by | Notes |
|---|---|---|
| `intUserID` | Not in the insert | Printed as the list row label. README asked for auto-increment `use_id`. Whether this column is `AUTO_INCREMENT` is **Unknown** |
| `txtFirstname` | Insert, update | String bind |
| `txtLastname` | Insert, update | String bind |
| `txtIDNumber` | Insert, update | Also the `WHERE` value for update and delete |
| `txtDOB` | Insert, update | From `<input type="date">` |
| `txtGender` | Insert, update | `Male` or `Female` |
| `txtUserType` | Insert, update | Option label |
| `txtEmail` | Insert, update | Login identity |
| `txtPassword` | Insert only | MD5 hex digest. Update leaves it unchanged |
| `txtUserDateCreated` | Insert, update | `Y-m-d` |
| `txtStatus` | Insert, update | `Active` or `Inactive` |
| `txtUserNote` | Insert, update | Free text |

A comment in `userForm.php` names an older shape (`usr_firstname`, `usr_lastname`, `usr_IdNumber`, `usr_dob`, `usr_gender`, `usr_userType`, `usr_userDateCreated`, `usr_status`, `usr_userNote`). That statement is not executed. Its placeholder count does not match its `bind_param` type string.

Every bind uses `s` (string). Indexes, nullability, and character set are **Unknown**.

## Connection selection

`dbconnection.php`:

| `$_SERVER["SERVER_NAME"]` | Host | User | Password | Database |
|---|---|---|---|---|
| `localhost` | `localhost` | `root` | empty string | `usermodule` |
| `lamp` | `lamp` | `root` | non-empty, committed in this file, omitted here | `usermodule` |
| anything else | empty string | empty string | empty string | empty string |

Failure: JavaScript `alert('Database Connection failed')`, then `die($conn->connect_error)`, which prints the driver message into the response. The success alert is commented out.

`projectFunction.php` is included on every connection. `print_rr($value, $debug = false)` wraps `print_r`. The second argument of PHP `print_r` is a return flag, not a debug switch. Nothing calls `print_rr`.

## Request map

| File | Methods that do work | SQL |
|---|---|---|
| `login.php` | POST `submit` | `SELECT * FROM `user`` then compare in PHP |
| `registrationForm.php` | POST `submit` | `SELECT *` for the ID scan, then prepared `INSERT` |
| `userForm.php` | POST `submit` | Same as registration |
| `editUser.php` | GET `update`, GET `delete`; GET with no button still lists | `UPDATE` (prepared, ID concatenated), `DELETE` (ID concatenated), `SELECT *` |

Update SQL, abbreviated:

```text
UPDATE `user` SET txtFirstname=?, txtLastname=?, txtIDNumber=?, txtDOB=?,
  txtGender=?, txtUserType=?, txtEmail=?, txtUserDateCreated=?, txtStatus=?, txtUserNote=?
WHERE txtIDNumber = $id
```

`$id` is `mysqli_real_escape_string` of the query-string value and is not inside quotes or a placeholder. Delete is `DELETE FROM `user` WHERE txtIDNumber = $id` with the same interpolation.

Create and login hash with `md5(mysqli_real_escape_string(...))`.

`login.php` starts with an HTML comment, then the PHP block that may call `header("Location: userForm.php")`. Output has already begun, so a default PHP configuration rejects that header.

## Client script

`validation()` in `clientsideUserFormValidation.js` is one function. It always reads these ids: `firstname`, `lastname`, `id_number`, `dob`, `email`, `password`, `confirmPassword`, `gender_male`, `gender_female`, `active`, `inactive`.

| Page | Calls `validation()` | Elements present |
|---|---|---|
| `userForm.php` | Submit | Yes |
| `registrationForm.php` | Submit | Yes |
| `login.php` | Submit | Only `email` and `password`. The rest are null. Reading `.value` throws |
| `editUser.php` | Update only | No `password` or `confirmPassword`. Reading them throws. Delete does not call the function |

Name test uses logical or. Status test uses logical or. Password match uses `===`. Email uses the regex in the PRD. ID length is 13.

The edit row-click script is inline at the bottom of `editUser.php`. Cell indexes: 1 date created, 2 first name, 3 last name, 4 ID, 5 date of birth, 6 email, 8 gender, 9 status, 10 notes. Index 7 (user type) is not copied.

## Template

`template/index.html` is "Dashboard | Smart Admin 2.0". Assets live under `template/assets/` (`app.bundle.js`, `app.bundle.css`, `vendors.bundle.js`). Demo pages include login, register, profile, invoice, and gallery HTML that are not the application. The application pages share the class name `smart-style-0` with the template and then load Bootstrap 4 from a CDN instead of `template/assets`.

Commit `c3de433` (2019-06-11, Jaco Brits, `#161 Setup tasks and adding template`) added this tree.

## `formValidation.php`

Entire file:

```php
echo "<script type='text/javascript'>alert('$msg');</script>";
echo "PHP file executed";
```

`$msg` is undefined. No other file includes this one. Commit `ff5fbe7` (2019-06-13) added it with the subject "Added form validation file. No code as yet".

## Security observations

These are properties of the code, recorded so the documentation matches the implementation.

- MD5 is the password transform the README names and the only transform in the PHP.
- The `lamp` MySQL password is in `dbconnection.php` in git. It is omitted here.
- `localhost` uses `root` and an empty password.
- Login loads every user and compares in the application.
- Update and delete accept the ID from the query string and concatenate it into SQL.
- Failed connects print `connect_error` to the client.
- There is no CSRF token, no auth gate, and no security header in the repository.

## Development and test

No PHPUnit, no fixture, no seed file, and no CI definition. The practical debug aid in git is `.vscode/launch.json`.

Manual runs implied by the code: a PHP-capable host whose `SERVER_NAME` is `localhost` or `lamp`, with database `usermodule` and a `user` table that already has the columns above. How that table was created is not in git.

## Monitoring, jobs, storage, deploy

Not present: cron, queues, workers, object storage, log configuration, health checks, Docker, and a pipeline. Sonar files named by the scan host are not in this revision.

## File sizes

The application scripts are short: `editUser.php` is 253 lines, `registrationForm.php` 246, `userForm.php` 258, `login.php` 88, the validation script 69. The bulk of the repository is the unused theme (`template/assets/app/app.bundle.css` and the vendor bundle).
