diff --git a/.agents/skills/php-common-library/SKILL.md b/.agents/skills/php-common-library/SKILL.md
index e386153..4a4708e 100644
--- a/.agents/skills/php-common-library/SKILL.md
+++ b/.agents/skills/php-common-library/SKILL.md
@@ -32,7 +32,7 @@ namespace ElixForms\Common;
Le pagine importano con:
```php
-require_once __DIR__ . '/../common/ElixFormsComponent.php';
+require_once __DIR__ . '/../../vendor/autoload.php';
use ElixForms\Common\ElixFormsComponent;
```
diff --git a/.agents/skills/php-project-architecture/SKILL.md b/.agents/skills/php-project-architecture/SKILL.md
index 6e38cc6..a53a4dd 100644
--- a/.agents/skills/php-project-architecture/SKILL.md
+++ b/.agents/skills/php-project-architecture/SKILL.md
@@ -20,6 +20,8 @@ Questa struttura è l'equivalente PHP del monorepo React in `react/`.
```
elixforms-custom-pages/
+├── composer.json ← Configurazione Composer e autoload PSR-4
+├── vendor/ ← Autoloader generato (non versionato)
├── react/ ← Custom pages React (esistente)
└── php/ ← Custom pages PHP
├── AGENTS.md ← Regole dell'agente PHP
@@ -35,7 +37,7 @@ elixforms-custom-pages/
Ogni pagina (es. `scelta-carriera/`) è un file PHP autonomo che:
-1. **Include la libreria `common/`** tramite `require_once`
+1. **Carica l'autoloader Composer** tramite `vendor/autoload.php`
2. **Definisce una classe** che estende `ElixFormsComponent`
3. **Sovrascrive `createCustomFormFields()`** per i campi specifici
4. **Istanzia e renderizza** la pagina con `echo $page->render()`
@@ -50,13 +52,10 @@ Il progetto usa **namespace PSR-4**:
namespace ElixForms\Common; // Per le classi in common/
```
-L'autoloading è gestito manualmente con `require_once` (non Composer):
+L'autoloading PSR-4 è gestito da Composer:
```php
-require_once __DIR__ . '/../common/ElixFormsComponent.php';
-// ElixFormsComponent.php include internamente:
-// - QueryParamHelper.php
-// - ElixFormsElement.php
+require_once __DIR__ . '/../../vendor/autoload.php';
```
## Requisiti PHP
@@ -108,7 +107,7 @@ Identica alla versione React:
| **Linguaggio** | TypeScript/JSX | PHP 8.0+ |
| **Rendering** | Client-side | Server-side |
| **Build tool** | Vite (single bundle JS) | Nessuno (file serviti direttamente) |
-| **Dipendenze** | npm (React, Vite, plugin) | Nessuna (PHP built-in) |
+| **Dipendenze** | npm (React, Vite, plugin) | Composer (autoload PSR-4) |
| **CSS loading** | `@import url()` + CSS injection plugin | `` tags |
| **State management** | React state (`this.setState`) | Stateless (`$_GET`) |
| **Deploy** | Upload del bundle `.js` | Upload dei file `.php` |
@@ -116,9 +115,10 @@ Identica alla versione React:
## Deploy su Apache
-1. Caricare la cartella `php/` sul server Apache
-2. Configurazione Apache minima:
+1. Eseguire `composer install --no-dev --optimize-autoloader` dalla root del repository
+2. Caricare sul server le cartelle `php/` e `vendor/`, mantenendole allo stesso livello
+3. Configurazione Apache minima:
- `mod_php` o `php-fpm` abilitato
- `DirectoryIndex index.php` (default Apache)
-3. La pagina è accessibile via URL: `https://server/php/nome-pagina/`
-4. elixForms reindirizza l'utente alla pagina passando i parametri via query string
+4. La pagina è accessibile via URL: `https://server/php/nome-pagina/`
+5. elixForms reindirizza l'utente alla pagina passando i parametri via query string
diff --git a/.gitignore b/.gitignore
index 105cb40..13f4e6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ lerna-debug.log*
# Dependency directories
node_modules
+vendor
# Build generated files
dist
@@ -54,4 +55,4 @@ obj
*.sw?
# Configuration files
-**/config.json
\ No newline at end of file
+**/config.json
diff --git a/composer-dump-autoload.bat b/composer-dump-autoload.bat
new file mode 100644
index 0000000..6731439
--- /dev/null
+++ b/composer-dump-autoload.bat
@@ -0,0 +1,10 @@
+@echo off
+SETLOCAL EnableDelayedExpansion
+echo %cmdcmdline% | findstr /i /c:"%~nx0" >NUL && set iscommandline=1
+echo %PSModulePath% | findstr /i /c:"%USERPROFILE%" >NUL && set ispowershell=1
+cd /D "%~dp0"
+
+@pwsh -NoProfile -ExecutionPolicy Bypass -Command .\scripts\composer-dump-autoload.ps1
+
+IF DEFINED iscommandline IF NOT DEFINED ispowershell pause
+ENDLOCAL
diff --git a/composer-install.bat b/composer-install.bat
new file mode 100644
index 0000000..9f7bf18
--- /dev/null
+++ b/composer-install.bat
@@ -0,0 +1,10 @@
+@echo off
+SETLOCAL EnableDelayedExpansion
+echo %cmdcmdline% | findstr /i /c:"%~nx0" >NUL && set iscommandline=1
+echo %PSModulePath% | findstr /i /c:"%USERPROFILE%" >NUL && set ispowershell=1
+cd /D "%~dp0"
+
+@pwsh -NoProfile -ExecutionPolicy Bypass -Command .\scripts\composer-install.ps1
+
+IF DEFINED iscommandline IF NOT DEFINED ispowershell pause
+ENDLOCAL
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..adc9318
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,13 @@
+{
+ "name": "elixforms-web-pages/pages",
+ "description": "Custom web pages PHP per elixForms",
+ "license": "proprietary",
+ "require": {
+ "php": ">=8.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "ElixForms\\Common\\": "php/common/"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..3f379b1
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,20 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "23a05b8814892947b8a622a763a2ace5",
+ "packages": [],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=8.0"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
+}
diff --git a/php/AGENTS.md b/php/AGENTS.md
index b34533d..42ba9c2 100644
--- a/php/AGENTS.md
+++ b/php/AGENTS.md
@@ -14,7 +14,8 @@ In caso di conflitti di ID (ad esempio con input `hidden`), applicare il suffiss
2. **Namespace PSR-4**:
- Tutte le classi nella libreria comune usano il namespace `ElixForms\Common;`.
- - Il caricamento è manuale tramite `require_once __DIR__ . '/../common/ElixFormsComponent.php';`.
+ - L'autoload PSR-4 è gestito da Composer tramite `vendor/autoload.php`.
+ - Gli entry point includono `require_once __DIR__ . '/../../vendor/autoload.php';`; le classi comuni non devono includersi manualmente tra loro.
3. **Rendering Server-Side**:
- I file PHP generano direttamente l'HTML server-side usando il design system Bootstrap Italia di UniPR.
@@ -24,12 +25,13 @@ In caso di conflitti di ID (ad esempio con input `hidden`), applicare il suffiss
- I checkbox inviano il proprio stato serializzato come stringa separata da virgole tramite un input hidden (gestito client-side da uno script JS automatico).
## Deploy e Server Locale
-- **Deploy**: Le cartelle vengono caricate direttamente su web server Apache.
+- **Dipendenze**: dalla root del repository eseguire `composer install --optimize-autoloader` oppure `composer-install.bat`.
+- **Deploy**: Eseguire `composer install --no-dev --optimize-autoloader` e caricare sul server sia `php/` sia `vendor/`, mantenendole allo stesso livello.
- **Server locale**:
- **PHP CLI**: Avviare il server dalla cartella `php/` tramite `php -S localhost:8080` (le pagine saranno raggiungibili su `http://localhost:8080//`).
- **Docker**: Dalla root del repository, eseguire:
```bash
- docker run --rm -p 8080:8080 -v "${PWD}/php:/app" -w /app php:8.2-cli php -S 0.0.0.0:8080
+ docker run --rm -p 8080:8080 -v "${PWD}:/app" -w /app/php php:8.2-cli php -S 0.0.0.0:8080
```
## Skill PHP Correlate
diff --git a/php/common/ElixFormsComponent.php b/php/common/ElixFormsComponent.php
index cb29d61..f48ed23 100644
--- a/php/common/ElixFormsComponent.php
+++ b/php/common/ElixFormsComponent.php
@@ -4,9 +4,6 @@ declare(strict_types=1);
namespace ElixForms\Common;
-require_once __DIR__ . '/QueryParamHelper.php';
-require_once __DIR__ . '/ElixFormsElement.php';
-
/**
* Classe astratta base per le custom page PHP di elixForms.
* Equivalente PHP di ElixFormsComponentAbstract.tsx nel progetto React.
diff --git a/php/docker-compose.yml b/php/docker-compose.yml
index 6b3021c..b5ff245 100644
--- a/php/docker-compose.yml
+++ b/php/docker-compose.yml
@@ -6,13 +6,13 @@ services:
context: .
dockerfile: ../.docker/php/Dockerfile
container_name: elixforms-www
- working_dir: /app
+ working_dir: /app/php
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "8080:8000"
volumes:
- - .:/app
+ - ..:/app
- ../.docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
#environment:
# XDEBUG_MODE: debug
diff --git a/php/recupero-proposta-cct-da-contratti/index.php b/php/recupero-proposta-cct-da-contratti/index.php
index 955e070..be2e637 100644
--- a/php/recupero-proposta-cct-da-contratti/index.php
+++ b/php/recupero-proposta-cct-da-contratti/index.php
@@ -2,8 +2,7 @@
declare(strict_types=1);
-require_once __DIR__ . '/../common/ElixFormsComponent.php';
-require_once __DIR__ . '/../common/QueryParamHelper.php';
+require_once __DIR__ . '/../../vendor/autoload.php';
use ElixForms\Common\ElixFormsComponent;
use ElixForms\Common\QueryParamHelper;
diff --git a/php/recupero-proposta-cct-da-contratti/search-contratti.php b/php/recupero-proposta-cct-da-contratti/search-contratti.php
index c292ec3..145b11b 100644
--- a/php/recupero-proposta-cct-da-contratti/search-contratti.php
+++ b/php/recupero-proposta-cct-da-contratti/search-contratti.php
@@ -4,7 +4,7 @@ declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
-require_once __DIR__ . '/../common/HttpClient.php';
+require_once __DIR__ . '/../../vendor/autoload.php';
use ElixForms\Common\HttpClient;
diff --git a/php/scelta-carriera/index.php b/php/scelta-carriera/index.php
index 80bccca..0641d10 100644
--- a/php/scelta-carriera/index.php
+++ b/php/scelta-carriera/index.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-require_once __DIR__ . '/../common/ElixFormsComponent.php';
+require_once __DIR__ . '/../../vendor/autoload.php';
use ElixForms\Common\ElixFormsComponent;
diff --git a/scripts/composer-dump-autoload.ps1 b/scripts/composer-dump-autoload.ps1
new file mode 100644
index 0000000..0dd5311
--- /dev/null
+++ b/scripts/composer-dump-autoload.ps1
@@ -0,0 +1,2 @@
+$ErrorActionPreference = 'Stop'
+& (Join-Path $PSScriptRoot 'composer-run-command.ps1') -Command dump-autoload -Arguments @('--optimize')
diff --git a/scripts/composer-install.ps1 b/scripts/composer-install.ps1
new file mode 100644
index 0000000..e1bdd1e
--- /dev/null
+++ b/scripts/composer-install.ps1
@@ -0,0 +1,2 @@
+$ErrorActionPreference = 'Stop'
+& (Join-Path $PSScriptRoot 'composer-run-command.ps1') -Command install -Arguments @('--optimize-autoloader')
diff --git a/scripts/composer-run-command.ps1 b/scripts/composer-run-command.ps1
new file mode 100644
index 0000000..db0c5b2
--- /dev/null
+++ b/scripts/composer-run-command.ps1
@@ -0,0 +1,47 @@
+param(
+ [Parameter(Mandatory)][string]$Command,
+ [string[]]$Arguments = @()
+)
+
+$ErrorActionPreference = 'Stop'
+$root = (Resolve-Path (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) '..')).Path
+
+$useDockerComposer = $false
+$dockerRoot = $null
+
+if (-not (Get-Command composer -ErrorAction SilentlyContinue)) {
+ Write-Host "Composer non trovato localmente. Verifico se Docker è disponibile..."
+ if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
+ throw "Composer non trovato e Docker mancante! Installa Composer o Docker prima di continuare."
+ }
+
+ $dockerRoot = $root -replace '\\', '/'
+ if ($dockerRoot -match '^([A-Za-z]):') {
+ $dockerRoot = "/$($Matches[1].ToLower())$($dockerRoot.Substring(2))"
+ }
+
+ Write-Host "Docker trovato. Verifico Composer tramite container..."
+ docker run --rm -v "$($dockerRoot):/app" -w /app composer:2 composer --version 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ throw "Docker presente, ma Composer non riesce ad avviarsi tramite container. Installa Composer localmente o verifica Docker."
+ }
+
+ $useDockerComposer = $true
+}
+
+Push-Location $root
+try {
+ if ($useDockerComposer) {
+ docker run --rm -v "$($dockerRoot):/app" -w /app composer:2 composer $Command @Arguments
+ }
+ else {
+ composer $Command @Arguments
+ }
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "Composer ha restituito il codice di uscita $LASTEXITCODE."
+ }
+}
+finally {
+ Pop-Location
+}