Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

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: 37)

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

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Sep 03, 2006

    <p>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).</p>

    <p>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 <ac:emoticon ac:name="smile" /> Because $_REQUEST is an amalgam of $_GET and $_POST I think this would be more natural and offer a better degree of simplicity.</p>

    <p>But apart from that, I'm all for it!</p>

    1. Sep 03, 2006

      <p>Cheers Simon!</p>

      <p>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.</p>

    2. Sep 03, 2006

      <p>I forgot to add, you can call both the accessor methods as well as _<em>set/</em>_get for requestUri, pathInfo, baseUrl, basePath.</p>

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

      1. Sep 03, 2006

        <p>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.</p>

        <p>E.g. If $_GET<ac:link><ri:page ri:content-title="'id'" /></ac:link> = 3 and $_GET<ac:link><ri:page ri:content-title="'foo'" /></ac:link> = 'bar' then I should be able to access them via $request->id or $request->get('get')<span style="text-decoration: line-through;">>id (and similarly for $_POST variables). This would also apply to args from a CLI environment - myscript --foo=bar => $request</span>>foo</p>

        <p>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.</p>

        1. Sep 03, 2006

          <p>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:</p>
          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          <script type="text/javascript" src="<?=$this->request->basePath?>/resources/scripts/common.js"></script>
          ...
          <form id="myForm" action="<?=$this->request->baseUrl?>/user/login" method="post">
          ]]></ac:plain-text-body></ac:macro>
          <p>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<ac:link><ri:page ri:content-title="'PATH_INFO'" /></ac:link>, $request->requestUri = $_SERVER<ac:link><ri:page ri:content-title="'REQUEST_URI'" /></ac:link> 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.</p>

          <p>As far as the E_NOTICE, $request->someRandomVarNotSet will ALWAYS return null regardless of _<em>get/</em><em>set/</em>_isset.</p>
          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          class MyClass
          {
          public $foo;
          public $bar;
          }

          $my = new MyClass();

          var_dump($my->foo);
          var_dump($my->bar);
          var_dump($my->notSetYet);

          ----------
          X-Powered-By: PHP/5.1.1
          Content-type: text/html

          NULL
          NULL
          NULL
          ]]></ac:plain-text-body></ac:macro>

          1. Sep 03, 2006

            <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$request = new Zend_Request_Http();

            echo $request->get->foo;
            ]]></ac:plain-text-body></ac:macro>

            <p>returns 'Notice: Trying to get property of non-object in /home/zendtest/index.php on line 6';</p>

            <p>If you're passing $_GET to the public 'get' property of Zend_Request_Http then you're not performing any isset() checking on those properties and it will throw notices</p>

            <p>Currently $request->foo will never work in your component (using the above code).</p>

            1. Sep 03, 2006

              <p>Actually further to that it also throws the same notice for array access</p>

              <p>e.g. request->get<ac:link><ri:page ri:content-title="'foo'" /></ac:link>;</p>

              <p>'Notice: Undefined index: foo in...'</p>

              1. Sep 04, 2006

                <p>That would be expected at this moment.</p>
                <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                $request = new Zend_Request_Http();
                echo $request->get->foo;
                returns 'Notice: Trying to get property of non-object in /home/zendtest/index.php on line 6';
                ]]></ac:plain-text-body></ac:macro>
                <p>I didn't implement a get object or get() method. It is simply a copy of the $_GET array.</p>

                <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                e.g. request->get['foo'];
                'Notice: Undefined index: foo in...'
                ]]></ac:plain-text-body></ac:macro>
                <p>Standard PHP behavior for an array</p>

                1. Sep 04, 2006

                  <p>Yes, I understand it's standard PHP behaviour. Coming back to my original message I was asking if you would implement a $_REQUEST via overloading so that this kind of property testing could be done more gracefully and provide a intuitive access to request variables.</p>

                  <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                  $foo = $request->foo // rather than $request->get->foo
                  ]]></ac:plain-text-body></ac:macro>

                  <p>All that would need to be done is to convert the $_REQUEST array as a Zend_Hash object (see Rob Allen's proposal) and store that - the hard work would be done already.</p>

                  1. Sep 04, 2006

                    <p>The only problem is that Zend_Request_Http is not simply a wrapper for $REQUEST, it is a "dispatch token" plus a wrapper for a subset of the $_SERVER vars which facilitate directing a PATH_INFO/GET/POST/Cookie request to the proper controller/action and for convenience also contains a copy of $_GET/$_POST/$_COOKIE/$_REQUEST. Because of this, providing direct access to $_REQUEST through __set/_get would not be a good idea since you would potentially have naming conflicts. What if you had a var called $_REQUEST['get'], $_REQUEST['post'], $_REQUEST['cookie'], $_REQUEST['pathInfo'], etc... If you did $request->post would that mean the $_REQUEST<ac:link><ri:page ri:content-title="'post'" /></ac:link> var or would it mean $_POST?</p>

                    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                    $request = new Zend_Request_Http();
                    echo $request->get['myvar'];
                    echo $request->request['foo'];
                    ]]></ac:plain-text-body></ac:macro>

                    1. Sep 04, 2006

                      <p>I'd suggested to Christopher Thompson that you could use SPL's ArrayAccess for that:-</p>

                      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[ http://www.test.com/news/view/id/3/month/8?search=sometext

                      // Vars parse by Path first, then $_GET, $_POST for $_REQUEST

                      echo $request->id // 3
                      echo $request['GET']->search // sometext
                      echo $request['REQUEST']->month // 8
                      echo $request['COOKIE']->preference // ... etc ...
                      ]]></ac:plain-text-body></ac:macro>

                      <p>This way you can provide namespaces for the superglobals and still allow a more consistent access to these properties.</p>

                      <p>As for the pathInfo, baseUrl I still think they should remain as methods only or you could use this namespacing to have</p>

                      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                      echo $request['HTTP']->baseUrl
                      ]]></ac:plain-text-body></ac:macro>

  2. Sep 05, 2006

    <p>This proposal should be reviewed in parallel with the other Zend_Http_Request proposal.</p>
    <ul>
    <li>reviewing together increases productivity</li>
    <li>ideas can be combined or evolved together</li>
    <li>avoids redoing work, if one proposal is accepted, and the other spurs changes to the first</li>
    </ul>

    1. Sep 05, 2006

      <p>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.</p>

      1. Sep 05, 2006

        <p>Oops I meant HTTP Request object <ac:emoticon ac:name="smile" /></p>

  3. Sep 11, 2006

    <p>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?</p>

    1. Sep 11, 2006

      <p>Mostly to make the class consistent with the Request object of other frameworks/languages which access <strong>all</strong> 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.</p>

      1. Sep 11, 2006

        <p>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<ac:link><ri:page ri:content-title="'PATH_INFO'" /></ac:link> not being the same as getServer('PATH_INFO').<br />
        for it. <br />
        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.</p>

        <p>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. </p>

        1. Sep 12, 2006

          <p>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.</p>

        2. Sep 12, 2006

          <p>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. </p>

          <p>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. </p>

        3. Sep 12, 2006

          <p>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.</p>

          <p>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.</p>

          <p>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 <a class="external-link" href="http://framework.zend.com/wiki/display/ZFPROP/Changes+to+Zend+MVC+-+Michael+Sheakoski+and+Christopher+Thompson">http://framework.zend.com/wiki/display/ZFPROP/Changes+to+Zend+MVC+-+Michael+Sheakoski+and+Christopher+Thompson</a></p>

          <p>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.</p>

          <p>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.</p>

          <p>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.</p>

          <p>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.</p>

          <p>E.g.</p>
          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          class Zend_Http_Request implements Zend_Http_Request_Interface
          {
          protected $_requestUri;
          protected $_baseUrl;
          protected $_basePath;
          protected $_pathInfo;
          protected $_request = array('get' => array(),
          'post' => array(),
          'cookie' => array(),
          'server' => array(),
          'env' => array());

          public function __construct()

          Unknown macro: { $this->_request['get'] = $_GET; ...etc... }

          }
          ]]></ac:plain-text-body></ac:macro>

          <p>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.</p>

          <p>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.</p>

          1. Sep 12, 2006

            <p>I agree with you. The proposal I did which is the second Class Skeleton here (<a class="external-link" href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Http_Request+-+Sylvain+Philip">http://framework.zend.com/wiki/display/ZFPROP/Zend_Http_Request+-+Sylvain+Philip</a>) differs from Michaels in a couple of respects. It puts the current request into properties, it adds the Dispatcher_Token info, and it does not contain SERVER or ENV accessor. I provided accessors for each type for completeness, but all you really need additional is access to $_GET when the method is POST, because where the method is GET there are no POST vars. I also made the constructor similer to the InputFilter class. </p>

            <p>The PathInfo and BaseURL methods need to be accessed by Intercepting Filters such as the Router so that is why they are methods.</p>

          2. Sep 12, 2006

            <p>Cheers Simon! This is not the final proposal. Gavin added this one to the "under review" section so both implementations could be compared and contrasted.</p>

            <p>I was toying with the idea in my head about being able to turn a filter on and off. Something like $request->filter(HtmlEntities, true/false) and filter('stripSlashes', true/false) which would automatically apply to methods such as __get, getQuery/Post('theVar'). Just a rough idea, not a specific implementation yet but I think it is definitely something that would be useful.</p>

            <p>As far as writing variables, you are correct that this class should be as read-only as possible. In other popular languages/frameworks the Http_Response object is used for sending data back to the browser.</p>

            <p>Stanislav, Christopher, and yourself all make very good points and I will definitely be redoing the items you mentioned. getRequest() can be removed due to its limited usefulness. I still think that getServer() and getEnv() would be useful to access <strong>unaltered</strong> data related to the request via a consistent interface with the added bonus of no E_NOTICE.</p>

            <p>As far as using __get() overloading I think it should be consistent with the order of precedence .NET uses which is 1.GET 2.POST 3.COOKIES 4.SERVER. <a class="external-link" href="http://msdn2.microsoft.com/en-us/library/system.web.httprequest.item.aspx">http://msdn2.microsoft.com/en-us/library/system.web.httprequest.item.aspx</a> – I still think that someone developing an app already knows where the input is coming from and would target getQuery/getPost/etc... for that specific data in the first place but I will add __get() support as a convenience method.</p>

            <p>I think that there are still many useful properties that should be top-level in the class as well. <a class="external-link" href="http://msdn2.microsoft.com/en-us/library/system.web.httprequest_properties.aspx">http://msdn2.microsoft.com/en-us/library/system.web.httprequest_properties.aspx</a> shows a pretty good idea of them:</p>
            <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
            $request->requestUri (calculated via the function)
            $request->pathInfo (calculated via the function)
            $request->baseUrl
            $request->basePath
            $request->isSecureConnection (this needs to be calculated anyway w/ a few lines of code)
            $request->httpReferrer
            $request->userAgent
            $request->userHost
            $request->userIp
            etc...
            ]]></ac:plain-text-body></ac:macro>
            <p>Perhaps they should be in the first-letter-capital-format PathInfo, BaseUrl, HttpReferrer, etc... just to avoid conflicts with the __get() stuff?</p>

            <p>Looking forward to hearing all of your thoughts on this</p>

          3. Sep 12, 2006

            <p>Okay I went ahead and redid the stuff that was talked about by everyone in the comments.</p>

            <p>Simon I wanted to clear something up about a previous comment you had, "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..."</p>

            <p>You don't need to call setPathInfo() in your app. It is only there as a fallback mechanism in case the automatic detection does not work. If $this->_pathInfo is null then getPathInfo() will automatically call setPathInfo(null) to trigger the auto-detection. I designed it this way because I believe the code for setting $this->_pathInfo should be in the setter not the getter.</p>

            <p>This works just fine:</p>
            <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
            $request = new Zend_Http_Request();
            echo $request->getPathInfo();
            ]]></ac:plain-text-body></ac:macro>

  4. Oct 10, 2006

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comment</ac:parameter><ac:rich-text-body>
    <p>This proposal is now incorporated in <a class="external-link" href="http://framework.zend.com/wiki/display/ZFPROP/MVC+Reorganization+Proposal">http://framework.zend.com/wiki/display/ZFPROP/MVC+Reorganization+Proposal</a></p></ac:rich-text-body></ac:macro>