Skip to content

Leaf 5 โ€” Sessions, Cookies, Flash & Security Reference โ€‹

Sessions โ€‹

bash
leaf install session

Set โ€‹

php
session()->set('firstName', 'John');
session()->set('user', ['name' => 'John Doe', 'email' => 'john@example.com']);
session()->set(['firstName' => 'John', 'lastName' => 'Doe']);

// Dot notation โ€” set nested value
session()->set('user.location', 'Everywhere');

Get โ€‹

php
$name = session()->get('firstName');
$name = session()->get('firstName', 'default');       // with default
$name = session()->get('firstName', 'default', false); // sanitization off
$name = session()->get(param: 'firstName', sanitize: false); // PHP 8+

// Multiple
$names = session()->get(['firstName', 'lastName']);

// Dot notation
$location = session()->get('user.location');

retrieve() โ€” get and delete (flash behavior) โ€‹

php
$name = session()->retrieve('firstName');   // returns value, then removes it
$name = session()->retrieve('firstName');   // now null
$name = session()->retrieve('firstName', 'John');  // with default

Check & Delete โ€‹

php
session()->has('firstName');
session()->has('user.username');    // dot notation

session()->delete('firstName');
session()->delete(['firstName', 'lastName']);
session()->clear();                 // delete everything

$all = session()->all();

Flash Messages โ€‹

Set via response (chainable):

php
response()->withFlash('message', 'Saved!')->redirect('/dashboard');
response()->withFlash('user', $userObject)->json('...');

Read via request:

php
$message = request()->flash();          // all flash data
$info    = request()->flash('info');    // specific key
$obj     = request()->flash('user');

Remove:

php
flash()->remove('info');

Toast Notifications (Blade + Tailwind + Alpine) โ€‹

php
return response()
    ->withFlash('leaf.toast', [
        'title'       => 'Email verified. Sign in to continue.',
        'description' => 'You can now sign in to your account.',
        'type'        => 'success',  // success, danger, warning, info, default
    ])
    ->redirect('/dashboard');

In your Blade layout:

html
<body>
  ...
  @toastContainer
</body>

Cookies โ€‹

bash
leaf install cookie

Set โ€‹

php
// Simple โ€” via response (chainable)
response()->withCookie('name', 'Fullname');
response()->withCookie('name', 'Fullname')->withCookie('age', 20)->json(['message' => 'Set']);

// Full options
cookie()->set('name', 'Fullname', [
    'expire'   => time() + 3600,
    'path'     => '/',
    'domain'   => 'example.com',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'None',
]);

Get โ€‹

php
$name    = request()->cookies('name');
$cookies = request()->cookies(['name', 'age']);  // ['name' => ..., 'age' => ...]
$all     = request()->cookies();

Delete โ€‹

php
response()->withoutCookie('name')->json(['message' => 'Deleted']);
cookie()->delete('name');
cookie()->deleteAll();

Validation โ€‹

bash
leaf install form

Basic Usage โ€‹

php
$data = request()->validate([
    'title'       => 'string|min:5',
    'email'       => 'email',
    'description' => 'optional|string|min:8',
]);

if (!$data) {
    $errors = request()->errors();
}

Rules with Parameters โ€‹

php
request()->validate([
    'bio'  => 'min:10',
    'age'  => 'between:[18,30]',
    'role' => 'in:[admin,user,guest]',
]);

Indexed Arrays โ€‹

php
request()->validate([
    'tags'      => 'array<string>',
    'prices'    => 'array<float>',
    'emails'    => 'array<email>',
    'passwords' => 'array<string|min:8>',
]);

Associative Arrays / Nested Objects โ€‹

php
request()->validate([
    'user.name' => 'string',
    'user.age'  => ['number', 'optional'],
]);

// Escape dot if key literally contains a dot
request()->validate(['user\.name' => 'string']);

Validate Non-Request Data โ€‹

php
$data = form()->validate(['name' => 'John', 'age' => 25], [
    'name' => 'string',
    'age'  => 'number',
]);

if (!$data) { $errors = form()->errors(); }

Custom Error Messages โ€‹

php
request()->validator()->message([
    'required' => '{Field} is required',      // {Field} capitalizes first letter
    'email'    => '{field} must be a valid email',
]);

Custom Rules โ€‹

php
// Regex
request()->validator()->rule('isEven', '/^\d*[02468]$/', '{field} must be even.');

// Function
request()->validator()->rule('isEven', function ($value) {
    return $value % 2 === 0;
}, '{field} must be even.');

request()->validate(['number' => 'isEven']);

Available Rules โ€‹

RuleDescription
emailValid email address
textAlphabetic + spaces ONLY โ€” wrong for passwords or free-form input
stringAny string (is_string) โ€” use this for passwords and free-form text
textOnlyAlphabetic only (no spaces)
alphaAlphabetic characters
alphaNumAlpha-numeric
alphaDashAlpha-numeric + underscores + dashes
usernameAlpha-numeric + underscores
numberNumeric only
floatFloat values
dateValid date
min:nMinimum value/length
max:nMaximum value/length
between:[a,b]Between two values
match:valueMust match value
contains:valueMust contain value
in:[a,b,c]Must be in list
ip / ipv4 / ipv6Valid IP address
urlValid URL
domainValid domain
creditCardValid credit card number
phoneValid phone number
uuidValid UUID
slugValid slug
jsonValid JSON string
regex:patternMust match regex
optionalField not required
array<rule>Indexed array of type

CSRF Protection โ€‹

bash
leaf install csrf

MVC: auto-configured after install. Basic: initialize before routes:

php
app()->csrf();
// ... routes

Protecting Forms (Blade) โ€‹

blade
<form action="/submit" method="POST">
    @csrf
    <input type="text" name="name">
    <button type="submit">Submit</button>
</form>

API / SPA โ€” X-CSRF-Token Header โ€‹

js
fetch('/submit', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': 'YOUR_CSRF_TOKEN',
    },
    body: JSON.stringify({ name: 'John Doe' }),
});

Get the token:

php
$token = csrf()->token();

Configuration โ€‹

php
// Basic app โ€” inline
app()->csrf([
    'secret'                 => 'my-secret-key',
    'methods'                => ['POST', 'PUT', 'PATCH', 'DELETE'],
    'except'                 => ['/webhook', '/api/public'],
    'messages.tokenNotFound' => 'Token not found.',
    'messages.tokenInvalid'  => 'Invalid token.',
    'onError'                => function ($error) {
        if ($error === 'tokenNotFound') {
            return response()->json(['error' => 'No CSRF token'], 403);
        } else {
            return response()->json(['error' => 'Invalid CSRF token'], 403);
        }
    },
]);

// MVC โ€” publish config
// leaf config:publish csrf  โ†’  config/csrf.php
// Secret default: derived from APP_KEY automatically.
// Override: X_CSRF_SECRET=my-secret-key in .env, or 'secret' in config (code wins).
// No APP_KEY + no secret = RuntimeException at startup. Fix: `php leaf key:generate`

CSRF is automatically disabled in test mode (APP_ENV != production).


Password Hashing โ€‹

Included in Leaf Auth. Install standalone if needed:

bash
leaf install password
php
use Leaf\Helpers\Password;

// Add a pepper/spice to all passwords
Password::spice('#@%7g0!&');
$spice = Password::spice();  // retrieve current spice

// Hash
$hash = Password::hash($password);                       // default algorithm
$hash = Password::hash($password, Password::BCRYPT);
$hash = Password::hash($password, Password::ARGON2);
$hash = Password::bcrypt($password, $options);
$hash = Password::argon2($password, $options);

// Verify
if (Password::verify($password, $hash)) { /* authenticated */ }
if (Password::bcryptVerify($password, $hash)) { /* bcrypt match */ }
if (Password::argon2Verify($password, $hash)) { /* argon2 match */ }