public static function beginForm($action = null, $method = 'post', $name = null, $options = [])
    {
        $hiddenInputs = [];
        $action = (array)$action;
        if (!isset($url['@scheme'])) {
            $action['@scheme'] = Url::ABS;
        }
        $action = Url::modify($action);
        $request = Instance::ensure('request', '\rock\request\Request', [], false);
        if ($request instanceof Request) {
            if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
                // simulate PUT, DELETE, etc. via POST
                $hiddenInputs[] = static::hiddenInput(
                    isset($name) ? $name . "[{$request->methodParam}]" : $request->methodParam,
                    $method,
                    ArrayHelper::getValue($options, 'hiddenMethod', [])
                );
                $method = 'post';
            }
        }

        $csrf = static::getCSRF();
        if ($csrf instanceof CSRF && $csrf->enableCsrfValidation && !strcasecmp($method, 'post')) {
            $token = $csrf->get();
            $hiddenInputs[] = static::hiddenInput(
                isset($name) ? "{$name}[_csrf]" : '_csrf',
                $token,
                ArrayHelper::getValue($options, 'hiddenCsrf', [])
            );
        }

        if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
            // query parameters in the action are ignored for GET method
            // we use hidden fields to add them back
            foreach (explode('&', substr($action, $pos + 1)) as $pair) {
                if (($pos1 = strpos($pair, '=')) !== false) {
                    $hiddenInputs[] = static::hiddenInput(
                        urldecode(substr($pair, 0, $pos1)),
                        urldecode(substr($pair, $pos1 + 1))
                    );
                } else {
                    $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
                }
            }
            $action = substr($action, 0, $pos);
        }
        unset($options['hiddenMethod'], $options['hiddenCsrf']);
        $options['action'] = $action;
        $options['method'] = $method;
        $form = static::beginTag('form', $options);
        if (!empty($hiddenInputs)) {
            $form .= "\n" . implode("\n", $hiddenInputs);
        }

        return $form;
    }