declare(strict_types=1);

namespace app\widgets\randomizedForm;

use Yii;
use yii\widgets\ActiveForm;
use yii\base\InvalidArgumentException as YiiBaseInvalidArgumentException;
use yii\web\View;
use yii\helpers\Html;
use yii\helpers\Json;

use app\components\SessionManager;
use app\components\interfaces\SessionInterface;
use app\components\traits\CacheJsCssTrait;
use app\widgets\randomizedForm\Randomizer;
use app\helpers\ParamHelper;
use app\helpers\TimeHelper;

/**
* RandomizedActiveForm overrides Yii2 ActiveForm to randomize all field names,
* store the mapping in session, and pass to fields.
*/
class RandomizedActiveForm extends ActiveForm
{
    /**
    * Placeholders keyed by logical attribute names.
    * To be set by the view when starting the widget.
    * @var array<string,string>
    */
    public array $placeholders = [];
    
    // @var array<string, string> Map of logical attribute => randomized name
    public array $fieldNameMap = [];
    
    // @var list<string> List of honeypot logical attribute names
    public array $honeypotFields = [];
    
    //  @var object|null The form model
    public ?object $model = null;
    
    /**
    * Session Manager Interface
    * @see app\components\interfaces\SessionInterface.php
    * @var SessionInterface
    */
    public ?SessionInterface $sessionManager = null;
    
    //  @var Randomizer
    protected Randomizer $__randomizer;
    
    // @var string The session key to use
    protected string $__sessionKey;
    
    // Note: Every protected variable below CacheJsCssTrait is used by the trait
    use CacheJsCssTrait;
    
    protected bool $__cacheDebugMode = false;
    protected bool $__compressJsCss = false;
    protected bool $__cacheEnable = false;
    protected int $__cacheJsCssTime = 3600;// +1 hour
    protected string $__cacheKeyPrefix = 'widget_randomizedActiveForm';
    
    const DEFAULT_DURATION_SECONDS = 3600;// +1 hour
    
    public function init(): void
    {
        // Get cacheDuration from Param and set with TimeHelper
        $cacheDuration = ParamHelper::get('widgets.cacheDuration');
        $this->__cacheJsCssTime = TimeHelper::convertDurationToSeconds($cacheDuration);
        
        // Cache & Compression
        $this->__cacheEnable = ParamHelper::get('widgets.cacheEnable');
        $this->__compressJsCss = ParamHelper::get('widgets.compressJsCss');
    
        /**
        * Always call the parent init method first to ensure any
        * initialization logic defined in the base class is executed first.
        */
        parent::init();
        
        if ($this->model === null)
        {
            throw new YiiBaseInvalidArgumentException(
                'RandomizedActiveForm requires you to set the "model" property.'
            );
        }
        
        // Initialize sessionManager internally if not set
        if (!isset($this->sessionManager))
        {
            $this->sessionManager = new SessionManager();
        }
        
        // Use widget/form ID or fallback to class name + random string hash
        if (empty($this->__sessionKey))
        {
            // Use form ID as part of session key—ensure safe chars by base64 or hash if needed
            if (!empty($this->id))
            {
                $sessionSafeId = preg_replace('/[^a-z0-9_\-]/i', '_', strtolower($this->id));
                $this->__sessionKey = 'randomMap_' . $sessionSafeId;
            }
            // Fallback: hash of class name + random string
            else
            {
                $randomPart = substr(\Yii::$app->security->generateRandomString(8), 0, 8);
                $this->__sessionKey = 'randomMap_' . md5(static::class) . '_' . $randomPart;
            }
        }
        
        $this->__randomizer = new Randomizer($this->__sessionKey, $this->sessionManager);
        
        // Get attributes to randomize
        if (is_array($this->model->attributes ?? null))
        {
            $attributes = array_keys($this->model->attributes);
        }
        elseif (method_exists($this->model, 'attributes'))
        {
            $attributes = $this->model->attributes();
        }
        else
        {
            $attributes = [];
        }
        
        // Combine model attributes and honeypots
        $logicalFields = array_merge($attributes, $this->honeypotFields);
        
        // Retrieve existing field map or generate new randomized names
        $this->fieldNameMap = $this->__randomizer->getMap();
        
        if (empty($this->fieldNameMap))
        {
            $this->fieldNameMap = $this->__randomizer->generate($logicalFields);
        }
    }
    
    public function run(): void
    {
        // Output hidden input with __sessionKey before form end tag
        echo Html::hiddenInput('randomizedFormSessionKey', $this->__sessionKey);
        
        // Register JS for dynamically applying placeholders if any provided
        if (!empty($this->placeholders))
        {
            $this->registerPlaceholderScript($this->placeholders);
        }
        
        parent::run();
    }
    
    /**
    * Register JS code to fill placeholders for randomized input names.
    * @param array<string,string> $placeholders
    * @return void
    */
    private function registerPlaceholderScript(array $placeholders): void
    {
        if (empty($this->fieldNameMap) || empty($placeholders))
        {
            return;
        }
        
        $fieldMapJson = Json::htmlEncode($this->fieldNameMap);
        $placeholdersJson = Json::htmlEncode($placeholders);
        
        $uniqueId = uniqid('', false);// Create 13-char unique string based on time
        $cacheKey = $this->__cacheKeyPrefix . $uniqueId . '_js_block_';
        
        $jsBlock = $this->getCachedJsBlock($cacheKey, function () use (
            $fieldMapJson,
            $placeholdersJson
        )
        {
            return <<<JS
            (function(\$)
            {
                \$(function()
                {
                    var map = $fieldMapJson;
                    var placeholders = $placeholdersJson;
                    
                    Object.keys(map).forEach(function(logicalName)
                    {
                        var randomized = map[logicalName];
                        var el = document.querySelector('[name="' + randomized + '"]');
                        
                        if (el && placeholders[logicalName])
                        {
                            el.setAttribute('placeholder', placeholders[logicalName]);
                        }
                    });
                });
            })(jQuery);
            JS;
        });
        
        // Register the JavaScript.
        $this->getView()->registerJs($jsBlock, View::POS_END);
    }
    
    /**
    * Override ActiveForm::field() to pass randomized fieldNameMap to ActiveField
    * @param object $model
    * @param string $attribute
    * @param array $options
    * @return string|app\widgets\randomizedForm\RandomizedActiveField
    *
    * Note: Do NOT add parameter(s) type hints. This method overrides an interface 
    * without type hints on parameters, adding types will cause PHP compatibility errors. 
    * Only docblock types to indicate expected input types.
    */
    public function field($model, $attribute, $options = [])
    {
        $options['fieldNameMap'] = $this->fieldNameMap;
        
        return parent::field($model, $attribute, $options);
    }
    
    /**
    * Get the Randomizer instance, for convenience
    */
    public function getRandomizer()
    {
        return $this->__randomizer;
    }
}

declare(strict_types=1);

namespace app\widgets\randomizedForm;

use Yii;
use yii\web\Request;

use app\components\SessionManager;
use app\components\interfaces\SessionInterface;

/**
* Core service that generates randomized field names,
* stores them in session via SessionInterface,
* maps POST data back,
* and checks honeypot fields.
*/
class Randomizer
{
    /**
    * Session Manager Interface
    * @see app\components\interfaces\SessionInterface.php
    * @var SessionInterface
    */
    protected SessionInterface $__session;
    
    // @var string The session key to use
    protected string $__sessionKey;
    
    // @var array<string,string>
    protected array $__fieldMap = [];
    
    /**
    * Construct
    * @param string $sessionKey
    * @param SessionInterface|null $session Allows internal creation if null.
    */
    public function __construct(string $sessionKey, ?SessionInterface $session = null)
    {
        $this->__sessionKey = $sessionKey;
        $this->__session = $session ?? new SessionManager();
    }
    
    /**
    * Generate random string identifiers for given field list,
    * store in session and return the map.
    * @param list<string> $fields
    *
    * @return array<string,string> map logical field to randomized string
    */
    public function generate(array $fields): array
    {
        $map = [];
        
        foreach ($fields as $field)
        {
            $map[$field] = $this->generateRandomString();
        }
        
        $this->__fieldMap = $map;
        $this->__session->set($this->__sessionKey, $map);
        
        return $map;
    }
    
    /**
    * Retrieve the map from session or return empty array
    * @return array<string,string>
    */
    public function getMap(): array
    {
        if (empty($this->__fieldMap))
        {
            $stored = $this->__session->get($this->__sessionKey);
            $this->__fieldMap = is_array($stored) ? $stored : [];
        }
        
        return $this->__fieldMap;
    }
    
    /**
    * Reverse-map submitted POST data keys to logical attribute names.
    * @param array<string,mixed> $postData
    * @return array<string,mixed> mapped data in logical keys.
    */
    public function mapPostData(array $postData): array
    {
        $map = $this->getMap();
        $reverseMap = array_flip($map);
        $mapped = [];
        
        foreach ($postData as $postKey => $value)
        {
            if (isset($reverseMap[$postKey]))
            {
                $mapped[$reverseMap[$postKey]] = $value;
            }
        }
        
        return $mapped;
    }
    
    /**
    * Check whether any honeypot fields are filled.
    * @param array<string,mixed> $mappedData
    * @param list<string> $honeypotFields
    * @return bool True if any honeypot field is filled, false otherwise.
    */
    public function honeypotFilled(array $mappedData, array $honeypotFields): bool
    {
        foreach ($honeypotFields as $honeypot)
        {
            if (!empty($mappedData[$honeypot]))
            {
                return true;
            }
        }
        
        return false;
    }
    
    /**
    * Generate a random alphanumeric string
    * @param int $length
    * @return string
    */
    protected function generateRandomString(int $length = 10): string
    {
        return substr(str_shuffle(str_repeat('abcdefghijklmnopqrstuvwxyz0123456789', $length)), 0, $length);
    }
    
    /**
    * Instantiate Randomizer using session key from POST request,
    * fallback to default key, creates SessionManager internally if not passed.
    * @param SessionInterface|null $session Optional session manager.
    * @param Request|null $request Optional request object.
    * @return self
    */
    public static function fromRequest(?SessionInterface $session = null, ?Request $request = null): self
    {
        $request = $request ?? \Yii::$app->request;
        $sessionKey = (string) $request->post('randomizedFormSessionKey', 'randomizedActiveFormFieldMap');
        
        return new self($sessionKey, $session);
    }
    
    /**
    * Get the session key currently used
    */
    public function getSessionKey(): string
    {
        return $this->__sessionKey;
    }
    
    /**
    * Get access to session interface
    *
    public function getSession(): SessionInterface
    {
        return $this->__session;
    }//*/
}

declare(strict_types=1);

namespace app\widgets\randomizedForm;

use Yii;
use yii\widgets\ActiveField;

/**
* RandomizedActiveField overrides Yii2 ActiveField to render inputs with 
* randomized 'name' attributes using the fieldNameMap populated by 
* RandomizedActiveForm.
*/
class RandomizedActiveField extends ActiveField
{
    // @var array randomized map of attribute => randomized name
    public array $fieldNameMap = [];
    
    /**
    * Internal helper to override the 'name' attribute if randomized
    * @param array<string,mixed> $options
    * @return array<string,mixed>
    */
    protected function applyRandomName(array $options): array
    {
        if ($this->fieldNameMap !== [] && isset($this->fieldNameMap[$this->attribute]))
        {
            $options['name'] = $this->fieldNameMap[$this->attribute];
        }
        
        return $options;
    }
    
    /**
    * @inheritDoc
    *
    * Note: Do NOT add parameter(s) type hints. This method overrides an interface 
    * without type hints on parameters, adding types will cause PHP compatibility errors. 
    * Only docblock types to indicate expected input types.
    */
    public function input($type, $options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::input($type, $options);
    }
        
    /**
    * @inheritDoc
    */
    public function textInput($options = [])
    {
        return $this->input('text', $options);
    }
    
    /**
    * @inheritDoc
    */
    public function passwordInput($options = [])
    {
        return $this->input('password', $options);
    }
    
    /**
    * @inheritDoc
    */
    public function hiddenInput($options = [])
    {
        return $this->input('hidden', $options);
    }
    
    /**
    * @inheritDoc
    */
    public function checkbox($options = [], $enclosedByLabel = true)
    {
        $options = $this->applyRandomName($options);
        
        return parent::checkbox($options);
    }
    
    /**
    * @inheritDoc
    */
    public function checkboxList($items, $options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::checkboxList($items, $options);
    }
    
    /**
    * @inheritDoc
    */
    public function radio($options = [], $enclosedByLabel = true)
    {
        $options = $this->applyRandomName($options);
        
        return parent::radio($options);
    }
    
    /**
    * @inheritDoc
    */
    public function radioList($items, $options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::radioList($items, $options);
    }
    
    /**
    * @inheritDoc
    */
    public function dropDownList($items, $options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::dropDownList($items, $options);
    }
    
    /**
    * @inheritDoc
    */
    public function listBox($items, $options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::listBox($items, $options);
    }
    
    /**
    * @inheritDoc
    */
    public function fileInput($options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::fileInput($options);
    }
    
    /**
    * @inheritDoc
    */
    public function textarea($options = [])
    {
        $options = $this->applyRandomName($options);
        
        return parent::textarea($options);
    }
    
    /**
    * @inheritDoc
    */
    public function urlInput($options = [])
    {
        return $this->input('url', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function emailInput($options = [])
    {
        return $this->input('email', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function numberInput($options = [])
    {
        return $this->input('number', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function rangeInput($options = [])
    {
        return $this->input('range', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function telInput($options = [])
    {
        return $this->input('tel', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function timeInput($options = [])
    {
        return $this->input('time', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function dateInput($options = [])
    {
        return $this->input('date', $this->applyRandomName($options));
    }
    
    /**
    * @inheritDoc
    */
    public function datetimeInput($options = [])
    {
        return $this->input('datetime-local', $this->applyRandomName($options));
    }
}

    public function actionContact(string $subject = null): Response|string
    {
        // Set Page title for the browser tab/page title and page header.
        $pageTitle = \Yii::t('app', 'contact us');
        // Set Meta Data for website title, keywords, description and prepend site name(bool).
        $this->setMeta($pageTitle, 'contact, contact us');
        // Set page breadcrumbs. (optional)
        //\Yii::$app->layoutManager->addBreadcrumb($pageTitle);// page now.
        
        
        // Setup $model
        if ($this->_isGuest)
        {
            $model = new ContactForm();
            $model->subject = $subject ?? '';
        }
        else
        {
            $user = RoleHelper::userIdentity();
            $model = new ContactForm();
            
            if (!$user)
            {
                // Firstname, lastname and email are all required feilds
                $model->name = $user->firstname . ' ' . $user->lastname;
                $model->email = $user->email;
                $model->subject = $subject ?? '';
            }
            else
            {
                $model->subject = $subject ?? '';
            }
        }
        
        // define your honeypot fields here as in your widget
        $honeypots = ['emaill'];
        
        $request = \Yii::$app->request;
        if ($request->isPost)
        //if (($request->isPost) && ($model->load($request->post()) && $model->validate()))
        {
            // Instantiate Randomizer via POST data's randomizedFormSessionKey or default
            $randomizer = Randomizer::fromRequest();
            
            $post = $request->post();
            $mappedData = $randomizer->mapPostData($post);
            
            // Check honeypots
            if ($randomizer->honeypotFilled($mappedData, $honeypots))
            {
                \Yii::$app->session->setFlash('error', 'Bot activity detected.');
                // TODO add bot logging here...
                
                return $this->refresh();
            }
            
            if ($model->load($mappedData, '') && $model->validate())
            {
                if ($model->process())
                {
                    // Optionally clear the randomized field map from session after successful submission
                    $randomizer->getSession()->remove($randomizer->getSessionKey());
                    
                    AlertHelper::setFlash(
                        'success',
                        \Yii::t(
                            'app', 
                            '<h4 class="alert-heading">Thank You</h4>
                            <p>Thank you for contacting us. We will respond to your inquiry as soon as possible.</p>'
                        ),
                        5000
                    );
                    
                    // Handle the POST-Redirect-GET pattern.
                    return $this->refresh();
                }
                else
                {
                    AlertHelper::setFlash('error', ErrorMessageHelper::processingError(), 8000);
                }
            }
            
        }
        
        // Re-rendered regardless of whether there was an error or if the form validation failed.
        return $this->render('contact', [
            //'pageTitle' => $pageTitle,
            'model' => $model,
            'honeypotFields' => $honeypots
        ]);
    }

<?php
use app\widgets\randomizedForm\RandomizedActiveForm;
use app\widgets\randomizedForm\RandomizedActiveField;
use yii\helpers\Html;  // for submit button

/** @var \yii\web\View $this */
/** @var \app\models\ContactForm $model */

$form = RandomizedActiveForm::begin([
    'id' => 'contact-form',
    'fieldClass' => RandomizedActiveField::class,
    'model' => $model,
    'honeypotFields' => ['emaill'],  
    'placeholders' => [
        'name' => 'Enter Name',
        'email' => 'Enter email',
        'subject' => 'Enter subject',
        'body' => 'Type your message here...',
        'verifyCode' => 'Enter Security Code',
    ],
]);

// Helper function to log and render field
function debugAndEchoField($field, $fieldName) {
    $html = (string) $field;
    \Yii::error("Field '$fieldName' HTML start: " . substr($html, 0, 100), __METHOD__);
    echo $html;
}

// Fields with logging
debugAndEchoField($form->field($model, 'name')->textInput(), 'name');
debugAndEchoField($form->field($model, 'email')->textInput(), 'email');
debugAndEchoField($form->field($model, 'subject')->textInput(), 'subject');
debugAndEchoField($form->field($model, 'body')->textarea(['rows' => 6]), 'body');
debugAndEchoField($form->field($model, 'verifyCode')->widget(\yii\captcha\Captcha::class), 'verifyCode');

// Honeypot fields hidden visually
foreach ($form->honeypotFields as $hp) {
    debugAndEchoField(
        $form->field($model, $hp)->textInput([
            'style' => 'position:absolute; left:-9999px;',
            'tabindex' => '-1',
            'autocomplete' => 'off',
            'aria-hidden' => 'true',
        ])->label(false),
        $hp
    );
}//*/

// Submit button
echo Html::submitButton('Submit', ['class' => 'btn btn-primary']);

RandomizedActiveForm::end();

this is the entire code...

Thanks for sharing all the code — this looks solid and well-structured. You’ve integrated RandomizedActiveForm, Randomizer, and your controller/view properly.
Summary / sanity checklist — why your form fields should render:

    In RandomizedActiveForm::init() you generate or retrieve randomized field names based on the model attributes + honeypots.
    In your view, you begin RandomizedActiveForm::begin() and call $form->field(...)->inputType() for every field, echoing the output.
    You end with RandomizedActiveForm::end() which calls run() to render the hidden session key and the JS for placeholders.
    Your Logged output shows your fields are rendering HTML (or at least creating proper strings).
    Honeypot field is included and hidden off-screen with CSS.
    Controller loads, maps, validates, and handles POST properly.

Given the above — if you still see no form HTML in browser source, then the most frequent remaining causes are:
1. View code output suppression

Make sure the view file is the file actually rendered by your controller action.

    Add a unique comment or echo at the top, like:

<?php
echo '<!-- DEBUG Contact view loaded -->';
?>

    Refresh the browser and View Source to verify it shows.

2. Output buffering / echo missing somewhere

Your form fields are cast and echoed correctly, but still no markup?

    Make sure the PHP template engine (e.g. if you use renderAjax() or layouts) outputs content.

    Make sure no ob_start() or flush issues supress output.

3. Layout or main template issues

If your layout is missing <?= $content ?> or swallows output, nothing appears.

Check you have in your layout file:

<?php $this->beginBody() ?>
<?= $content ?>
<?php $this->endBody() ?>

4. Check for PHP errors/warnings suppressed or hidden

    Check server error logs.
    Temporarily enable error display with:

ini_set('display_errors', 1);
error_reporting(E_ALL);

At top of entry script.
Sometimes fatal errors prevent output but no visible message.
5. Verify $model attributes and validation

You have model public properties — good.

Try:

var_dump($model->attributes());

in controller or view to verify attributes list is not empty.
6. Browser Inspect / View Source comparison

    View source in browser (Ctrl+U) — do you see <form id="contact-form"?

    Use devtools to check for form in DOM even if empty or hidden.

7. Confirm CSS / JS do not hide form or remove it

    Check CSS rules affecting form or .form-group

    Check console for JS errors causing form scripts to fail.

Next Debug Step

Try minimal test — replace your view content temporarily with this:

<?php
use yii\widgets\ActiveForm;
use yii\helpers\Html;

/* @var $model \app\models\ContactForm */

echo '<!-- minimal test -->';

$form = ActiveForm::begin(['id' => 'test-form']);
echo $form->field($model, 'name')->textInput();
echo Html::submitButton('Submit');
ActiveForm::end();

If this minimal form does not render:

    The problem is general — model not passed properly, or view/layout broken.

If it DOES render:

    The problem is specific to your RandomizedActiveForm or usage.

Also try logging content output:

Inside your view:

ob_start();

$form = RandomizedActiveForm::begin([ ... ]);
// fields as before
RandomizedActiveForm::end();

$content = ob_get_clean();

\Yii::info("FORM HTML content: " . substr($content, 0, 500), __METHOD__);

echo $content;

Final notes

Your code seems basically correct — so the issue is very likely view output not flushed or displayed properly, or the wrong view file used.

If you want, I can help you audit your controller, view rendering call, and layout.

In summary: Your form is generating HTML strings (confirmed by logs), but they somehow are never shown in the browser source.

If you send me:

    Your controller’s render call or renderPartial method
    The layout file (or at least relevant parts like <?= $content ?>),
    The full view file path and confirm if that matches the edited file

I can help pinpoint where the output is swallowed/missing.

    View code output suppression

Make sure the view file is the file actually rendered by your controller action.

Add a unique comment or echo at the top, like:

'; ?>

Refresh the browser and View Source to verify it shows.

ok help rewrite to echo all the fields and not just error log lets break the view file down and see what peieve of the code is preventing it from even appearing in the source code

<?php
use app\widgets\randomizedForm\RandomizedActiveForm;
use app\widgets\randomizedForm\RandomizedActiveField;
use yii\helpers\Html;  // for submit button

/** @var \yii\web\View $this */
/** @var \app\models\ContactForm $model */

$form = RandomizedActiveForm::begin([
    'id' => 'contact-form',
    'fieldClass' => RandomizedActiveField::class,
    'model' => $model,
    'honeypotFields' => ['emaill'],  
    'placeholders' => [
        'name' => 'Enter Name',
        'email' => 'Enter email',
        'subject' => 'Enter subject',
        'body' => 'Type your message here...',
        'verifyCode' => 'Enter Security Code',
    ],
]);

// Helper function to log and render field
function debugAndEchoField($field, $fieldName) {
    $html = (string) $field;
    \Yii::error("Field '$fieldName' HTML start: " . substr($html, 0, 100), __METHOD__);
    echo $html;
}

// Fields with logging
debugAndEchoField($form->field($model, 'name')->textInput(), 'name');
debugAndEchoField($form->field($model, 'email')->textInput(), 'email');
debugAndEchoField($form->field($model, 'subject')->textInput(), 'subject');
debugAndEchoField($form->field($model, 'body')->textarea(['rows' => 6]), 'body');
debugAndEchoField($form->field($model, 'verifyCode')->widget(\yii\captcha\Captcha::class), 'verifyCode');

// Honeypot fields hidden visually
foreach ($form->honeypotFields as $hp) {
    debugAndEchoField(
        $form->field($model, $hp)->textInput([
            'style' => 'position:absolute; left:-9999px;',
            'tabindex' => '-1',
            'autocomplete' => 'off',
            'aria-hidden' => 'true',
        ])->label(false),
        $hp
    );
}

// Submit button
echo Html::submitButton('Submit', ['class' => 'btn btn-primary']);

RandomizedActiveForm::end();

Let's simplify your view to:

finish your thought