Added by Michael Sheakoski, last edited by Karol Babioch on Mar 04, 2008  (view change)

Labels

 
(None)

Zend Framework: Zend_Http_Request Component Proposal

Proposed Component Name Zend_Http_Request
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Http_Request
Proposers Michael Sheakoski
Revision 1.1 - 1 August 2006: Updated from community comments. (wiki revision: 36)

Table of Contents

1. Overview

  • Zend_Http_Request is a container used for accessing GET, POST, COOKIE, PATH_INFO, and various input from the browser. It uses error correction and fallback methods to emulate certain variables when they are not set or not in the proper format.

2. References

http://msdn2.microsoft.com/en-us/library/system.web.httprequest.aspx
http://java.sun.com/javaee/5/docs/api/javax/servlet/http/HttpServletRequest.html

3. Component Requirements, Constraints, and Acceptance Criteria

4. Dependencies on Other Framework Components

5. Theory of Operation

The component can be instantiated anywhere in the script. The "get" accessor methods will automatically attempt to gather the requested data. If not successful, null is returned. Some methods include a "set" accessor as well in case the matching "get" accessor needs to be overridden.

6. Milestones / Tasks

7. Class Index

  • Zend_Http_Request

8. Use Cases

9. Class Skeletons

For the most part it's a nice evolution of the idea from the initial discussions, but I'm not quite on board with treating the pathInfo, basePath, etc properties the same as the get/post/request params. I'd rather call the methods by themselves - conceptually the values they provide are quite separate values than you would retrieve via the get/post variables (e.g. BasePath may need to be calculated rather than simply passed through).

FWIW, I'd rather access the $_REQUEST properties via the __get and __set methods, or retrieve the $_GET/$_POST properties more explicitly by a get('post') or get('get') method (there was a slightly confusing method called getGet in the last proposal that was the right method with the wrong name Because $_REQUEST is an amalgam of $_GET and $_POST I think this would be more natural and offer a better degree of simplicity.

But apart from that, I'm all for it!

Cheers Simon!

If I understand correctly, you are thinking that $request->basePath will simply return an uncalculated value? This is not the case because it will actually attempt to figure out the basePath if $_basePath is null or has not been set by the user. I can add support for the $_REQUEST superglobal, that is an easy fix. As far as accessor methods for get/post/request/cookie, is there any benefit to using accessors on them? They are all simple arrays and an accesssor would seem like an unnecessary layer of bloat to access them while providing no benefits.

I forgot to add, you can call both the accessor methods as well as _set/_get for requestUri, pathInfo, baseUrl, basePath.

$request->pathInfo is the same as $request->getPathInfo()

No, I understand that $request->pathInfo maps directly to $request->getPathInfo() but I don't believe it should. Because these kind of properties are calculated values, they should only be accessed via the methods. Everything else, though, is fine.

E.g. If $_GET['id'] = 3 and $_GET['foo'] = 'bar' then I should be able to access them via $request->id or $request->get('get')>id (and similarly for $_POST variables). This would also apply to args from a CLI environment - myscript --foo=bar => $request>foo

The big advantage of checking them like this is that $request->notyetset would not throw an E_NOTICE (because the __get would check via __isset). I would say that providing access via both the methods and the __get properties for PathInfo (etc..) would be bloat because it's unnecessary duplication.

I mainly added the __get/__set methods for consistency so all of the members could be accessed in the same fashion. It is not a big deal to add a get() function though, I'll add it. Here is how I ended up using the class which should explain why I added the __set/__get in the first place:

I wanted the code to look as readable as possible in the views. I understand what you mean about calculated vs uncalculated but there is a fine line in this case because $request->pathInfo = $_SERVER['PATH_INFO'], $request->requestUri = $_SERVER['REQUEST_URI'] which are technically uncalculated values... I simply put a few checks in with the get/set functions to make sure that data is there and in the proper format.

As far as the E_NOTICE, $request->someRandomVarNotSet will ALWAYS return null regardless of _get/set/_isset.

View the rest of this thread. Most recent comment: Sep 04, 2006
6 more comments by: Simon Mundy, Michael Sheakoski

This proposal should be reviewed in parallel with the other Zend_Http_Request proposal.

  • reviewing together increases productivity
  • ideas can be combined or evolved together
  • avoids redoing work, if one proposal is accepted, and the other spurs changes to the first
Posted by Gavin at Sep 05, 2006 20:18

Agreed. I didn't mean to cause any confusion by making a seperate proposal. The other proposal differs from mine because it was originally a router and now heading towards a combination of a dispatch token + minimal HTTP Response class. Mine is a pure HTTP Response class designed to access just about every form of input that the browser sends.

Oops I meant HTTP Request object

Mehtods for resolving base URL, path etc. are indeed very welcome addition, however I don't really see why to have plain wrappers around variables like _GET and _POST. It's not likely they are going anywhere, and since you don't do anything but return them why the user won't just use _GET/_POST and save the function call?

Mostly to make the class consistent with the Request object of other frameworks/languages which access all input from the browser. It wouldn't really make sense to have an object like Http_Request that only supports a subset of the information. In addition, by going through the functions as opposed to directly accessing the GET/POST superglobals you can do things like include them in your templates without worrying about generating an E_NOTICE for undefined indexes as well as filtering or error checking as demonstrated in the getServer() function.

I think it makes a lot of sense for an object to give the information that is not available elsewhere, but do not attempt to copy system variables which can be accessed directly. I am also not sure that using data transformations in get functions is good thing especially when you already have separate functions for the same data. This leads to very interesting effects like $a = getServer(); $a['PATH_INFO'] not being the same as getServer('PATH_INFO').
for it.
As for notices, I'm not sure adding 2 function calls for each variable access (and function calls are not cheap in PHP at all) is worth silencing warning, especially when you have perfectly good @ for it.

I think we should use function which are really very usable - the second half of the class, the path/url resolution function - but let alone the getters for superglobals, there's really not much need to create methods in class just to avoid a notice.

All good points. I guess I just feel funny having the class called Zend_Request_Http which sounds like it should give me access to everything but only supporting a subset of that. Perhaps I can find the happy medium and condense the getQuery/Path/Server/Env/etc... into a single function. No matter what goes on in here Zend will of course be the final decision maker. I assume they will take a combination of features from both of the proposals.

Michael and I have gone around and around on this. I believe that the Http Request should focus on GET/POST and maybe COOKIE. I proposed following the Zend_Hash interface to provide a standard interface for the current incoming data (either GET or POST auto-selected by method). This is the simplest interface and I believe in the ZF style.

I can see some Cookie access for convenience, but I would prefer to see a separate Http_Cookie class that had full support for writing cookies. Likewise I think SERVER and ENV should be in a Server_Info or System_Info class.

I agree that by itself it doesn't add any value, but what if we are extending Zend_Http_Request to accept (for example) a filter? Then it makes plenty of sense to use the $request object to retrieve the value rather than accessing the superglobal.

I think Michael has good reason to implement the _GET/_POST accessors - avoiding notices aside, it encourages developers to use the ZF components over more procedural-style code and it allows us a good base for adding useful features over time.

My only source of confusion at the moment is the way to access these. So far I've seen 5 or 6 different implementations with weird and wonderful ways of accessing request/get/post data. Is this the final proposal or should I be looking at http://framework.zend.com/wiki/display/ZFPROP/Changes+to+Zend+MVC+-+Michael+Sheakoski+and+Christopher+Thompson

My ultimate preference is to access request parameters as public properties. These are set in order of hierarchy: _POST then _GET then path parameters. In 9/10 cases this will be all I need. I don't want write access, only read (because if I change a parameter what does it map back to?). If I need to determine if a parameter exists from a specific source, then getPost() / getQuery() / getParam() will do just fine.

getRequest is redundant, as it (in effect) provides slightly less information as you're gathering here (it contains _GET/_POST/_COOKIE but not path parameters). I think it should go.

I agree with Stan that the getServer() method should return only an unaltered value from the array - if I wish to calculate the REQUEST_URI I'll use that method instead.

You've also got the convenience of having the $_SERVER values within the request object already, so the calls to $_SERVER from setRequestUri and setBaseUrl could instead reference a protected property.

E.g.

Lastly - and this may be more of a personal preference - but I would see the initial setup of methods like setPathInfo() happen in the constructor. Currently these methods calculate default values only if you pass a null value to the method. I would think that those could be created upon instantiation and available immediately so that you don't need to call an empty method in your app - the value is just there. If you wish to explicitly overwrite that value then do so, but otherwise it seems like an unnecessary call.

I don't wish to seem overly picky - these are only my personal preferences and can of course be completely ignored - but I would like to see as much refinement of this component as possible as it is so critical to the framework.

View the rest of this thread. Most recent comment: Sep 12, 2006
3 more comments by: Michael Sheakoski, Christopher Thompson