# Developer Reference — Shared Libraries & Dev Tools

**Folders:** `CRUD/functions/`, `CRUD/z-dev-tools/`

These have no user interface. `functions/` is the shared code every page relies on; `z-dev-tools/` is developer tooling that is **denied at the webserver** (`Require all denied`) and run from the command line.

---

## `functions_loader.php` — the backbone

**Included by 227 files.** Requiring it gives a page the shared helper libraries *and* a set of pre-built dropdown HTML variables.

```php
require_once $_SERVER['DOCUMENT_ROOT'].'/CRUD/functions/functions_loader.php';
```

It auto-loads nine libraries:

| Library | Provides |
|---|---|
| `hire_rules.php` | `hire_job_type_has_driver()`, `hire_driver_for_job_type()` |
| `functions_pdi.php` | `loadModels()`, `loadPlantList()`, `loadPdiChecklistTemplate()`, `loadPdiModelOverrides()`, `nextPdiNumber()` … |
| `functions_delivery_docket.php` | `buildPlantOptions()` |
| `functions_dropdown_handler_cus_sites.php` | `loadCustomerAccs()` |
| `functions_images.php` | `getImagesForHireID()` |
| `PlantData.php` | `Hickey\Plant\PlantData` — `fetchAllSimple()`, `loadPlantFull()`, `filterPlant()`, `fetchExcavatorsByWeight()` |
| `DropdownBuilders.php` | 11 dropdown builders (fleet, customer, site, job type, driver, hire status, X-hire…) |
| `AdditionsBuilder.php` | `Hickey\Additions\AdditionsBuilder::buildAdditionsHtml()` |
| `Helpers.php` | `Hickey\Utils\Helpers` — `h()`, `formatNiceDate()`, `getRowColor()`, `renderReadyCell()` |

It then **executes queries at include time** to populate globals used directly in page markup:

`$opsfleetno`, `$opsjob`, `$opsdriver`, `$opshirestatus`, `$opsxh`, `$opsxhpld`, `$opsldriver`, `$delveh`, `$driverVehicleMap`, `$customerOptions`, `$opsAdditions`

It also defines `asset_url()`, which appends `?v=<mtime>` to a static asset path as a cache-buster.

> ⚠️ Because the loader runs DB queries on include, requiring it is not free. Endpoints that only need one helper are better off including that file directly (as `upload_guard.php` and `ocr_python.php` are).

## Included directly, not via the loader

| Library | Included by | Provides |
|---|---|---|
| `upload_guard.php` | **11 files** | `validate_uploaded_document()`, `store_uploaded_document()`, `store_uploaded_documents()` — extension whitelist + **magic-byte** validation. The standard way to accept an upload; added during the [security review](../security-audit-2026-07.md) |
| `ocr_python.php` | 4 files | `findOcrPython()` — locates the Python interpreter for the OCR extractor service |

## `hire_rules.php` — a business rule in code

Small but load-bearing. Only **Operated** and **Operator only** job types carry a driver; every other type clears it.

```php
$drivers_name = hire_driver_for_job_type($job_type_name, $drivers_name);
```

Applied in the five handlers that write both `job_type_name` and `drivers_name`. Matching is **case-insensitive** because the value arrives with inconsistent casing (`job_type` holds `"Operator only"`, `update_history_op.php` posts `"Operator Only"`). See [hire-list.md](hire-list.md#editing-a-hire--hire_updates).

## Sub-directories

| Path | Contents |
|---|---|
| `functions/ajax/` | `load_sites.php`, `load_site_move.php` — site dropdown feeds |
| `functions/js/` | `customer_site_dropdown.js`, `pdi_dropdown.js` — the client half of those dropdowns |

---

## `z-dev-tools/` — developer tooling

**Not web-reachable** — the folder carries `Require all denied`. Run these from the command line.

| Tool | Purpose |
|---|---|
| `build_page_inventory.php` | Regenerates [page-inventory.csv](../page-inventory.csv) — every app page with type, size, auth status, link status and orphan category. **Preserves the Reviewed / Bug / Documented / Screenshot / Notes columns** on re-run |
| `clear_stale_drivers.php` | One-off backfill clearing driver names left on non-operated hires. Dry run by default; `--apply` to commit; `--user="Name"` for the audit trail |
| `code_search.php` | Search the codebase |
| `find_unused_functions.php` | Find functions with no references |
| `scan_function_references.php` | Reference scanner |
| `file_list.php` | File listing helper |
| `start_ocr_services.{bat,sh}` / `stop_ocr_services.sh` | Start/stop the OCR extractor service (port 8012) |
| `phpinfo.php` | PHP configuration dump |

```bash
php CRUD/z-dev-tools/build_page_inventory.php          # refresh the inventory
php CRUD/z-dev-tools/clear_stale_drivers.php           # dry run
php CRUD/z-dev-tools/clear_stale_drivers.php --apply   # commit
```

> The inventory's reference detection matches on **basename**, so a stale copy sharing a filename with a live file inherits its "live" status (this happened with `reports/.../back up/`). When judging whether something is dead, confirm the **path** is referenced, not just the name.

---

## Known issues

Reviewed 2026-07-21.

| # | Issue | Status |
|---|---|---|
| 1 | **`holidays.php` and `holidays2.php` are unreferenced — and dangerous to wire in.** 12 report files under `reports/.../month_reports/` each declare their own `number_of_working_days()`, which also exists in `holidays2.php`. They don't clash only because nothing includes it. Adding either to `functions_loader.php` would **instantly fatal 12 report pages**, and `holidays2.php`'s copy has bank holidays **hardcoded for 2019–2020 only**, so it would also corrupt working-day calculations. | **Open** — recommend deleting both |
| 2 | `get_prices.php` and `get_prices_byid.php` are unreferenced. The live price lookup is `CRUD/prices/get_customer_prices.php` — a different file. | **Open** — confirm then delete |
| 3 | `PlantData.php` had **10 SQL references hardcoded to the live database name**, which broke `yard_list.php` on local and test. | **Fixed** during the [hire-list review](hire-list.md#known-issues) |
| 4 | `functions_images.php`'s `getImagesForHireID()` was **redeclared locally** in `site_move_read.php`, fatalling that page. | **Fixed** during the [hire-support review](hire-support.md#known-issues) |
| 5 | `z-dev-tools/phpinfo.php` dumps full PHP configuration. Harmless while the folder is denied, but it is one `.htaccess` change away from being exposed. | **Open** — low risk, worth deleting |
