For me, one of the promises of PSGI and Plack is to get away from programming with global variables in Perl, particularly being able to modify the request object after it’s been created. Long ago, CGI.pm replaced using a global <pre>%FORM</pre> hash with a “query object”, but it essentially has been used as always-accessible read/write data structure.
It would appear at first look that improving this might a fundamental part of the new system: A lexical environment hashref is explicitly passed around, as seen here:
vs the old implicit grab from the environment:
You might hope to be escaping some of th action-at-a-distance badness of global variables, like having a change to environment alter your object after the object is created. You might like avoid having changes made to your object avoid changing the global environment as well.
Not only does Plack::Request require an explicit environment hash to be passed in, both nearly all methods are read-only, including a param() method inspired by a read/write method from CGI.pm of the same name. That’s all good.
This would all seem to speak to safety and simplicity for using Plack::Request, but the reality turns out to be far muddier than you might hope. I encourage you to down and run this short, safe interactive perl script which illustrates some differences. It shows that:
- Plack::Request objects can be altered after they are created by changing the external environment.
- Modifying a Plack::Request object can potentially alter the external environment hash (Something which CGI.pm explicitly does not allow).
In effect, the situation with global variables is in some regards worse. Plack::Request provides the impression that there is a move away from action-at-distance programming, but the fundamental properties of being affected by global changes and locally creating them are still present.
On the topic of surprising read/write behavior in Plack::Request, you may also
interested to note the behavior of
parameters() is not consistent in this regard. I submitted tests and
suggestion to clarify
although that contribution has not yet been accepted.
Here’s the deal: The hashref returned by
parameters() are all read/write – subsequent calls to
the same method return the modified hashref.
However, modifying the hashes returned by
query\_paremeters() does not modify the hashref returned by
claims to be a merger of the two.
It seems that either all the return values should be read-only, ( always
returning the same values ), or if modifying them is supported then the
parameters() hash should be updated when either of the
query\_parameters() hashes are updated.
An incoming HTTP request to your server is by it’s nature read-only. It’s analogous to a paper letter being delivered to you be postal mail.
It’s a perfect application for an immutable object object design that Yuval Kogman eloquently advocates for. Plack::Request comes close to implementing the idea with mostly read-only accessors, but falls short. The gap it leave unfortunately carries forward some possibilities for action-at-distance cases that have been been sources of bugs in the past. I’d like to see Plack::Request, or some alternative to it, with the holes plugged: It should copy the input, not modify it by reference, and parameter related methods should also return copies rather than reference to internal data structures.