Lightpack Utility Functions
Lightpack provides few handy utility functions to simplify common usage scenarios in your application.
csrf_input()
What it does:
Outputs a hidden HTML input containing the current CSRF token (named _token).
When to use:
- Always include this in every
<form>that performs a POST, PUT, PATCH, or DELETE request. - Protects your application from Cross-Site Request Forgery attacks.
Example:
<form method="POST">
<?= csrf_input() ?>
<!-- other fields -->
</form>
csrf_token()
What it does: Returns the current CSRF token as a string.
When to use:
- When you need to access the token value directly (e.g., for AJAX requests or custom headers).
Example:
$token = csrf_token();
// Use $token in an AJAX request header
spoof_input()
spoof_input(string $method)
What it does: Outputs a hidden input to spoof HTTP methods (like PUT, PATCH, DELETE) in HTML forms.
When to use:
- When you want to send a method other than GET or POST in a form submission.
- HTML forms only support GET and POST natively; this lets you use RESTful verbs.
Example:
<form method="POST" action="/resource/123">
<?= csrf_input() ?>
<?= spoof_input('DELETE') ?>
<button type="submit">Delete</button>
</form>
hidden_input()
hidden_input(string $name, string $value = '')
What it does:
Outputs a hidden HTML input field with automatic XSS protection. Both the name and value are automatically escaped using htmlspecialchars().
When to use:
- When you need to include hidden data in forms (user IDs, redirect URLs, etc.).
- Prefer this over manually writing
<input type="hidden">for automatic security. - Any time you're passing user-generated or dynamic content in hidden fields.
Example:
<form method="POST" action="/profile/update">
<?= csrf_input() ?>
<?= hidden_input('user_id', $user->id) ?>
<?= hidden_input('redirect_to', '/dashboard') ?>
<button type="submit">Update</button>
</form>
// Automatically escapes malicious content
<?= hidden_input('data', '"><script>alert("XSS")</script>') ?>
// Output: <input type="hidden" name="data" value=""><script>...">
form_open()
form_open(string $action = '', string $method = 'POST', array $attributes = [], bool $csrf = true)
What it does: Opens an HTML form tag with automatic CSRF token injection and HTTP method spoofing support. Automatically escapes all attribute values for security.
When to use:
- For all forms in your application—provides CSRF token hidden field by default.
- When you need PUT, PATCH, or DELETE methods (automatically adds method spoofing).
- To reduce boilerplate and prevent forgetting CSRF tokens.
Parameters:
$action- Form action URL$method- HTTP method (GET, POST, PUT, PATCH, DELETE)$attributes- Additional HTML attributes (class, id, data-*, etc.)$csrf- Whether to include CSRF token (default: true)
Example:
// Basic POST form with CSRF
<?= form_open('/profile/update') ?>
<input type="text" name="name" value="<?= _e($user->name) ?>">
<button type="submit">Update</button>
<?= form_close() ?>
// GET form (no CSRF token)
<?= form_open('/search', 'GET') ?>
<input type="text" name="q" placeholder="Search...">
<button type="submit">Search</button>
<?= form_close() ?>
// DELETE with method spoofing
<?= form_open('/posts/123', 'DELETE') ?>
<button type="submit">Delete Post</button>
<?= form_close() ?>
// Outputs: method="POST" with hidden _method=DELETE and CSRF token
// With custom attributes
<?= form_open('/profile', 'POST', [
'class' => 'profile-form',
'id' => 'user-form',
'enctype' => 'multipart/form-data'
]) ?>
<!-- form fields -->
<?= form_close() ?>
// Disable CSRF for webhooks or public endpoints
<?= form_open('/webhook/stripe', 'POST', [], false) ?>
<!-- webhook payload -->
<?= form_close() ?>
Important:
- POST, PUT, PATCH, and DELETE forms automatically include CSRF tokens (unless disabled).
- PUT, PATCH, and DELETE are automatically converted to POST with method spoofing.
- GET forms never include CSRF tokens (even if
$csrf = true). - All attribute values are automatically escaped to prevent XSS attacks.
- Remember to add the
csrffilter to your route for CSRF-protected forms.
form_close()
form_close()
What it does:
Outputs a closing </form> tag.
When to use:
- To close forms opened with
form_open(). - Provides consistency and readability in your view templates.
Example:
<?= form_open('/profile/update') ?>
<!-- form fields -->
<?= form_close() ?>
dd()
dd(...$args)
What it does:
“Dump and Die”—outputs the contents of variables (like var_dump) and stops the script.
When to use:
- For debugging: inspect variables, arrays, or objects at any point in your code.
- Use in development only; never leave in production code.
Example:
dd($user, $posts);
pp()
pp(...$args)
What it does:
“Pretty Print”—outputs variables using print_r and stops the script. This function is useful for debugging purposes, especially when working with arrays and objects.
When to use:
- For debugging: best for arrays and objects where structure matters. This provides a clear and readable representation of the data.
- Use in development only.
Example:
pp($myArray);
pp($var1, $var2, $var3)
halt()
halt(int $code, string $message = '', array $headers = [])
What it does: Immediately halts execution and throws an HTTP exception with the specified status code. If no message is provided, uses the standard HTTP status message (e.g., "Not Found" for 404).
When to use:
- When you need to stop request processing and return an HTTP error response.
- For access control (403 Forbidden), missing resources (404 Not Found), or server errors (500 Internal Server Error).
- As a clean way to handle error conditions in controllers, middleware, or route handlers.
Example:
// Simple halt with standard message
halt(404); // Throws "Not Found"
halt(403); // Throws "Forbidden"
// With custom message
halt(404, 'User not found');
halt(403, 'You need admin access');
// With custom headers
halt(503, 'Service Unavailable', ['Retry-After' => '3600']);
// In conditional logic
if (!$user) {
halt(404, 'User not found');
}
// One-liner
if (!$user) halt(404);
once()
once(callable $callback)
What it does: Executes a callback only once and caches the result. Subsequent calls with the same callback return the cached result without re-executing the callback.
When to use:
- Prevent duplicate expensive operations (database queries, API calls, file reads)
- Lazy loading with automatic caching
- Ensure idempotent operations within the same request
Example:
// In a model - expensive query runs only once
class User
{
public function permissions()
{
return once(function() {
// This query only runs once per request
return Permission::where('user_id', $this->id)->all();
});
}
}
$user->permissions(); // Executes query
$user->permissions(); // Returns cached result
$user->permissions(); // Returns cached result
// Prevent duplicate API calls
$callback = function() {
return $httpClient->get('https://api.example.com/data');
};
$data1 = once($callback); // Makes API call
$data2 = once($callback); // Returns cached response
$data3 = once($callback); // Returns cached response
// Expensive computation
class Report
{
private $calculateStats;
public function __construct()
{
$this->calculateStats = function() {
// Heavy computation
return $this->processLargeDataset();
};
}
public function getStats()
{
return once($this->calculateStats);
}
}
$report = new Report();
$stats = $report->getStats(); // Computes
$stats = $report->getStats(); // Cached
Important:
- The callback must be the same instance to benefit from caching
- Different callback instances (even with identical code) are treated separately
- Cache persists only for the current request
optional()
optional($value, ?callable $callback = null)
What it does: Safely access properties and methods on a potentially null value without causing errors. Returns a null-safe object that allows chaining.
When to use:
- Accessing nested properties that might be null
- Preventing "Attempt to read property on null" errors
- Cleaner code without excessive null checks
- Working with optional relationships or API responses
Example:
// Basic usage - no error even if $user is null
$name = optional($user)->name;
$email = optional($user)->profile->email;
// Without optional (verbose)
$name = $user !== null ? $user->name : null;
$email = $user !== null && $user->profile !== null ? $user->profile->email : null;
// With optional (clean)
$name = optional($user)->name;
$email = optional($user)->profile->email;
// Real-world: fetching user from database
$user = User::find($id); // Might return null
// Safe access
$userName = optional($user)->name;
$userCity = optional($user)->address->city;
$userEmail = optional($user)->getEmail();
// All work without errors, even if $user is null
// With callback transformation
$user = User::find($id);
$displayName = optional($user, function($u) {
return strtoupper($u->firstName . ' ' . $u->lastName);
});
// If $user is null, $displayName is the null object
// If $user exists, $displayName is the transformed string
// In views - prevent errors from missing data
<h1><?= optional($post->author)->name ?></h1>
<p><?= optional($order->customer->address)->city ?></p>
// In controllers
public function show($id)
{
$user = User::find($id);
return response()->json([
'name' => optional($user)->name,
'email' => optional($user)->profile->email,
'city' => optional($user)->address->city,
]);
}
// Chaining with method calls
$user = User::find($id);
// No error even if getProfile() returns null
$email = optional($user->getProfile())->email;
$phone = optional($user->getProfile())->getContactInfo()->phone;
Important:
- The null object converts to empty string when cast:
(string)optional(null)->property === '' - For actual null checks, use
is_null()or strict comparison - Works with properties, methods, and deep chaining
- Callback is only executed if value is not null