diff --git a/.abacusai/config.json b/.abacusai/config.json new file mode 100644 index 0000000..ade5063 --- /dev/null +++ b/.abacusai/config.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(git *)", + "Bash(git --no-pager log --oneline -8 2>&1 || true)", + "Bash(git --no-pager diff -- app/Controllers/ApiProxyController.php routes/web.php app/Controllers/JobTypeController.php app/V…)" + ] + } +} \ No newline at end of file diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 494e663..9a03ddb 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -13,7 +13,8 @@ "Bash(dir /b /s)", "Bash(findstr \"^app\")", "PowerShell(cd \"d:\\\\Development\\\\PHP\\\\Campaign-Tracker\"; Get-ChildItem -Recurse -Directory | Select-Object -ExpandProperty FullName | Where-Object { $_ -notmatch '\\\\.git|\\\\.claude|node_modules' } | Sort-Object | Select-Object -First 30)", - "Bash(git -C \"d:\\\\Development\\\\PHP\\\\Campaign-Tracker\" remote get-url origin)" + "Bash(git -C \"d:\\\\Development\\\\PHP\\\\Campaign-Tracker\" remote get-url origin)", + "PowerShell(Get-ChildItem -Path \"d:\\\\Development\\\\PHP\\\\Campaign-Tracker\" -Recurse -Directory -ErrorAction SilentlyContinue | Select-Object -First 30 | ForEach-Object { $_.FullName })" ] } } diff --git a/.env_prod b/.env_prod new file mode 100644 index 0000000..2e0e03c --- /dev/null +++ b/.env_prod @@ -0,0 +1,19 @@ +APP_ENV=local +APP_DEBUG=true + +DB_HOST=sqlserver +DB_PORT=1433 +DB_DATABASE=Campaign_Tracker +DB_USERNAME=sa +DB_PASSWORD=Dev_Password123! + +# ── Keycloak ─────────────────────────────────────────────────────────────────── +# KEYCLOAK_BASE_URL: Base URL of your Keycloak server. +# Keycloak 17+ (no /auth prefix): http://localhost:8080 +# Keycloak < 17 (has /auth prefix): http://localhost:8080/auth +KEYCLOAK_BASE_URL=http://kci-app01.ntp.kentcommunications.com:8180/ +KEYCLOAK_REALM=KCI +KEYCLOAK_CLIENT_ID=canopy-web +KEYCLOAK_CLIENT_SECRET=LHWXp5UUuES00Dz2iCjTJJgX9su6co0y +KEYCLOAK_REDIRECT_URI=http://192.168.1.200:8801/auth/callback +KEYCLOAK_LOGOUT_REDIRECT_URI=http://192.168.1.200:8801/login diff --git a/AGENTS.md b/AGENTS.md index 3f6550b..8cd361b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1311,4 +1311,21 @@ class MyDashboardViewModel } ``` -Populate it in the controller using private `repo()` methods or inline `new Repository(database())` calls — consistent with how other controllers are written in this project. \ No newline at end of file +Populate it in the controller using private `repo()` methods or inline `new Repository(database())` calls — consistent with how other controllers are written in this project. + +--- + +## Clean JavaScript Practices (Distilled) + +Source: https://medium.com/@onix_react/best-practices-for-writing-clean-javascript-code-a4e5755de69a + +- Prefer `const` and `let` over `var` to avoid function-scoped surprises. +- Keep scope tight and avoid globals to prevent hidden coupling and collisions. +- Use small, focused functions and clear names to make intent obvious. +- Prefer arrow functions when lexical `this` and concise syntax improve clarity. +- Use `async/await` for async flows and handle failures with `try/catch`. +- Fail safely: validate inputs, handle exceptions, and log actionable error details. +- Use array helpers (`map`, `filter`, `reduce`, `forEach`) where they improve readability. +- Minimize direct DOM writes; batch updates, cache selectors, and use delegation when possible. +- Keep formatting and naming conventions consistent across the project. +- Document non-obvious decisions briefly; avoid redundant comments that restate code. \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8cd361b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,1331 @@ +# AGENT.md — PHP Coding Standard + +This file defines the coding standards and working rules for AI agents and developers contributing to this PHP codebase. It is based on the principles from **PHP: The Right Way** and adapted into practical project instructions. + +Source reference: https://phptherightway.com/ + +--- + +## 1. Core Philosophy + +Write PHP that is: + +- **Readable** before clever. +- **Secure by default**. +- **Consistent with community standards**. +- **Easy to test, debug, and refactor**. +- **Separated by responsibility**: routing, controllers, services, models, persistence, templates, and configuration should not be mixed together. + +PHP does not have only one canonical “right way,” so prefer widely accepted standards, documented project conventions, and clear tradeoffs over personal style. + +--- + +## 2. PHP Version Standard + +Use the current stable PHP version supported by the project. + +Default expectation: + +```text +PHP 8.x+ +``` + +Do not introduce code that depends on unsupported PHP versions unless the project explicitly targets a legacy runtime. + +When adding a language feature, verify that it is supported by the project’s configured PHP version in `composer.json`. + +Example: + +```json +{ + "require": { + "php": ">=8.2" + } +} +``` + +--- + +## 3. Coding Style + +Follow recognized PHP standards unless the repository already defines stricter rules. + +Preferred standards: + +- **PSR-1**: Basic Coding Standard +- **PSR-12**: Extended Coding Style +- **PSR-4**: Autoloading + +Use automated tooling rather than manual formatting arguments. + +Recommended tools: + +```bash +composer require --dev squizlabs/php_codesniffer +composer require --dev friendsofphp/php-cs-fixer +``` + +Example checks: + +```bash +vendor/bin/phpcs --standard=PSR12 src tests +vendor/bin/php-cs-fixer fix --dry-run --diff +``` + +Example fix: + +```bash +vendor/bin/php-cs-fixer fix +``` + +### Naming Rules + +Use English names for code symbols and infrastructure. + +Use: + +```php +class InvoiceRepository +{ + public function findByCustomerId(int $customerId): array + { + // ... + } +} +``` + +Avoid unclear abbreviations: + +```php +class InvRepo +{ + public function fbcid($cid) + { + // ... + } +} +``` + +### Formatting Rules + +- Use `value; + } +} +``` + +Guidelines: + +- Keep controllers thin. +- Put business rules in services or domain objects. +- Put persistence logic in repositories or data access classes. +- Use interfaces when multiple implementations are expected or when it improves testing. +- Avoid huge “utility” classes. +- Avoid magic methods unless they provide clear framework integration or a documented benefit. + +--- + +## 8. Dependency Injection + +Prefer dependency injection over creating dependencies inside classes. + +Good: + +```php +final class RegisterUser +{ + public function __construct( + private UserRepository $users, + private PasswordHasher $passwords + ) { + } + + public function handle(string $email, string $plainPassword): void + { + $hash = $this->passwords->hash($plainPassword); + $this->users->create($email, $hash); + } +} +``` + +Avoid: + +```php +final class RegisterUser +{ + public function handle(string $email, string $plainPassword): void + { + $users = new UserRepository(); + $passwords = new PasswordHasher(); + // ... + } +} +``` + +Rules: + +- Constructor injection is preferred for required dependencies. +- Do not use service locators casually. +- Do not hide dependencies in global variables. +- Keep dependency containers at application boundaries, not inside domain logic. + +--- + +## 9. Database Access + +Use PDO or a well-maintained database abstraction layer/ORM. + +Never concatenate untrusted input into SQL. + +Bad: + +```php +$sql = "SELECT * FROM users WHERE id = " . $_GET['id']; +``` + +Good: + +```php +$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id'); +$stmt->bindValue(':id', $id, PDO::PARAM_INT); +$stmt->execute(); +$user = $stmt->fetch(PDO::FETCH_ASSOC); +``` + +Rules: + +- Use prepared statements and bound parameters. +- Validate input before using it in writes. +- Keep SQL out of templates. +- Keep database access out of controllers where practical. +- Use transactions when multiple writes must succeed or fail together. +- Do not rely only on client-side validation. +- Do not expose raw database errors to users. + +Transaction example: + +```php +$pdo->beginTransaction(); + +try { + $orders->create($order); + $auditLog->record('order.created', $order->id()); + $pdo->commit(); +} catch (Throwable $e) { + $pdo->rollBack(); + throw $e; +} +``` + +--- + +## 10. Input Validation and Output Escaping + +Treat all external data as untrusted. + +Untrusted data includes: + +- `$_GET` +- `$_POST` +- `$_REQUEST` +- `$_COOKIE` +- `$_SERVER` +- uploaded files +- request bodies +- session values +- database values originally supplied by users +- third-party API responses + +### Validate on Input + +Example: + +```php +$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); + +if ($email === false || $email === null) { + throw new InvalidArgumentException('A valid email address is required.'); +} +``` + +### Escape on Output + +For HTML output: + +```php +function e(string $value): string +{ + return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} +``` + +Usage: + +```php +
= e($user->name()) ?>
+``` + +Rules: + +- Escape based on context: HTML, attribute, JavaScript, CSS, URL, SQL, shell. +- Do not use the same escaping function for every context. +- Prefer template engines with automatic escaping when appropriate. +- Avoid allowing raw HTML from users. If required, sanitize with a proven whitelist sanitizer. +- Use `escapeshellarg()` when passing controlled values to shell commands, and avoid shell execution when possible. +- Never trust file paths supplied by users. Reject path traversal values such as `../`, `/`, `\`, and null bytes. + +--- + +## 11. Passwords and Authentication + +Never store plain-text passwords. + +Use PHP’s password API: + +```php +$hash = password_hash($plainPassword, PASSWORD_DEFAULT); + +if (! password_verify($plainPassword, $hash)) { + throw new RuntimeException('Invalid credentials.'); +} +``` + +Rules: + +- Use `password_hash()` for new password hashes. +- Use `password_verify()` for login checks. +- Use `password_needs_rehash()` when algorithm/cost settings change. +- Do not create your own password hashing algorithm. +- Do not use general-purpose hashes like `md5`, `sha1`, or raw `sha256` for passwords. +- Rate-limit login attempts. +- Regenerate session IDs after login. +- Use secure, HTTP-only, SameSite cookies for sessions. + +--- + +## 12. Serialization and Data Exchange + +Do not call `unserialize()` on untrusted data. + +Prefer JSON for data exchange: + +```php +$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR); +$json = json_encode($data, JSON_THROW_ON_ERROR); +``` + +Rules: + +- Use `JSON_THROW_ON_ERROR` for new code. +- Validate decoded data before using it. +- Avoid PHP serialization for data that crosses trust boundaries. + +--- + +## 13. Configuration and Secrets + +Rules: + +- Keep secrets out of source control. +- Do not commit passwords, API keys, private keys, tokens, or production DSNs. +- Store configuration outside the public web root. +- Use environment variables or ignored local config files for secrets. +- Provide a safe example file such as `.env.example`. + +Example `.gitignore` entries: + +```text +.env +.env.local +/config/local.php +/var/cache/ +/var/log/ +/vendor/ +``` + +Example `.env.example`: + +```text +APP_ENV=local +APP_DEBUG=true +DATABASE_URL=mysql://user:password@localhost:3306/app +``` + +--- + +## 14. Error Handling and Logging + +Use exceptions for exceptional failure paths. + +Development: + +- Show errors locally. +- Log errors. +- Use Xdebug when debugging complex issues. + +Production: + +- Do not display errors to users. +- Log errors to a secure log destination. +- Return safe, generic error messages. +- Preserve enough context in logs for troubleshooting. + +Do not leak: + +- stack traces to users +- SQL statements with secrets +- environment variables +- full filesystem paths +- tokens or passwords + +Example: + +```php +try { + $service->handle($request); +} catch (Throwable $e) { + $logger->error('Order processing failed.', [ + 'exception' => $e, + 'requestId' => $requestId, + ]); + + http_response_code(500); + echo 'An unexpected error occurred.'; +} +``` + +--- + +## 15. Templates and Views + +Keep presentation separate from business logic. + +Rules: + +- Do not query the database from templates. +- Do not place business rules in templates. +- Escape output by default. +- Prefer simple view models or arrays passed into templates. +- Use a template engine with automatic escaping when it fits the project. + +Plain PHP template example: + +```php +` description on the left. +- A primary action button (or back link) on the right. + +```html +
+``` + +**Page h1 size** is 32px / 40px line-height (set in `.section-heading h1`). Do not override this inline. + +--- + +### Section Panels + +Every content block on a page must be wrapped in ``, and a right side for action buttons. +- Panel `
Brief description of this section's content.
+Loading…
+