# PRD — react-php-tutorial

Evidence date: 2026-10-05.

Source: `git@bitbucket.org:overdrive/react-php-tutorial.git`, `master` at `d10301a` (2020-04-29).

There is no prior PRD in Silicon Ark and no README in the source repository. Requirements below are recovered from `srcHooks/`, `php/`, and the five-commit history. Provenance is labelled. The database password in `php/db_connection.php` is an empty string; it is not repeated as a sample people should copy into a shared environment.

## Product purpose

**Implemented.** A browser UI lets a person submit a name, email, and password to PHP scripts that read and write a MySQL database named `login`. A second screen submits a name and password and receives a numeric login result. A table component can list users. Add and edit modals exist as files.

**Documented.** Nothing in the repository states a purpose, a user, or a success metric. The navbar label is `React-php`. The commit messages name the work: sign in, sign up, a Bootstrap table, React hooks, then "actions (1/2)" and "add user, delete user".

**Inferred.** The repository name and the unfinished wiring (empty `update.php`, components that are not routed, a commit that says actions are only half done) indicate a personal tutorial, not a finished product.

**Unknown / Requires Confirmation:** who it was for, and whether it should stay as a tutorial record or be treated as an application someone still runs.

## Users and interactions

No role table, login session, or permission check is active.

| Actor | Evidence | Status |
|---|---|---|
| Person using the React routes | `App.js` links to `/signIn` and `/signUp` with no gate | **Implemented** |
| Person calling PHP directly | `listView.php` and `delete.php` do not read a session or a token | **Implemented** |
| Signed-in user | `session_start()` and `$_SESSION` assignments in `signIn.php` are inside a comment. `SaveLog()` reads `$_SESSION['id']` and is not called | **Not active** |
| User group / role | `signIn.php` comments mention `$_SESSION['userGroup'] = $row['refUserGroupID']`. No column is selected into a response, and no script branches on it | **Commented only** |
| Automated test | `App.test.js` renders `<App />` and looks for `/learn react/i` | **Present, does not match the UI** |

`refUserGroupID` appears only in that comment. Whether the `user` table has the column is **Unknown**.

## Systems the product talks to

1. MySQL on `localhost`, database name `login`, account `root`, empty password, via `mysqli_connect` in `php/db_connection.php`. Failure ends the script with `Connection failed:` plus `mysqli_connect_error()`. Whether any machine still has this database is **Unknown**.
2. The browser calls `http://localhost/php-react/signIn.php`, `signUp.php`, and `listView.php`. The git folder is `php/`, not `php-react`. The web-server mapping is **Unknown / Requires Confirmation**.
3. No mail server, queue, object store, or third-party API is called. `php/functions.php` contains the comment "email function still need to be added".

## Constraints that are visible

- API base URL is hardcoded to localhost. There is no environment file.
- Passwords are written and compared as the submitted string. No hash function appears in `php/`.
- SQL is built by concatenating request values. No prepared statements appear.
- `header.php` sends `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Methods: POST`. `signIn.php` and `signUp.php` repeat those headers instead of including `header.php`.
- Responses often mix debug output (`var_dump`, `print_r` of the SQL, `echo` of the DELETE statement) with the status the UI would need.
- The React app cannot be installed from git: `package.json` and `public/index.html` are absent.

## Major workflows

### WF-1 — Open the shell

**Trigger:** a bundler renders `srcHooks/index.js` into an element with id `root`. That HTML file is not in git.

**Behaviour:** `index.js` mounts `<App />` in `React.StrictMode` and calls `serviceWorker.unregister()`. `App.js` shows a light navbar, the brand link `React-php` to `/`, a Sign In link to `/signIn`, and a Sign Up link to `/signUp`.

**Routes:**

| Path | Element |
|---|---|
| `/` | No `<Route>`. Navbar only |
| `/signIn` | `SignIn` |
| `/signUp` | `SignUp` |

`ListView`, `AddUserModal`, and `EditUserModal` are not imported by `App.js`. Provenance: **Implemented**.

**Errors:** `App.test.js` expects the text "learn react", which `App.js` does not render. Provenance: **Implemented** leftover from the Create React App starter.

### WF-2 — Sign up from the Sign Up page

**Trigger:** submit on `/signUp`.

**Client inputs:** controlled fields `name`, `email`, `password`. If `email` or `password` is `""`, `setError("Fields are required")` and the function returns. The `error` state is never rendered. An empty name is allowed through this check. Provenance: **Implemented** in `SignUp.js`.

**Request:** `axios.post("http://localhost/php-react/signUp.php", { user_name, user_email, user_password })`. The promise result is `res.data` and is not shown. `useHistory` is imported from `react` (that export belongs to `react-router-dom` in v5) and the call is commented out, so there is no navigation after submit.

**Server:** `signUp.php` JSON-decodes `php://input`. It always `var_dump`s the decoded value. It continues only when `user_name` and `user_email` are set. `user_password` is not in that `isset` check. All three values are `trim`med.

**Duplicate rule:** `SELECT * FROM user WHERE email = '$email' OR username ='$name'`. If `mysqli_num_rows` is greater than 0, the script echoes `1` and does not insert.

**Insert path:** the script echoes `0`, then calls `Insertdata("user", ['email','username','password'], [$email, $name, $password], $password)`. The function accepts three parameters, so the fourth value is an extra argument. On a truthy query result it sets `date_created` to `NOW()` for that username and echoes `{"success":1,"msg":"User Inserted."}`. `Insertdata` also `print_r`s the SQL text into the response.

**Output the browser keeps:** none. The page does not change.

**Errors:** connection failure text; mysqli error string from `Insertdata`; a body that is a var_dump plus `1` when the username or email already exists. A failed insert after the `echo 0` produces no JSON success object. Provenance: **Implemented**.

### WF-3 — Sign in

**Trigger:** submit on `/signIn`.

**Client inputs:** `name` and `password`. If either is `""`, `setError("Fields are required")` and return. `error` is never rendered.

**Request:** `axios.post("http://localhost/php-react/signIn.php", JSON.stringify({ name, password }))`. The body keys are `name` and `password`.

**Server expects:** `user_name` and `user_password` on the decoded JSON. If those properties are absent the script sends headers and then ends with no body.

**Discrepancy:** the Sign In component does not send `user_name` or `user_password`. A submit from this form does not enter the login query. Provenance: **Implemented** on both sides. `AddUserModal.js` and `SignUp.js` do use the names the PHP reads; Sign In does not.

**When the expected keys are present:** select from `user` where `username` and `password` both match the raw strings.

- The script fetches one row and enters the success branch when `$row > 0`. A matching `mysqli_fetch_array` returns an array; no match returns `false` on older PHP and `null` from PHP 8.1. Whether `array > 0` is true or a `TypeError` depends on the PHP version. **Requires Confirmation.** On that success branch the script calls `updateDate('last_loggedin', $username)`, inserts into `tblloginuser` the strings `Login successful` and the username, and echoes `1`.
- Otherwise it looks up the username alone. The log string is `Login failed : Incorrect password` if a row exists, else `Login failed : User not found`. It still inserts that pair into `tblloginuser` and echoes `0`.
- Session lines are commented out, so success does not create a PHP session.

`Insertdata` prints the INSERT statement into the same response, ahead of the `1` or `0`.

**Output the browser keeps:** none. The `.then` discards `res.data`.

### WF-4 — List users

**Trigger:** mount of `srcHooks/Components/ListView.js`. Nothing in `App.js` mounts it. Provenance: **Implemented file, not reachable from the router**.

**Request:** `fetch("http://localhost/php-react/listView.php")` with the default GET, then `res.json()` into state `user`.

**Effect:** `useEffect(() => { fetchData(); })` has no dependency array, so the effect runs after every render that the fetch itself causes. Provenance: **Implemented**.

**Server:** `SELECT * FROM user`, then a JSON array of objects with `id`, `username`, `email`, and `date_created` only. The password column is not copied into that array. On query failure the script sends no JSON body (the `if` simply skips the echo).

**UI:** `BootstrapTable` from `react-bootstrap-table-next`, key field `id`, columns User ID (sortable), User Name, User Email, Date created, striped, hover, condensed. No empty-state branch. The previous commit `d8dc00b` had a `Loading...` branch, row checkboxes, and edit/delete buttons. `d10301a` removed them.

**Import faults in this file:** it imports `./App.css` from `Components/`, but `App.css` lives in `srcHooks/`. It imports Bootstrap CSS from `../node_modules/`, which resolves under `srcHooks/node_modules`. `node_modules` is not in git.

### WF-5 — Add a user from the modal

**Trigger:** none from the router. `AddUserModal` is only exported.

**Client:** modal title "Add User". Name, email, and password inputs use the HTML `required` attribute. Submit builds `{ user_name, user_email, user_password }` and posts it to the same `signUp.php` URL as WF-2. The result is ignored. The modal does not close itself. `console.log` writes the object.

**Server:** same as WF-2.

### WF-6 — Edit a user

**Trigger:** none. `EditUserModal` is not mounted. The edit button that existed on the table in `d8dc00b` was a `<button href=...>` that did not call `update.php`, and `d10301a` removed that column.

**Client:** modal title "Edit user". Name and email are `required`. A `password` state exists and is never bound to an input. Submit `preventDefault`s, builds the same three-field object as sign-up, and `console.log`s it. No `axios` import and no request.

**Server:** `php/update.php` is an empty file. `updatetUser()` in `functions.php` would set `username` and `email` where `username` equals `$oldusername`, echo `1` on success, and return a boolean. Nothing calls it. The function comment says "this must be changed !!!". `UpdateTable()` builds an UPDATE string and does not execute it. Its comment says "not implemented yet".

Provenance: **Implemented** as unfinished code. A completed edit workflow is **not implemented**.

### WF-7 — Delete a user

**Trigger:** no current JavaScript calls `delete.php`. At `d8dc00b` the table rendered a "delete" button with no click handler.

**Server:** `delete.php` reads `$_GET['id']`, echoes the text of `DELETE FROM user WHERE id ='$id' LIMIT 1`, runs that statement, then echoes JSON.

- Query truthy: `{"success":1,"msg":"User deleted."}`
- Otherwise: `{"success":0,"msg":"Something wrong appened, please try again."}` (spelling is the source string)

Because the SQL text is echoed first, the body is not valid JSON. `header.php` allows method POST while this script reads a query parameter, which a GET would carry. There is no check that the caller is signed in.

Provenance: **Implemented** endpoint, **not wired** to the UI at `d10301a`.

## Functional requirements

| ID | Requirement | Trigger | Inputs | Expected behaviour in this tree | Output | Status |
|---|---|---|---|---|---|---|
| FR-001 | The shell offers Sign In and Sign Up | Load `App` | none | Navbar links to `/signIn` and `/signUp`. Brand links to `/`, which has no page body | Navbar | **Implemented** |
| FR-002 | Sign-up form rejects a blank email or password before the request | Submit `/signUp` | email, password | Sets `error` and returns when either is `""`. Does not check name. Does not show `error` | No request | **Implemented**, message not visible |
| FR-003 | Sign-up posts name, email, and password to PHP | Submit with email and password set | `user_name`, `user_email`, `user_password` | POST to `http://localhost/php-react/signUp.php` | Response ignored | **Implemented** |
| FR-004 | The server refuses a second user with the same email or the same username | `signUp.php` with both name and email set | trimmed name and email | `SELECT` with `OR`. If any row, echo `1` and skip insert | `1` after a `var_dump` | **Implemented** |
| FR-005 | A new user row stores email, username, and password, then stamps `date_created` | No matching row | those three fields | `INSERT` then `UPDATE ... SET date_created = NOW()` | `0` plus SQL text plus `{"success":1,"msg":"User Inserted."}` | **Implemented**, response is mixed |
| FR-006 | Sign-in form rejects a blank name or password before the request | Submit `/signIn` | name, password | Sets `error` and returns. Does not show `error` | No request | **Implemented**, message not visible |
| FR-007 | Sign-in authenticates username and password | POST JSON with `user_name` and `user_password` | both properties set | Match both columns as plain strings. Echo `1` or `0` | Numeric text plus the login INSERT printed by `Insertdata` | **Implemented** on the PHP side |
| FR-008 | The Sign In screen sends the field names the PHP script reads | Submit `/signIn` | name, password | Body is `{name, password}` after `JSON.stringify` | PHP does not enter the query | **Discrepancy.** Client and server do not agree |
| FR-009 | A successful login stamps `last_loggedin` and writes a login-log row | Password match inside `signIn.php` | username | `updateDate('last_loggedin', username)` and insert `Login successful` | `1` | **Implemented** when FR-007's keys are present |
| FR-010 | A failed login writes a different log string for unknown user and wrong password | Username lookup after a failed match | username | `Login failed : User not found` or `Login failed : Incorrect password` | `0` | **Implemented** |
| FR-011 | Login does not start a session | Success path | — | Session lines are comments | Echo `1` only | **Implemented** |
| FR-012 | A caller can list users as JSON | GET `listView.php` | none | Array of `id`, `username`, `email`, `date_created` | JSON | **Implemented** |
| FR-013 | The list is visible inside the routed app | Open a route | — | `ListView` is not a route and is not imported | — | **Not implemented** |
| FR-014 | The list shows a Bootstrap table of those four fields | `ListView` render | JSON array | Columns as in WF-4. User ID sortable | Table | **Implemented** in the component file |
| FR-015 | An operator can add a user from a modal | Open Add User | name, email, password, all `required` | Would POST to `signUp.php` | Response ignored, modal stays open | **Implemented** component, **not routed** |
| FR-016 | An operator can edit a user | Open Edit user and submit | name, email | Logs an object. No HTTP call. `update.php` is empty | Console only | **Not implemented** |
| FR-017 | An operator can delete one user by id | Request `delete.php?id=` | `id` query parameter | One-row `DELETE`, then a success or failure JSON object after the SQL text | Mixed body | **Implemented** endpoint, **no UI caller** |
| FR-018 | List and delete require a signed-in caller | Either script | — | No session or token check | Public response | **Not implemented** |
| FR-019 | The service worker stays off | App boot | — | `unregister()` | No registration from `index.js` | **Implemented** |
| FR-020 | The default CRA test passes | Run the Jest test in `App.test.js` | rendered `App` | Looks for "learn react" | Assertion fails against this `App` | **Implemented test, expected to fail** |

## Integrations

| Integration | Direction | Auth | Success signal | Failure behaviour |
|---|---|---|---|---|
| MySQL `login` via mysqli | PHP → database | user `root`, empty password, localhost | query result used as boolean | `die` on connect; `mysqli_error` echoed from helpers |
| Browser → `http://localhost/php-react/` | React → PHP | none | ignored by every `.then` except the list `setUser` | Sign-in and sign-up show nothing. List has no `.catch` |

No other integration is present.

## Requirement provenance summary

Confirmed only as **Implemented Behaviour** unless a row above says otherwise. Nothing in this PRD is a **Documented Requirement** from the source project. Commented session, email, and `UpdateTable` notes are **Planned Behaviour** only in the sense that the author left a comment. They are not a backlog anyone confirmed.
