Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108689 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 49162 invoked from network); 19 Feb 2020 22:05:53 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 19 Feb 2020 22:05:53 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 397101804F4 for ; Wed, 19 Feb 2020 12:21:52 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS29169 217.70.176.0/20 X-Spam-Virus: No X-Envelope-From: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 19 Feb 2020 12:21:50 -0800 (PST) X-Originating-IP: 107.223.28.39 Received: from samurai.attlocal.net (107-223-28-39.lightspeed.nsvltn.sbcglobal.net [107.223.28.39]) (Authenticated sender: pmjones@pmjones.io) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id D284E40005; Wed, 19 Feb 2020 20:21:48 +0000 (UTC) Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 13.0 \(3608.60.0.2.5\)) In-Reply-To: Date: Wed, 19 Feb 2020 14:21:46 -0600 Cc: php internals Content-Transfer-Encoding: quoted-printable Message-ID: <1BF3D29A-04C6-4A82-9216-EAE991A38916@pmjones.io> References: <50BD013E-CF72-414C-BBC0-A7A2E45CBDDB@pmjones.io> <5904137.fSVIMsojiJ@mcmic-probook> <3DDBFBA4-8D3A-46C5-9A10-B093A5E2386B@pmjones.io> <54493258-B52E-442A-A11D-82E1D4C7DE5E@gmail.com> <8264F6CF-AD55-41E2-9F5A-DAE8942E2B79@pmjones.io> To: Rowan Tommins X-Mailer: Apple Mail (2.3608.60.0.2.5) Subject: Re: [PHP-DEV] RFC: Server-Side Request and Response Objects (v2) From: pmjones@pmjones.io ("Paul M. Jones") Hi Rowan, Again, thanks for your time and effort in evaluating this proposal. > On Feb 18, 2020, at 14:21, Rowan Tommins = wrote: >=20 > - What's the sales pitch - what do YOU think is great about these = classes? >=20 > ... >=20 > Sell it to me! :) I don't do sales; occasionally I can present a good narrative. Maybe the = narrative below will help. > - When working out the details, what code should we be picturing using = the new classes? Not to be flippant, but: request-reading and response-writing code? >> ...a built-in OO-ish request/response object set... >=20 > "OO-ish" is a wise choice of words here. One of the reasons I'm not = terribly keen on this proposal - particularly the request half - is that = I'm a bit of an OO purist. Heh -- yes, I am aware of the impurities (both perceived and actual) of = ServerRequest. ;-) However ... > The lack of behaviour also makes it less useful to people writing = their own request and response objects: if I have a copy of $_POST and = want to put it in my own object property, why would I first pass it to a = ServerRequest object, and then get it straight back out again, if the = object isn't helping me do anything with that data? ... in this case, ServerRequest does quite a bit that is easy to take = for granted, since it does so quickly and quietly. Among other things: 1. It "bundles" several related pieces of data; not just $_POST but = $_GET, $_COOKIES, etc., so they can be carried around together. 2. It separates that data from global state. 3. It parses some of that data into commonly-needed structures (eg. the = `$accept*` arrays). 4. Its copies of that data are read-only and immutable, so that once set = they cannot be modified at a distance when shared around; this keeps = them stable throughout the system. So while it is true that ServerRequest "lacks behavior" in the sense = that it doesn't "do" anything *after* construction, but it also true = that it does quite a bit *at* construction. Indeed, the vast majority of uses (in practice) for a ServerRequest type = of object are "reading" or "getter" uses. There is comparatively little = need to write to a ServerRequest instance. Almost all of the time, all = you need is to read from it post-construction. Further, there's no need to optimize for calculation-on-demand, since = the calculations are so speedy in the first place. And since it will = happen only once in the request lifespan, you might as well build all = the properties at construction-time. At that point, getter-type methods = are just returning what has already been calculated. And at *that* = point, read-only properties do the trick quite nicely. For the comparatively-rare (but still necessary) times when you need to = write to a ServerRequest type of object, it is open to extension in = userland for those application-specific cases. >> ...that does pretty much just what PHP itself already does... >=20 > As you say elsewhere, this is useful for helping people migrate to it. = The flipside of that is that it ties us into past decisions, rather than = evaluating whether those decisions are still what we want. Your comment jogged my memory of an older conversation, = , in which you figure prominently, = and in which I pointed out the existence of the prior version of this = RFC. Many things there are reminiscent here: - Should superglobals be made read-only? - Should superglobals be made into objects instead of arrays? - Should superglobals be replaced entirely with some new system? - Are additional superglobals needed? - Should there be a layer of indirection between superglobals and their = use in applications? This RFC answers from (if you'll pardon the word) a conservative = position: no, no, no, maybe, and yes. In the last case, this RFC can be = construed to provide ServerRequest as that layer of indirection for some = of the superglobals. > The approach in providing both $files and $uploads arrays is a good = compromise - provide the new better way, but also an easy-to-migrate = way. I'd love to see them named more distinctly, though, maybe even = calling one of them "legacy". I'd probably also make them methods so = that the data can be stored once (in the new format) and re-formatted on = demand (again, objects as behaviour rather than data). I appreciate that concession on your part, and thanks for saying. I think $uploads is a pretty distinct name, while being clearly related = to files. And, as I mentioned earlier, the cost of reformatting the = $_FILES structure to $uploads is so low that it might as well occur at = construction time, instead of adding a method to calculate-and-retain = the reformatted structure. >> ...easing their transition away from global state >=20 > This I find less convincing. To quote from further down the e-mail, I = wrote: >=20 >>> ...you can do almost as much in that direction as the RFC by writing = "$get=3D$_GET; unset($_GET);" The hard problem is that the entry point = for a request is in global scope, not a main() or handleRequest() = function. >=20 > and you replied: >=20 >> Another "hard" problem is carrying those values around in the system = once they are decoupled from global state; the objects in this RFC = purport to do so. >=20 > If all the object does is "carry around" a copy of the superglobal = arrays, I can write it in about a dozen lines of code. I answered this above, but to reiterate: "carrying around" is *one* = thing ServerRequest does, but not *all* it does. > Again, the response object is more powerful in this regard, because we = can't currently pass around a list of prepared cookies and trivially = output it to the client. >=20 > Along the lines of my previous message about decoupling concerns, I = would personally like the response parts of the proposal split out and = re-cast as a kind of "response buffer". I admit I considered this. However, it makes more sense to me in terms = of symmetry/complementarity, and in terms of "what we need on a daily = basis", to provide both the request object and the response object = together. >> I had considered mentioning the dual OO+procedural APIs of mysqli, = date, etc. but it seemed too much in an already-long RFC; I'm glad you = brought it up, as I did have it in mind. >=20 > This is an interesting comparison to consider. Just as those = extensions have procedural options for people who can't stand objects, = the current proposal has objects for people who like "OO-ish". Yes, I think so too. >>> Introducing these objects as part of a new calling convention for = PHP scripts would definitely add value, and make them a true replacement = for the superglobals, but that would be a very different RFC. >>=20 >> That does seem rather ambitious. If you feel that's a valuable thing = to add to PHP, perhaps it could be part of a future RFC, maybe even one = that uses the objects in this RFC as a starting point. >=20 > One of the things I don't like about the current proposal is that it's = modelled so closely around the current superglobals that I fear it would = actually be *harder* to replace them. Maybe? I can similarly imagine that if new-and-different superglobals = appear, the ServerRequest object can evolve to contain and/or translate = between them. > Parsing a request body from an arbitrary source into arrays that match = the structure of $_POST and $_FILES would be a really useful feature. Yes, although one can do at least the $_POST portion with ServerRequest = as it is now. For example, a naive userland factory might do this: class ServerRequestFactory { public function new(array $globals, ?string $content =3D null) : = ServerRequest { if ($this->isJson($globals['_SERVER']['CONTENT_TYPE'] ?? '') = { $globals['_POST'] =3D json_decode( $content ?? file_get_contents('php://input'), true ); } return new ServerRequest($globals, $content); } protected function isJson(string $contentType) : bool { return $contentType =3D=3D=3D 'application/json' || substr($contentType, -5) =3D=3D=3D '+json'; } } Call `$request =3D (new ServerRequestFactory())->new();` and voila: the = equivalent of `$_POST`, populated from JSON content, stored as = $request->input. > I also really want to see the day when I never have to interact with = $_SERVER again. Other than renaming it, this object's design makes that = less likely, not more. >=20 > Imagine if in future we're able to redesign the internals so that = there is a dedicated and reliable field for the requested URL; the = current object doesn't have anywhere we can put that. If we add it = later, it will be too late, code will be written that passes around = ServerRequest objects, but relies on the full array of CGI variables to = reconstruct the URL. >=20 > If this object took a more opinionated view of what behaviour to = encapsulate, we could simply hide the "server" array completely. Common = use cases would be exposed via methods, and rarer use cases would have = to be added in userland with their own copy of $_SERVER. Then my dream = of deprecating $_SERVER could mean something other than moving it into = an object. Yes, the internals thread I pointed to earlier makes that clear. And = though I understand where you're coming from, especially regarding "a = dedicated and reliable field for the requested URL", this RFC does not = have that kind of ambition, nor do I expect it to in the future. This proposal is satisfied to take only a smaller series of steps to = ease a more limited set of problems, rather than revise major and = wide-ranging elements of PHP overall. I presume you will find that = unsatisfactory, but, well, there it is. --=20 Paul M. Jones pmjones@pmjones.io http://paul-m-jones.com Modernizing Legacy Applications in PHP https://leanpub.com/mlaphp Solving the N+1 Problem in PHP https://leanpub.com/sn1php