[TDD] Who wants to help me build an http request class?

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Cool :)

nice easy one :P

Code: Select all

class testOfHttpRequest extends UnitTestCase
{
    public function testCreationOfNewRequestMethod()
    {
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertIdentical($_POST, $request->getRequestGlobal());
        $_POST = array(); //Clean up
    }

    public function testRetrievalOfSingleRequestVar()
    {
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertIdentical($_POST['dumy'], $request->get('dumy'));
        $_POST = array(); //Clean up
    }

    public function testCheckVarExists()
    {
        $request = new HttpRequest('post');
        $this->assertFalse($request->has('dumy'));
        unset($request);
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertTrue($request->has('dumy'));
        $_POST = array(); //Clean up
    }

    public function testOfGETRequestMethod()
    {
        $_GET['dummy'] = 1;
        $request = new HttpRequest('get');
        $this->assertIdentical($_GET, $request->getRequestGlobal());
        $this->assertTrue($request->has('dummy'));
        $this->assertIdentical($_GET['dummy'], $request->get('dummy'));
        $_GET = array();
    }
    
    public function testOfCOOKIERequestMethod()
    {
        $_COOKIE['dummy'] = 1;
        $request = new HttpRequest('cookie');
        $this->assertIdentical($_COOKIE, $request->getRequestGlobal());
        $this->assertTrue($request->has('dummy'));
        $this->assertIdentical($_COOKIE['dummy'], $request->get('dummy'));
        $_COOKIE = array();
    }
}
Though strictly speaking, it's not an actual request method - but's all part of the same jive within the HTTP header/request and is succumb to the same practices such as magic quotes :)

Code: Select all

class HttpRequest
{
    protected $reqVar;

    public function __construct($method)
    {
        switch (strtolower($method))
        {
            case 'get': $this->reqVar = $_GET;
            break;
            case 'post': $this->reqVar = $_POST;
            break;
            case 'cookie': $this->reqVar = $_COOKIE;
            break;
        }
    }
    
    public function getRequestGlobal()
    {
        return $this->reqVar;
    }

    public function get ($name)
    {
        return $this->reqVar[$name];
    }

    public function has ($name)
    {
        return isset($this->reqVar[$name]);
    }
}
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

now what? :?
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

get_magic_quotes_gpc() ?
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

I am not quite sure how to go about that... How would you fake magic_quotes being on? just addslashes() to the dummy text?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

The Ninja Space Goat wrote:I am not quite sure how to go about that... How would you fake magic_quotes being on? just addslashes() to the dummy text?
Exactly... if you can use addslashes() to do it yourself then you've mimmicked it. As for the class detecting that, use ini_set() to fool it ;) It doesn't need to really be happening.... you can set things up artificially like this ;)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

I don't think ini_set works for setting the magic_quotes because it is already done by the time your php script is run... isn't it?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Gah, you're right I just tried it. I didn't expect it to actually be turned on, but at least allow ini_get() to think it is. Looks like we're going to need to write a method to return TRUE or FALSE if magic quotes is on or not. That way we can fake it's return value ;)
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

I can do this if you want to watch and then do the next step? :)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

I'd appreciate it... I've been ripping my hair out :x
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

The Ninja Space Goat wrote:I'd appreciate it... I've been ripping my hair out :x
;) The clue is to use a partial Mock. This finished up with me needing to make a few other modifications to keep the original tests passing. Interestingly, I needed to make sure the item was a string before stripslashing it, otherwsie the assertIdentical assertions fails with a type mismatch where we had an integer.

The test

Code: Select all

class testOfHttpRequest extends UnitTestCase
{
    public function testCreationOfNewRequestMethod()
    {
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertIdentical($_POST, $request->getRequestGlobal());
        $_POST = array(); //Clean up
    }

    public function testRetrievalOfSingleRequestVar()
    {
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertIdentical($_POST['dumy'], $request->get('dumy'));
        $_POST = array(); //Clean up
    }

    public function testCheckVarExists()
    {
        $request = new HttpRequest('post');
        $this->assertFalse($request->has('dumy'));
        unset($request);
        $_POST['dumy'] = 1;
        $request = new HttpRequest('post');
        $this->assertTrue($request->has('dumy'));
        $_POST = array(); //Clean up
    }

    public function testOfGETRequestMethod()
    {
        $_GET['dummy'] = 1;
        $request = new HttpRequest('get');
        $this->assertIdentical($_GET, $request->getRequestGlobal());
        $this->assertTrue($request->has('dummy'));
        $this->assertIdentical($_GET['dummy'], $request->get('dummy'));
        $_GET = array();
    }

    public function testOfCOOKIERequestMethod()
    {
        $_COOKIE['dummy'] = 1;
        $request = new HttpRequest('cookie');
        $this->assertIdentical($_COOKIE, $request->getRequestGlobal());
        $this->assertTrue($request->has('dummy'));
        $this->assertIdentical($_COOKIE['dummy'], $request->get('dummy'));
        $_COOKIE = array();
    }

    public function testOutputIsClean()
    {
        Mock::GeneratePartial('HttpRequest', 'MockHttpRequest', array('magicQuotesIsOn', 'getRequestGlobal'));

        $input = "What's happenin' boys?";
        $_GET['dummy'] = addslashes($input);
        $request = new MockHttpRequest($this);
        $request->setReturnValue('getRequestGlobal', $_GET);
        $request->setReturnValue('magicQuotesIsOn', false);
        $this->assertEqual("What\\'s happenin\\' boys?", $request->get('dummy'));
        unset($request);

        $request = new MockHttpRequest($this);
        $request->setReturnValue('getRequestGlobal', $_GET);
        $request->setReturnValue('magicQuotesIsOn', true);
        $this->assertEqual("What's happenin' boys?", $request->get('dummy'));
        $_GET = array();
    }
}
And the code:

Code: Select all

class HttpRequest
{
    protected $reqVar;

    public function __construct($method)
    {
        switch (strtolower($method))
        {
            case 'get': $this->reqVar = $_GET;
            break;
            case 'post': $this->reqVar = $_POST;
            break;
            case 'cookie': $this->reqVar = $_COOKIE;
            break;
        }
    }

    public function getRequestGlobal()
    {
        return $this->reqVar;
    }

    public function get ($name)
    {
        $req = $this->getRequestGlobal();
        return $this->cleanRequestVar($req[$name]);
    }

    public function has ($name)
    {
        $req = $this->getRequestGlobal();
        return isset($req[$name]);
    }

    public function cleanRequestVar($req)
    {
        if (!$this->magicQuotesIsOn()) return $req;
        else
        {
            $return = array();
            if (is_array($req))
            {
                foreach ($req as $key => $value) $ret[$key] = $this->cleanRequestVar($value);
            }
            else
            {
                if (is_string($req)) return stripslashes($req);
                else return $req;
            }
        }
    }

    public function magicQuotesIsOn()
    {
        if (get_magic_quotes_gpc()) return true;
        else return false;
    }
}
By generating a partial mock we could mimmick the behaviour as if magic quotes was on. My big greivance with these (unless I'm missing something) is that you can set constructor params in the usual way so I ended up slightly changing the code for get() and has().
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Someone wanna test that still works with COOKIE? I totally never covered that one. Not sure magic_quotes screws with cookie data does it?
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Sorry to rain on your parade but if you are doing true TDD you should have written the tests before the code. But keep going anyway this looks good. I'm following closely because i'm writing a lot of unit tests at the moment.
feyd wrote:static properties are almost as bad as singletons
I do prefer static classes to singletons. What is bad about static properties? Not the principle surely.
Also I'm interested what is the definition of a plugin in the sense you have been using it? I still think of them as those things for Winamp.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

ole wrote:Sorry to rain on your parade but if you are doing true TDD you should have written the tests before the code. But keep going anyway this looks good. I'm following closely because i'm writing a lot of unit tests at the moment.
feyd wrote:static properties are almost as bad as singletons
I do prefer static classes to singletons. What is bad about static properties? Not the principle surely.
Also I'm interested what is the definition of a plugin in the sense you have been using it? I still think of them as those things for Winamp.
Excuse me. We have been writing tests, and then writing code. My understanding was that you write a small test, then write some code to pass it. Then write another test, followed by the code to pass it etc etc.

How on earth could you possibly drive the direction of development if you wrote an entire lengthy set of tests before ever writing the code? Yes test first, code second, but not *all tests* then *all code*. Maybe I misunderstood you?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

ole wrote:Also I'm interested what is the definition of a plugin in the sense you have been using it? I still think of them as those things for Winamp.
Ah ha ;) And what do winamp plugins do? They change the behaviour of the application. We can do just the same with PHP code. Plugins adhere to an API which gives them access to components in the pluggable system, and also allows the pluggable system to trigger calls in the plugins. :)
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Excuse me. We have been writing tests, and then writing code. My understanding was that you write a small test, then write some code to pass it. Then write another test, followed by the code to pass it etc etc.
My mistake, there was too much to read.
How on earth could you possibly drive the direction of development if you wrote an entire lengthy set of tests before ever writing the code? Yes test first, code second, but not *all tests* then *all code*. Maybe I misunderstood you?
This is an interesting point, exactly how many test should you write before swaping to code? I'm only new to TDD myself; all us newbies together can't be great?! I've been reading that Beck's book on TDD and this isn't specified so far.
Post Reply