Params...Request(3pm)

πŸ“¦
version 1.20 Β· 5 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

πŸŽ€ Params::CallbackRequest πŸŽ€

hey bestie πŸ’– this is the manual page for Params::CallbackRequest, straight outta the User Contributed Perl Documentation (you know, the (3pm) crew ✨). it's a long one so buckle up, we're doing this in parts 🫢


✨ NAME ✨

Params::CallbackRequest β€” Functional and object-oriented callback architecture πŸ’…


✨ SYNOPSIS ✨

so first flavor: functional parameter-triggered callbacks πŸ“

use strict;
use Params::CallbackRequest;

# Create a callback function.
sub calc_time {
    my $cb = shift;
    my $params = $cb->params;
    my $val = $cb->value;
    $params->{my_time} = localtime($val || time);
}

# Set up a callback request object.
my $cb_request = Params::CallbackRequest->new(
    callbacks => [ { cb_key  => 'calc_time',
                     pkg_key => 'myCallbacker',
                     cb      => \&calc_time } ]
);

# Request callback execution.
my %params = ('myCallbacker|calc_time_cb' => 1);
$cb_request->request(\%params);

# Demonstrate the result.
print "The time is $params{my_time}\n";

orrr, if you're living that object-oriented life, in a subclass of Params::Callback:

package MyApp::Callback;
use base qw(Params::Callback);
__PACKAGE__->register_subclass( class_key => 'myCallbacker' );

# Set up a callback method.
sub calc_time : Callback {
    my $self = shift;
    my $params = $self->request_params;
    my $val = $cb->value;
    $params->{my_time} = localtime($val || time);
}

and then, in your actual application 🌈

# Load order is important here!
use MyApp::Callback;
use Params::CallbackRequest;

my $cb_request = Params::Callback->new( cb_classes => [qw(myCallbacker)] );
my %params = ('myCallbacker|calc_time_cb' => 1);
$cb_request->request(\%params);
print "The time is $params{my_time}\n";

✨ DESCRIPTION ✨

okay so here's the whole vibe πŸ‘‘

Params::CallbackRequest hands you functional and object-oriented callbacks to method and function parameters. a callback can be either:

  • a code reference you hand to the new() constructor, or
  • a method defined in a subclass of Params::Callback.

and when do they fire? either on every single call to Params::CallbackRequest's request() method, or when the parameters you pass to request() contain specially named keys πŸ‘€

the big idea behind this module is that it's basically a plugin architecture for Perl templating systems fr fr. callbacks get triggered by the contents of a request to the Perl templating server, and they happen before the templating system itself even runs. which means you get to:

  • carry out logical processing of data submitted from a form,
  • affect the contents of the request parameters before they get passed off to the templating system, and
  • even redirect or abort the whole request before the templating system touches it.

iconic, honestly πŸ’…


✨ JUSTIFICATION ✨

so like, why would you even want this? great question bestie, and there are a bunch of reasons. some that come to mind offhand:

  • Stricter separation of logic from presentation β€” some Perl templating systems actually enforce separating application logic from presentation (ngl, things like TT, HTML::Template), and others just... don't (looking at you, HTML::Mason, Apache::ASP πŸ™ˆ). and even in the well-behaved ones, application logic often gets dumped into scripts that run alongside the presentation templates and get loaded on-demand under mod_perl. but if you move that logic into Perl modules and then tell the templating system to execute that code as callbacks? you obviously get a way cleaner separation of application logic and presentation. periodt.
  • Widgitization β€” because callbacks can preprocess parameters, devs get to build easier-to-use, more dynamic widgets that work in literally any and all templating systems πŸ”₯. example: a widget that puts a bunch of related fields into a form (like a date selection widget) can have its fields preprocessed by a callback β€” say, to properly combine those fields into one unified date parameter β€” before the template that responds to the form submission ever gets the data. Params::Callback has an example solution for this exact problem, iykyk.
  • Shared Memory β€” if you run your templating system under mod_perl, callbacks are just Perl subroutines in modules loaded at server startup time. so all the memory they eat lives in the Apache parent process and gets shared by the child processes ✨. for code that runs frequently, that can be wayyy less resource-intensive than code in templates, because templates get loaded separately in each Apache child process, on demand. it's giving efficiency.
  • Performance β€” since callbacks execute before the templating architecture does much processing, they get the chance to short-circuit the whole template processing by doing something else instead. best example: redirection. often the app logic in a callback does its thing and then redirects the user to a different page, and doing that redirection in a callback eliminates a lot of extraneous processing that would otherwise happen first β€” snappier response for the user, slay πŸ’….
  • Testing β€” templating system templates are NOT easy to test through a testing framework like Test::Harness 😭. subroutines in modules, though? fully testable. which means you can write tests in your application test suite to test your callback subroutines. chef's kiss.

and if those aren't enough reasons, consider this: callbacks are just way cool. πŸ’–


✨ USAGE ✨

Params::CallbackRequest supports two different types of callbacks:

  1. ones triggered by specially named parameter keys, and
  2. ones executed for every request.

🌸 Parameter-Triggered Callbacks 🌸

parameter-triggered callbacks fire off when they see specially named parameter keys. these keys are built like this: the package name, then a pipe character (|), then the callback key with the string _cb appended, and finally an optional priority number at the end πŸ‘‘

for example β€” if you specified a callback with the callback key save and the package key world, a callback field might look like this:

my $params = { "world|save_cb" => 'Save World' };

when that $params hash gets passed to Params::CallbackRequest's request() method, the world|save_cb parameter triggers the callback associated with the save callback key in the world package ✨

but heads up: if such a callback hasn't been configured, Params::CallbackRequest will throw a Params::CallbackRequest::Exceptions::InvalidKey exception πŸ’€. here's how to configure a functional callback when you're constructing your Params::CallbackRequest object so that doesn't happen:

my $cb_request = Params::CallbackRequest->new
  ( callbacks => [ { pkg_key => 'world',
                     cb_key  => 'save',
                     cb      => \&My::World::save } ] );

with that config in place, the world|save_cb parameter key will trigger the execution of the My::World::save() subroutine during a callback request:

# Execute parameter-triggered callback.
$cb_request->request($params);

πŸ’œ Functional Callback Subroutines πŸ’œ

functional callbacks use a code reference for parameter-triggered callbacks, and Params::CallbackRequest executes them with a single argument: a Params::Callback object. so a callback subroutine will generally look something like this:

sub foo {
    my $cb = shift;
    # Do stuff.
}

that Params::Callback object gives you accessors to all the data relevant to the callback, including the callback key, the package key, and the parameter hash. it also has an abort() method. go read the Params::Callback documentation for all the goodies 🎁

ngl there's a thing you should know: Params::CallbackRequest installs an exception handler during the execution of callbacks. so if any of your callback subroutines die, Params::CallbackRequest will throw a Params::Callback::Exception::Execution exception. BUT if your callback subroutines throw their own exception objects, Params::CallbackRequest will just rethrow them as-is. don't like this setup? use the exception_handler parameter to new() to install your own exception handler πŸ’…

πŸ’œ Object-Oriented Callback Methods πŸ’œ

object-oriented callback methods β€” supported under Perl 5.6 or later ✨ β€” are defined in subclasses of Params::Callback, and they're identified by attributes in their declarations.

unlike functional callbacks, callback methods are not called with a Params::Callback object β€” they're called with an instance of the callback subclass πŸ‘€. and because those classes inherit all the goodies Params::Callback provides, you can basically use their instances exactly like you'd use the Params::Callback object in functional callback subroutines. but since they're subclasses, you can add your own methods and attributes on top 🫢. go read Params::Callback for all the gory details on subclassing (plus a few examples). generally, callback methods look like this:

sub foo : Callback {
    my $self = shift;
    # Do stuff.
}

just like with functional callback subroutines, method callbacks execute with a custom exception handler. again, check out the exception_handler parameter if you wanna install your own.

Note: under mod_perl, it's important that you use any and all Params::Callback subclasses before you use Params::CallbackRequest ⚠️. this gets around an issue with identifying the names of the callback methods in mod_perl. read the comments in the Params::Callback source code if you're curious and want to learn more πŸ‘‘

πŸ’œ The Package Key πŸ’œ

the package key is a convenience, so that a system rockin' tons of functional callbacks can use callbacks with the same keys but in different packages 😌. the idea is that the package key uniquely identifies the module each callback subroutine lives in β€” but honestly, it doesn't have to be that deep. use the package key however you want, or not at all:

my $cb_request = Params::CallbackRequest->new
  ( callbacks => [ { cb_key  => 'save',
                     cb      => \&My::World::save } ] );

but note πŸ‘€ β€” if you don't specify the package key, you'll still need to provide one in the parameter hash you pass to request(). by default, that key is DEFAULT. so such a callback parameter would look like this:

my $params = { "DEFAULT|save_cb" => 'Save World' };

not vibing with the DEFAULT package name? you can set an alternative default with the default_pkg_name parameter to new():

my $cb_request = Params::CallbackRequest->new
  ( callbacks        => [ { cb_key  => 'save',
                            cb      => \&My::World::save } ],
    default_pkg_name => 'MyPkg' );

and then, obviously, any callbacks without a specified package key of their own have to use that custom default:

my $params = { "MyPkg|save_cb" => 'Save World' };
$cb_request->request($params);

πŸ’œ The Class Key πŸ’œ

Sometimes you want your callback to feel like it's in charge of a whole class, and that's literally what the class key is for. It's basically a synonym for the package key, except it speaks the object-oriented love language way more directly. Two differences, bestie: it maps to an actual class, and β€” key detail β€” every Params::Callback subclass is required to have a class key. It is not optional the way it is with functional callbacks. Not negotiable, not vibes-based, it's a requirement. πŸ’…

You can declare it right in your subclass like this:

package MyApp::CallbackHandler;
use base qw(Params::Callback);
__PACKAGE__->register_subclass( class_key => 'MyCBHandler' );

Or you can declare it the other way, by implementing a CLASS_KEY subroutine:

package MyApp::CallbackHandler;
use base qw(Params::Callback);
__PACKAGE__->register_subclass;
use constant CLASS_KEY => 'MyCBHandler';

And if you just... don't define a class key at all? No stress. Params::Callback will fall back to using the subclass name instead. Either way, you still have to call the register_callback() method to register the subclass with Params::Callback. Full deets live in the Params::Callback documentation β€” go read it, I promise it's worth it. ✨

✨ Priority ✨

Okay so sometimes one callback is simply more important than another. Main character callback energy. Like, maybe you're relying on one callback to run first and set up variables that another callback needs.

And here's the mess: you cannot rely on the order in which callbacks execute. The parameters get passed via a hash, and β€” as we all know β€” processing a hash is unordered. So you need an actual method to guarantee your setup callback goes first.

That method is priority. Set a higher priority level on the setup callback than on the callbacks that depend on it. For functional callbacks, it looks like this:

my $cb_request = Params::CallbackRequest->new
  ( callbacks        => [ { cb_key   => 'setup',
                            priority => 3,
                            cb       => \&setup },
                          { cb_key   => 'save',
                            cb       => \&save }
                        ] );

For object-oriented callbacks, you declare the priority right in the callback method declaration, iconically:

sub setup : Callback( priority => 3 ) {
    my $self = shift;
    # ...
}

sub save : Callback {
    my $self = shift;
    # ...
}

So in these examples, setup is configured with a priority level of 3. That guarantees it always executes before save, which is just sitting there at the default priority of 5. And yes, this holds true no matter what order the fields show up in the hash:

my $params = { "DEFAULT|save_cb"  => 'Save World',
               "DEFAULT|setup_cb" => 1 };

In this config, setup always runs first because of its higher priority. Periodt. πŸ’–

Now, save got the default priority of 5, but bestie, that default is customizable too. You can change it via the default_priority parameter to new() for functional callbacks, and via the default_priority to the class declaration for object-oriented callbacks. Watch this β€” functional callback configuration:

my $cb_request = Params::CallbackRequest->new
  ( callbacks        => [ { cb_key   => 'setup',
                            priority => 3,
                            cb       => \&setup },
                          { cb_key   => 'save',
                            cb       => \&save }
                        ],
    default_priority => 2 );

Or this Params::Callback subclass declaration:

package MyApp::CallbackHandler;
use base qw(Params::Callback);
__PACKAGE__->register_subclass( class_key        => 'MyCBHandler',
                                default_priority => 2 );

Both of these will cause save to always execute before setup, because its priority level now defaults to 2. Plot twist! πŸ”₯

AND there's more β€” the priority level can also be overridden via the parameter key itself, by appending a priority level to the end of the key name. So this example:

my $params = { "DEFAULT|save_cb2" => 'Save World',
               "DEFAULT|setup_cb" => 1 };

Causes the save callback to execute before the setup callback by overriding the save callback's priority to level 2. Of course, any other parameter key that triggers the save callback without a priority override will still run save at its configured level. Consistency, we love her. 🌸

✨ Request Callbacks ✨

Request callbacks come in two flavors, and you know I have to describe them both:

  • the ones that execute before the parameter-triggered callbacks, and
  • the ones that execute after the parameter-triggered callbacks.

Functional request callbacks get specified via the pre_callbacks and post_callbacks parameters to new(), respectively, like so:

my $cb_request = Params::CallbackRequest->new
  ( pre_callbacks  => [ \&translate, \&foobarate ],
    post_callbacks => [ \&escape, \&negate ] );

Object-oriented request callbacks get declared via the PreCallback and PostCallback method attributes, like so:

sub translate : PreCallback { ... }
sub foobarate : PreCallback { ... }
sub escape : PostCallback { ... }
sub negate : PostCallback { ... }

In these examples, translate() and foobarate() will execute β€” in that order, bestie, order matters β€” before any parameter-triggered callbacks run. (None will in these examples, since none are specified. Clean slate energy. ✨)

Conversely, escape() and negate() will execute (again, in that order) after all the parameter-triggered callbacks have executed. And literally regardless of which parameter-triggered callbacks get triggered, the request callbacks always execute for every request β€” unless an exception is thrown by an earlier callback, in which case all bets are off. 😭

Even though they might be used for totally different purposes, the pre_callbacks and post_callbacks functional callback code references expect the same argument as parameter-triggered functional callbacks do: a Params::Callback object.

sub foo {
    my $cb = shift;
    # Do your business here.
}

Similarly, object-oriented request callback methods get passed an object of the class defined in the class key portion of the callback trigger β€” either an object of the class in which the callback is defined, or an object of a subclass:

sub foo : PostCallback {
    my $self = shift;
    # ...
}

And naturally, the attributes of the Params::Callback or subclass object will be different than they are in parameter-triggered callbacks. For example, the priority, pkg_key, and cb_key attributes will be undefined. It's giving "no thoughts, head empty." πŸ™ˆ

It will, however, be the same instance of the object passed to all other functional callbacks β€” or to all other class callbacks with the same class key β€” within a single request. Same object, one request, we are consistent. 🫢

And just like parameter-triggered callbacks, request callbacks run right under the nose of a custom exception handler, so if any of them dies, an Params::Callback::Exception::Execution exception gets thrown. If that's not your vibe, use the exception_handler parameter to new() and customize away. πŸ’œ

πŸ’– INTERFACE

✨ Parameters To The new() Constructor ✨

So Params::CallbackRequest has a whole lil squad of its own parameters for the new() constructor (and ngl, none of them trigger callbacks, tragic, I know 😭). Here's the whole lineup:

  • callbacks β€” this is where you set up your parameter-triggered functional callbacks, bestie πŸŽ€. It's an array reference of hash references, and every single hash ref is one whole callback. The keys you can put in each of those callback spec hashes are:
    • cb_key β€” Required πŸ”₯. A string that, when it shows up in a properly-formatted parameter hash key, triggers that callback to run. That's the whole vibe check.
    • cb β€” Required πŸ”₯. A reference to the Perl subroutine that gets executed once cb_key has been spotted in a parameter hash handed to request(). Every code reference should expect exactly one argument: a Params::Callback object. And important β€” the same instance of a Params::Callback object gets used for all the functional callbacks in a single call to request(). Same one, every time, no switching up πŸ‘‘.
    • pkg_key β€” optional, no pressure πŸ’…. A key that uniquely IDs the package where the callback subroutine lives. Super useful in systems with a million-and-one callbacks, where devs might wanna reuse the same cb_key for different subroutines in different packages. The default package key can be set with the default_pkg_key parameter to new().
    • priority β€” optional, but she matters πŸ’œ. The level of priority of a callback. Some callbacks are just more important than others and need to go first, periodt. Params::CallbackRequest supports priority levels all the way from 0 (highest priority, main character πŸ‘‘) down to 9 (lowest priority). The default priority for functional callbacks can be set with the default_priority parameter.
  • pre_callbacks β€” this one takes an array reference of code references that should run on every single call to request(), before any of the parameter-triggered callbacks do their thing 🌸. They run in the exact order they're listed in the array reference, no reordering, no chaos. Each code reference should expect a Params::Callback object as its one and only argument. And yes β€” that same instance of a Params::Callback object is shared across all functional callbacks in a single call to request(). Reach for these pre-parameter-triggered request callbacks when you wanna do something with the params submitted for every call to request(), like converting character sets ✨.
  • post_callbacks β€” same energy, other end of the timeline πŸ¦‹. This takes an array reference of code references that run on every call to request() after all the parameter-triggered callbacks have been called. Order listed = order executed, always. Each code reference should expect a Params::Callback object as its sole argument, and again, the same Params::Callback instance is used for all functional callbacks in a single call to request(). Use these post-parameter-triggered request callbacks when you wanna do something with the params submitted for every call to request(), like encoding or escaping their values for presentation πŸ’….
  • cb_classes β€” an array reference listing the class keys of all the Params::Callback subclasses that hold callback methods you want included in your Params::CallbackRequest object πŸ“‹. OR β€” plot twist β€” cb_classes can just be the word ALL, in which case every Params::Callback subclass gets its callback methods registered with your object. Iconic, honestly. Head to the Params::Callback documentation for the deets on creating callback classes and methods.
Note: in a mod_perl environment, make sure you use Params::CallbackRequest only after you've used all the Params::Callback subclasses you need β€” otherwise you literally won't be able to use their callback methods πŸ™ˆ.
  • default_priority β€” the priority level at which functional callbacks get executed. Does not apply to object-oriented callbacks, ok? This value gets used in every hash reference passed via the callbacks parameter to new() that's missing a priority key. You can pick anything in the range of 0 (highest priority) to 9 (lowest priority). If you don't specify it, it defaults to 5 πŸŽ€.
  • default_pkg_key β€” the default package key for functional callbacks. Also does not apply to object-oriented callbacks. This value gets used in each hash reference passed via the callbacks parameter to new() that's missing a pkg_key key. It can be any string that evaluates to a true value, and it defaults to DEFAULT if you don't specify it ✨.
  • ignore_nulls β€” by default, Params::CallbackRequest runs all callbacks triggered by parameter hash keys. But sometimes you lowkey wanna skip any callbacks that have no value for the callback field. You could just check $cbh->value inside the callback itself, sure β€” but if you need to shut down execution of all parameter-triggered callbacks whenever the callback parameter value is undefined or the null string (''), pass the ignore_null parameter with a true value. It's set to a false value by default. πŸ’€
  • leave_notes β€” by default, Params::CallbackRequest clears out the contents of the hash you get at via the notes() method right before it returns from a call to request(). But sometimes you want the notes hash to persist past the end of a call to request() 🫢. Say a templating architecture wants to keep the notes around for the whole duration of a template request. In that case, pass a true value to leave_notes, and then use the clear_notes() method to manually clear out the notes hash whenever the right moment comes.
  • exception_handler β€” Params::CallbackRequest installs a custom exception handler while callbacks are executing. This custom handler just rethrows any exception objects it runs into, but it throws a Params::Callback::Exception::Execution exception object if it's handed nothing but a string value (the kind of thing you get from die "fool!"). But if you're out here throwing your own exceptions in your callbacks and wanna handle them your own way, pass the exception_handler parameter a code reference and do your thing πŸ’….

🌈 Instance Methods 🌈

Params::CallbackRequest obviously has several instance methods. Covering the most important ones first, bestie πŸ’–.

πŸ’« request

$cb_request->request(\%params);

# If you're in a mod_perl environment, pass in an Apache request object
# to be passed to the Callback classes.
$cb_request->request(\%params, apache_req => $r);

# Or pass in argument to be passed to callback class constructors.
$cb_request->request(\%params, @args);

This executes the callbacks you specified when the Params::CallbackRequest object was created ✨. It takes one required argument: a hash reference of parameters. Any arguments after that get passed along to the constructor for each callback class whose callbacks are about to be executed. By default, the only extra parameter the Params::Callback base class supports is an Apache request object, which you pass via the apache_req parameter. It returns the Params::CallbackRequest object on success β€” or the code you passed to Params::Callback's abort() method if callback execution was aborted πŸ‘‘

A single call to request() is called a "callback request" (naturally πŸ’…). The order goes like this: first, all pre-request callbacks run. Then, any parameter-triggered callbacks triggered by the keys in the parameter hash reference (the sole argument) run. And finally, all post-request callbacks run. request() returns the Params::CallbackRequest object on successful completion of the request πŸŽ€.

Any callback that calls abort() on its Params::Callback object prevents any other callbacks scheduled by the request from running after it finishes β€” post-request callbacks included πŸ›‘. And on top of that, any callback that dies or throws an exception will obviously also stop any subsequent callbacks from executing, and it also has to be caught by the caller or the entire process terminates πŸ’€:

eval { $cb_request->request(\%params) };
if (my $err = $@) {
    # Handle exception.
}

πŸ’« notes

$cb_request->notes($key => $value);
my $val = $cb_request->notes($key);
my $notes = $cb_request->notes;

The notes() method gives you a lil spot to stash application data, so devs have a way to share data among multiple callbacks over the course of a call to request() 🫢. Whatever you store here sticks around for the duration of the request β€” unless the leave_notes parameter to new() was passed a true value. In that case, use clear_notes() to manually clear the notes.

Conceptually, notes() holds a hash of key-value pairs. notes($key, $value) stores a new entry in that hash. notes($key) returns a value you stored earlier. And notes() with no arguments at all returns a reference to the entire hash of key-value pairs 🌸.

notes() is similar to the mod_perl method $r->pnotes(). The main differences? This notes() works in a non-mod_perl environment, and its lifetime is tied to the lifetime of the call to request() β€” unless leave_notes is true ✨.

And for convenience's sake, a shortcut to notes() is provided to callback code through the notes() method in Params::Callback πŸ’–.

πŸ’« clear_notes

$cb_request->clear_notes;

Use this one to clear out the notes hash 🧹. Most useful when the leave_notes parameter to new() has been set to a true value and you need to manage the clearing of notes yourself. This method is specifically designed for a templating environment, where it might be advantageous for the templating architecture to let the notes persist beyond the duration of a call to request() β€” like keeping them for the duration of a call to the templating architecture itself. Peep MasonX::Interp::WithCallbacks for an example of this strategy πŸ‘‘


πŸ‘‘ Accessor Methods πŸ‘‘

The properties default_priority and default_pkg_key have standard read-only accessor methods of the same name πŸ’…. Like so:

my $cb_request = Params::CallbackRequest->new;
my $default_priority = $cb_request->default_priority;
my $default_pkg_key = $cb_request->default_pkg_key;

✨ ACKNOWLEDGMENTS ✨

okay so nobody invents this stuff alone and we're giving credit where it's due 🫢

Garth Webb implemented the original callbacks in Bricolage, based on an idea he borrowed from Paul Lindner's work with Apache::ASP. My thanks to them both for planting this great idea! This implementation is however completely independent of previous implementations.


πŸ‘€ SEE ALSO πŸ‘€

  • Params::Callback β€” these objects get passed as the sole argument to all functional callbacks, and they offer access to data relevant to the callback. Params::Callback also defines the object-oriented callback interface, so its documentation is a must-read for anyone who wants to create callback classes and methods. no cap, go read it πŸ’…
  • MasonX::Interp::WithCallbacks β€” uses this module to provide a callback architecture for HTML::Mason. it's giving ecosystem 🌈

🌸 SUPPORT 🌸

This module lives in an open GitHub repository, bestie: http://github.com/theory/params-callbackrequest/. Feel free to fork and contribute! yes really, go off ✨

Please file bug reports via GitHub Issues at http://github.com/theory/params-callbackrequest/issues/, or by sending mail to bug-params-callbackrequest@rt.cpan.org. πŸ“¬

found a bug? tell the people. they wanna know. bugs do not fix themselves and frankly that's iconic of them πŸ’€

πŸ‘‘ AUTHOR πŸ‘‘

David E. Wheeler <david@justatheory.com>


Copyright 2003-2011 David E. Wheeler. Some Rights Reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

fr fr, same terms as Perl itself. read that license, it's the whole vibe. πŸ“œ

aaaand that's a wrap on the whole page, bestie πŸŽ€ you made it to the end, which is honestly main character energy. this doc shipped as part of perl v5.36.0, dated 2022-10-15, under the title Params...Request(3pm) β€” go forth and callback responsibly πŸ’œπŸ¦‹

πŸ“¦ where this page comes from