diff --git a/MEMORY.md b/MEMORY.md index 52a076b3..a8e43d4d 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -25,6 +25,7 @@ App gestionale Laravel 13 con AdminLTE 4 per gestione Persone e Gruppi. - **2026-06-10**: Per-User Column Views, ColumnManager JS, editVista data-* fix, 405 AJAX fix - **2026-06-17**: Google OAuth 2.0 unificato (revert Socialite, unified OAuth per Email/Drive/Calendar, XOAUTH2 SMTP+IMAP, UI impostazioni) - **2026-06-17**: Fix CalendarioConnessione — `encryptAndSetConfig()` perdeva campi sensibili in edit, `is_active` checkbox senza hidden fallback, query duplicata nella view +- **2026-06-18**: Fix import CSV — `declare(strict_types=1)` mancante in IndividuoController, header check assente, Log::warning() su errori; fix freeze server (set_time_limit, session_write_close, DB::transaction, fgetcsv length illimitato) in GruppoController e IndividuoController ## Funzionalità Implementate @@ -665,3 +666,42 @@ Aggiunto `if (!Schema::hasTable('table_name')) { ... }` wrapper a 28 migration f - PHP lint: OK su controller + routes - JS brace balance: OK su index view - Route list: `POST mailing-liste/mass-tag` → `MailingListController@massTag` (name: `mailing-liste.mass-tag`) + +## 2026-06-18 — Fix import CSV: freeze server + bug IndividuoController + +### Problema freeze server (Gruppi + Individui) +Importando CSV con molte righe, il server si bloccava: +1. **PHP max_execution_time (30s)** — killava il processo su import grandi +2. **Session lock** — altre richieste dello stesso utente restavano in attesa +3. **Nessuna transazione DB** — ogni `create()` era una INSERT individuale autocommit +4. **fgetcsv length=1000** — righe CSV più lunghe di 1000 caratteri venivano troncate + +### Fix applicati a `GruppoController@importStore` e `IndividuoController@importStore` +```php +set_time_limit(0); +session_write_close(); +$header = fgetcsv($handle, 0, ','); // length=0 = nessun limite +DB::beginTransaction(); +// ... loop ... +DB::commit(); +``` + +### Bug trovati in `IndividuoController@importStore` +| Bug | Fix | +|-----|-----| +| `declare(strict_types=1)` mancante | ✅ Aggiunto | +| Indentazione errata negli import (spazi extra su 8 righe) | ✅ Corretto | +| `use Illuminate\Support\Facades\Log` mancante | ✅ Aggiunto | +| Nessun controllo `$header === false` — CSV vuoto causava TypeError | ✅ Aggiunto | +| Nessun `array_map('trim', $header)` | ✅ Aggiunto | +| Nessun `Log::warning()` su eccezioni riga | ✅ Aggiunto | +| `fgetcsv` length=1000 (vs 0) | ✅ Portato a 0 | +| Nessun `set_time_limit(0)` / `session_write_close()` | ✅ Aggiunto | +| Nessun `DB::beginTransaction()` / `DB::commit()` | ✅ Aggiunto | + +### File modificati +- `app/Http/Controllers/GruppoController.php` — aggiunti `use DB`, set_time_limit, session_write_close, fgetcsv length=0, DB::transaction +- `app/Http/Controllers/IndividuoController.php` — strict_types, Log import, DB import, header check, trim, set_time_limit, session_write_close, DB::transaction + +### Verifica +- `php -l` su entrambi: nessun errore di sintassi diff --git a/app/Http/Controllers/GruppoController.php b/app/Http/Controllers/GruppoController.php index 30c3f4f7..e695360e 100644 --- a/app/Http/Controllers/GruppoController.php +++ b/app/Http/Controllers/GruppoController.php @@ -11,6 +11,7 @@ use App\Models\Tag; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\Paginator; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class GruppoController extends Controller @@ -385,9 +386,12 @@ class GruppoController extends Controller 'file' => 'required|file|mimes:csv,txt', ]); + set_time_limit(0); + session_write_close(); + $file = $request->file('file'); $handle = fopen($file->path(), 'r'); - $header = fgetcsv($handle, 1000, ','); + $header = fgetcsv($handle, 0, ','); if ($header === false) { fclose($handle); @@ -400,7 +404,9 @@ class GruppoController extends Controller $errors = []; $rowNumber = 1; - while (($row = fgetcsv($handle, 1000, ',')) !== false) { + DB::beginTransaction(); + + while (($row = fgetcsv($handle, 0, ',')) !== false) { $rowNumber++; if (count($row) < count($header)) { @@ -444,6 +450,8 @@ class GruppoController extends Controller fclose($handle); + DB::commit(); + $message = 'Importati ' . $imported . ' gruppi.'; if ($skipped > 0) { $message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).'; diff --git a/app/Http/Controllers/IndividuoController.php b/app/Http/Controllers/IndividuoController.php index 3008fadc..e9f4d707 100644 --- a/app/Http/Controllers/IndividuoController.php +++ b/app/Http/Controllers/IndividuoController.php @@ -1,19 +1,23 @@ 'required|file|mimes:csv,txt', ]); + set_time_limit(0); + session_write_close(); + $file = $request->file('file'); $handle = fopen($file->path(), 'r'); - $header = fgetcsv($handle, 1000, ','); + $header = fgetcsv($handle, 0, ','); + if ($header === false) { + fclose($handle); + return back()->with('error', 'Il file CSV non è valido.'); + } + + $header = array_map('trim', $header); $imported = 0; $skipped = 0; $errors = []; $rowNumber = 1; - while (($row = fgetcsv($handle, 1000, ',')) !== false) { + DB::beginTransaction(); + + while (($row = fgetcsv($handle, 0, ',')) !== false) { $rowNumber++; if (count($row) < count($header)) { @@ -565,12 +580,15 @@ public function emailList(Request $request) $imported++; } catch (\Exception $e) { + Log::warning('Errore import individuo riga ' . $rowNumber . ': ' . $e->getMessage()); $errors[] = 'Errore riga ' . $rowNumber . ': ' . $e->getMessage(); } } fclose($handle); + DB::commit(); + $message = 'Importati ' . $imported . ' individui.'; if ($skipped > 0) { $message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).'; diff --git a/nscadmin@192.168.222.177 b/nscadmin@192.168.222.177 new file mode 100644 index 00000000..ad033584 Binary files /dev/null and b/nscadmin@192.168.222.177 differ diff --git a/vendor/autoload.php b/vendor/autoload.php index b6ebe27a..816daef4 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 51e734a7..87766a22 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -26,6 +26,12 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null @@ -311,6 +317,18 @@ class InstalledVersions self::$installedByVendor = array(); } + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + /** * @return array[] * @psalm-return list}> @@ -322,6 +340,7 @@ class InstalledVersions } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { @@ -330,9 +349,11 @@ class InstalledVersions } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $required; + $copiedLocalDir = true; } } } @@ -350,7 +371,7 @@ class InstalledVersions } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 3acefcce..2db779bc 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'laravel/laravel', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a', + 'reference' => 'd2084b9d07cbeccb7e289e6f31ca767f6cce07c8', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -427,7 +427,7 @@ 'laravel/laravel' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a', + 'reference' => 'd2084b9d07cbeccb7e289e6f31ca767f6cce07c8', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -732,8 +732,8 @@ 'dev_requirement' => false, 'provided' => array( 0 => '1.0 || 2.0 || 3.0', - 1 => '3.0.0', - 2 => '1.0|2.0|3.0', + 1 => '1.0|2.0|3.0', + 2 => '3.0.0', ), ), 'psr/simple-cache' => array(