File: /home/xedaptot/ai.naniguide.com/app/Services/Form/FormGdprChecklist.php
<?php
namespace App\Services\Form;
/**
* F4.4 — Static catalog of GDPR rules audited by `form.check_gdpr`.
*
* Runtime SoT for the auditor; mirrors `form.json` →
* `entities.gdpr_checklist[]`. F38 Pest reflection asserts dual-source
* parity (D80).
*/
class FormGdprChecklist
{
/**
* @return list<array{id:string,label:string,rationale:string,fix_hint:string}>
*/
public static function rules(): array
{
return [
[
'id' => 'consent_checkbox',
'label' => 'Explicit consent checkbox present',
'rationale' => 'GDPR Article 7 requires affirmative action; a checkbox is the clearest evidence. Must be UNTICKED by default — pre-ticked is invalid.',
'fix_hint' => 'Add a checkbox before the submit button: \'I agree to receive {topic} updates from {Brand}. I can unsubscribe anytime.\' Untick by default.',
],
[
'id' => 'privacy_policy_link',
'label' => 'Privacy policy link visible',
'rationale' => 'GDPR Article 13 requires the data subject is told where to find the privacy notice. Link must be visible at submit time, not buried in a footer two pages away.',
'fix_hint' => 'Add a small-text link below the submit button: \'See our privacy policy.\' Point to your real privacy page.',
],
[
'id' => 'double_opt_in_enabled',
'label' => 'Double opt-in enabled on parent list',
'rationale' => 'Two-step confirmation produces a verifiable consent record (timestamp + IP + click). Single opt-in is legal but weaker — under DPA scrutiny, double opt-in materially raises your defence.',
'fix_hint' => 'On the parent mail list\'s settings, enable \'Subscribe confirmation\' (`mail_lists.subscribe_confirmation=1`). The form requires no change.',
],
[
'id' => 'consent_language_explicit',
'label' => 'Consent text says what you\'ll send',
'rationale' => 'GDPR consent must be specific. \'I agree to receive emails\' is too vague — name the kind of email (newsletter / product updates / promotional offers).',
'fix_hint' => 'Update the consent checkbox label to name the email type: \'I agree to receive {newsletter|product updates|offers} from {Brand}.\'',
],
[
'id' => 'data_minimization',
'label' => 'Required-field count <=3',
'rationale' => 'GDPR Article 5(1)(c) — data minimization. Collecting required fields you don\'t actually use is a violation. 3 required fields is a soft ceiling for marketing forms; B2B lead-gen can justify up to 5 if every field has a documented use.',
'fix_hint' => 'Mark fields you don\'t strictly need as `required=0`. If you can\'t articulate why a field is required, downgrade it to optional or remove it.',
],
[
'id' => 'unsubscribe_info',
'label' => 'Success message mentions unsubscribe',
'rationale' => 'Reinforces consent reversibility. Optional under GDPR but materially raises trust + reduces complaint rate. Most ESPs require unsubscribe in every email anyway — say it on the form too.',
'fix_hint' => 'Add to your success message: \'You can unsubscribe anytime via the link in any email.\' One sentence is enough.',
],
];
}
/**
* @return list<string>
*/
public static function ruleIds(): array
{
return array_map(fn (array $r) => $r['id'], self::rules());
}
/**
* @return array{id:string,label:string,rationale:string,fix_hint:string}|null
*/
public static function rule(string $id): ?array
{
foreach (self::rules() as $rule) {
if ($rule['id'] === $id) {
return $rule;
}
}
return null;
}
}