Flick
Guide Validation Services API Pro Examples GitHub
Docs / Getting Started

Configuration

Flick works with zero configuration, right out of the box.

However, sometimes you'll need to pass additional values to Flick when creating a form, such as adding an ID or enabling the cache. This is easily achieved by creating a configuration array and passing it to Flick during instantiation.

$config = [
    'id' => 'myFlickForm'
];

$form = new Flick($config);

Flick's Defaults

These default configuration values can be overridden when instantiating Flick.

$config = [
    'action' => '/',        // or the current request URI, when there is one
    'dateFormat' => 'Y-m-d',
    'debug' => false,
    'echo' => true,
    'id' => 'myForm',
    'lang' => 'en',
    'trim' => true,
    'views' => 'flick'
];

Tip

If views is the only thing you're changing, pass the name straight to Flick instead of building an array:

$form = new Flick('bootstrap');

General Settings

action

  • string
  • Default: '/'
  • The form submission URL. If not specified, the form will submit to the current page.

assets

  • string
  • Path to your assets directory, which can include custom views, language files, and form definitions.

Adding an assets value unlocks the ability to easily add your own forms, dropdowns, views, and more. Just add a path to the directory in which you keep your files and Flick will look in there first. If a form, dropdown, or view file isn't found, Flick will attempt to use a comparable default instead. Language files are the exception: when both assets and lang are set, Flick loads the language files from <assets>/lang/<code>/ and throws if they're missing — even for 'lang' => 'en' — so copy rules.php and messages.php into your assets directory when you set both keys.

Read more about using assets in the Customizing docs.

$config = [
    'assets' => __DIR__.'/myFlickAssets'
];

Warning

The directory must already exist. Flick throws a path error at instantiation if it doesn't, so create the directory before pointing assets at it.

Flick only reads from this directory. It can be read-only, and it can sit anywhere. The one exception is cache below, which writes compiled views into it — read that section before turning caching on.

cache

  • bool|string
  • Enables view caching. Set to flush to clear the cache.

Flick uses view files to create your form elements. By default, these views are compiled on demand, meaning when someone views your form; each element is compiled - every time it's viewed - which can have a negative effect on performance. Therefore, Flick offers the option to easily precompile your views and cache them to disk.

To enable caching, you must first create an assets directory, then set the cache value to TRUE. Caching without an assets path throws — Flick has nowhere to write the compiled views.

$config = [
    'assets' => __DIR__.'/myFlickAssets',
    'cache' => true
];

Keep your assets folder out of your public folder

When cache is on, Flick writes files into your assets folder. If that folder sits inside the folder your web server serves, anybody can open those files in a browser. The cached views are just HTML, so nobody's stealing secrets, but a folder your app writes to shouldn't be reachable from the internet.

The easy fix is to keep the assets folder one level up, next to your public folder instead of inside it:

myproject/
├── public/            <- your web server points here
│   └── index.php
└── myFlickAssets/     <- up here, where the web server can't reach it

If you don't have a public folder and your PHP files sit right in the served folder, then your assets folder is being served too, and you'll want to block it.

Apache

Flick does this for you. The first time it caches a view it writes an .htaccess file into myFlickAssets/cache that denies all requests. LiteSpeed reads that file too. If you already have your own .htaccess in there, Flick leaves it alone.

Heads up: Apache only reads .htaccess files when AllowOverride is turned on, and a fresh Ubuntu or Debian server has it turned off for /var/www. If that's you, either turn it on or add the deny rule to your site config yourself.

nginx

nginx ignores .htaccess files, so add this to your site's server block:

location ^~ /myFlickAssets/ {
    deny all;
}

The ^~ part matters. Without it, nginx can hand a request like /myFlickAssets/lang/en/rules.php to PHP before it ever gets to your deny rule.

Caddy

Same story as nginx. Add this to your site in the Caddyfile:

@flickAssets path /myFlickAssets/*
respond @flickAssets 403

Check it

Whichever server you're on, open one of your own asset files in a browser, something like https://yoursite.com/myFlickAssets/lang/en/rules.php. You want a 403 or a 404. If you see anything else, the folder is public.

To flush the cache, set the cache value to flush, and all cached items will be deleted. Flushing prints a success alert into the page, so remove the flush value once it has run.

$config = [
    'assets' => __DIR__.'/myFlickAssets',
    'cache' => 'flush'
];

Tip

Setting the cache value to FALSE disables the cache, but does not delete the cached files.

csrf

  • bool|int|string
  • Default: 3600
  • Enable CSRF protection. If set to an integer, it specifies the token timeout in seconds.

CSRF protection is automatically added to your form by creating a hidden field and populating it with a random token, which is validated upon form submission. If the token fails validation, an error message will be shown to the user.

The csrf value changes the expiration time in seconds. The default expiration value is 3600 seconds (1 hour).

$config = [
    'csrf' => 1800
];

Inside a framework

When a framework supplies the token — Laravel's csrf_token(), wired up for you by flickphp/laravel — Flick renders that token instead of issuing its own, and the accepted values change meaning:

Value What Flick does
false Renders the framework's token; trusts the framework's middleware to validate it. The default in Laravel.
'strict' Renders the framework's token and compares the posted _token against it itself.
true or an integer Ignores the framework's token and uses Flick's own session token, with the integer as its timeout.

false is a trust, not a check. It holds for a route inside Laravel's web middleware group, where Laravel validates the token before your controller runs. A form posted to a route outside that group gets no CSRF check from either side — keep form routes in web, or set 'strict'.

$config = [
    'csrf' => 'strict'
];

'strict' is opt-in rather than the default because a client that sends the token only as a header posts no _token field at all — Axios sends X-XSRF-TOKEN automatically — and Flick would reject that submission.

dateFormat

  • string
  • Default 'Y-m-d'
  • The default date format used for date-related form fields and validation.

debug

  • bool
  • Default: FALSE
  • Show the error details on Flick's error pages.

Flick never leaks anything about your server in production. When something throws with debug off, your visitors see a short generic message and nothing else - no path, no exception message, no source, no stack trace. All of it goes to your PHP error log instead, including the message, the help text, the code sample and the docs link.

That means with debug off you read the details from the log, not from the page. Turn debug on while developing to get the full error page with the file, the line, a source excerpt and the stack trace.

$config = [
    'debug' => true
];

echo

  • bool
  • Default: TRUE
  • If true, form elements are immediately echoed. If false, they are returned as strings.

Flick will automatically echo form elements and messages to the screen, which is usually what you want. However, in some instances this is not an option, such as when using a templating framework. In these cases simply set echo to FALSE and manually echo your elements and messages.

$config = [
    'echo' => false
];

// manually echo elements...
echo $form->text('name', 'Name');

honeypot

  • string|null
  • Name of the honeypot field for bot protection. If set, Flick will add a hidden field to catch automated submissions.

Adds a hidden "honeypot" field to the form, which is useful in preventing bots from hijacking the form. If a bot fills in the honeypot field the script will automatically exit upon submission. Just add your field name as the honeypot value and you're good to go.

$config = [
    'honeypot' => 'company'
];

Flick adds the field for you when it renders open():

<input type="text" name="company" value="" style="display:none">

Tip

Adding a honeypot helps, but it will never be 100% successful in stopping bot hijacks. For this reason you may want to consider adding Flick's reCAPTCHA or Turnstile services to your forms.

id

  • string
  • Default: 'myForm'
  • The default ID attribute for generated forms.

Warning

If you render more than one form in the same request/session, give each an explicit, unique id. Flick keys each form's stored field definitions by its id, so two forms sharing the default 'myForm' overwrite each other's definition — and a no-argument request() would then validate against the wrong form's rules.

lang

  • string
  • Default: 'en'
  • The language code for localization. Flick will attempt to load the corresponding language file.

Flick ships with English (US) only. To use another language, add your own rules.php and messages.php to <assets>/lang/<code>/, then point lang at that code. Both files must exist, but either can be partial: a key you leave out falls back to the shipped English for that key. Read more about Flick's translation service.

$config = [
    'assets' => __DIR__.'/myFlickAssets',   // expects myFlickAssets/lang/es/rules.php + messages.php
    'lang' => 'es'
];

Warning

Setting lang to a code with no matching language files throws a "file was not found" error at instantiation. 'en' is the only code that works without an assets directory.

services

  • array
  • An associative array for configuring services. Keys are service names, values are arrays of configuration options for each service.

Each key is a service name; its value is that service's configuration. Installed services work without an entry here — add one only when the service needs settings.

Read more about adding Services.

$config = [
    'services' => [
        'my-service' => [
            'publicKey' => '1234567890',
            'secretKey' => 'abcdefghij',
        ],
    ]
];

showErrorsAlert

  • bool
  • Default: FALSE
  • Show a single alert box listing every error, instead of only showing each error beneath its field.

By default Flick prints each error under the field it belongs to. Set showErrorsAlert to TRUE and $form->errors() will render one alert box containing all of them.

$config = [
    'showErrorsAlert' => true
];

$form->errors();                 // alert box listing every error
$form->errors('Please fix these'); // same, with your own heading

Warning

$form->errors() outputs nothing while showErrorsAlert is off, which is the default. If you called errors() and saw a blank page, this is why. The per-field error messages are unaffected — they render either way.

trustedProxies

  • array or not set
  • Default: not set
  • Which proxy servers Flick believes when they say the original request used HTTPS (the X-Forwarded-Proto header) or came from a given client IP (X-Forwarded-For).

When your app sits behind a reverse proxy or load balancer, HTTPS often ends at the proxy — PHP itself only sees plain HTTP. Proxies pass the real story along in forwarded headers, but those headers can be faked by anyone, so Flick only believes them when the request comes from a proxy it trusts.

Out of the box, Flick trusts proxies on your own machine or private network (nginx in front of PHP-FPM, Docker, a load balancer in your VPC). Most setups need no configuration at all.

Set trustedProxies when you want control:

// Trust only these proxies (IPs or CIDR ranges)
$config = [
    'trustedProxies' => ['203.0.113.5', '10.0.0.0/8']
];

// Strict mode: never trust forwarded headers
$config = [
    'trustedProxies' => []
];

// Trust any proxy (only behind a proxy you fully control)
$config = [
    'trustedProxies' => ['*']
];

Setting the key replaces the built-in private-network rule — only your list counts.

views

  • string
  • Default: 'flick'
  • The views/theme to use for form elements. This affects the HTML structure and CSS classes applied to form elements.

Flick has support for Tailwind, Bootstrap (defaults to v.5), Bootstrap 4, Bulma, Foundation, and Materialize right out of the box. Just tell Flick which one you want to use when creating a new form, and Flick will take care of the rest. If you don't add anything, Flick will use its default views, which you can customize via CSS.

Of course, you can add your own views too.

$config = [
    'views' => 'tailwind'
];

Form Handling

ajax

  • bool
  • Enable handling of XHR/Ajax form submissions.

request

  • RequestInterface
  • A custom request adapter for reading HTTP data. Useful for framework integration and testing.

By default, Flick reads from PHP's superglobals ($_POST, $_GET, etc.). You can inject a custom adapter to read from framework Request objects or mock data for tests.

use Flick\Http\ArrayRequest;

$config = [
    'request' => ArrayRequest::createPost([
        '_id' => 'myForm',                // must match the form's id or submitted() is false
        'email' => 'test@example.com',
    ]),
    'csrf' => false, // Disable CSRF for testing
];

See the Request Adapters guide for detailed usage.

session

  • SessionInterface
  • A custom session adapter for session management. Useful for framework integration and testing.

By default, Flick uses PHP's native session handling. You can inject a custom adapter to integrate with framework session managers or mock sessions for tests.

use Flick\Session\ArraySession;

$config = [
    'session' => new ArraySession(['user_id' => 123]),
    'csrf' => false,
];

See the Session Adapters guide for detailed usage.

persistToSession

  • bool
  • Default: false
  • Persist every validated field value to the session, keyed by field name, and repopulate rendered fields from those values.

This is the mechanism multistep forms use to carry values between steps — multistep enables it for its own form automatically. Turn it on manually only when you want validated values (including sensitive ones) written to session storage:

$config = [
    'persistToSession' => true,
];

Injecting a session adapter via the session key does not enable persistence — the two settings are independent.

sessionAutoStart

  • bool
  • Default: true
  • Whether Flick should automatically start the PHP session.

Set to false when using a framework that manages its own session lifecycle. This prevents Flick from calling session_start().

Hardened session cookies

When Flick starts the native session itself, it sets hardened cookie params (HttpOnly, Secure on HTTPS, SameSite=Strict). PHP only lets these be set before session_start(), so if the session was already started elsewhere (a framework, session.auto_start, or earlier code), Flick can't apply them — the existing cookie settings stand. Let Flick own session startup if you want its hardened defaults.

$config = [
    'session' => new LaravelSession(session()),
    'sessionAutoStart' => false, // Framework already started the session
];

Response Handlers

Response handlers let you customize how Flick responds to events like redirects and security checks. This is essential for framework integration. See the Framework Integration guide for detailed examples.

Handlers must be closures (an arrow function or anonymous function, as in the examples below). A plain function name string like 'myHandler' is not accepted.

handlers

  • ResponseHandlers
  • A pre-configured ResponseHandlers instance for full control over all response behavior.
use Flick\Http\ResponseHandlers;

$handlers = (new ResponseHandlers())
    ->onRedirect(fn($r) => redirect($r->getUrl()))
    ->onHoneypot(fn() => abort(403));

$form = new Flick([
    'handlers' => $handlers
]);

onRedirect

  • Closure
  • Custom handler for redirect responses. Receives a RedirectResponse object.
$config = [
    'onRedirect' => fn($response) => redirect($response->getUrl())
];

onJson

  • Closure
  • Custom handler for JSON responses (AJAX submissions). Receives a JsonResponse object.
$config = [
    'onJson' => fn($response) => response()->json($response->getData())
];

onException

  • Closure
  • Custom handler for exception/error pages. Receives an HtmlResponse object.
$config = [
    'onException' => fn($response) => response($response->getContent(), 500)
];

onHoneypot

  • Closure
  • Custom handler when honeypot field is filled (bot detected). Receives no parameters.
$config = [
    'onHoneypot' => fn() => abort(403)
];

onCsrfExpired

  • Closure
  • Custom handler when CSRF token expires. Receives the error message string.
$config = [
    'onCsrfExpired' => fn($message) => throw new TokenMismatchException($message)
];

Validation

messages

  • array
  • Custom error messages for validation rules. Keys are field names, values are arrays of custom messages.

Add your custom validation messages to the config array, and Flick will automatically apply them to your field elements and form requests.

$config = [
    'messages' => [
        'name' => [
            'min' => 'name must be at least 2 characters',
            'max' => 'name must be under 60 characters',
            'required' => 'Please enter your name',
        ],
        'email' => [
            'email' => 'Please enter a valid email address',
            'required' => 'Please enter your email',
        ],
    ]
];

rules

  • array
  • An associative array of validation rules for form fields. Keys are field names, values are arrays of rules.

Add your validation rules to the config array, and Flick will automatically apply them to your field elements and form requests.

$config = [
    'rules' => [
        'name' => ['min:2', 'max:60', 'required'],
        'email' => ['email', 'required'],
    ]
];