Parsing a URI

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

Post Reply
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Parsing a URI

Post by Luke »

Like I mentioned in a separate thread, I am writing a quick and dirty framework in php mainly for educational purposes. I am in the process of writing / testing my Visi_Request class. I would like for my request class to use the request params that are available inside of $_SERVER by default, but I'd also like to allow request creation on the fly. So something like this:

Code: Select all

$request = new Visi_Request("/path/to/file.php/named/params/are/cool/?i=like&query=params");
This has several benefits I think. This way I can allow plugins / other code to modify the request before it is routed / dispatched. It will also make unit testing MUCH easier when it comes time to unit test actual web apps built w/this framework.

So my question is this... Since I can't count on file.php or index.php or any other file name to be in the URI (since I'd like this framework to be compatible w/mod_rewrite), how do I parse a URI? The one in the example would be pretty simple, but what if I get a request like /path/to/somewhere/named/params/are/cool/. The only thing I can think of that might make this easier is if my request object knew the base path (/path/to/somewhere/). Am I on the right track?
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: Parsing a URI

Post by s.dot »

Would getcwd() and $_SERVER['PHP_SELF'] or one of the other server variables be useful to you?
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Re: Parsing a URI

Post by Luke »

Well no... because I'm trying to parse an arbitrary uri passed in through the constructor. Those would work if I was just trying to use the requested uri (from the browser).
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Parsing a URI

Post by inghamn »

Here's the URL class I've been using in our stuff. I've added to it over the past couple years as we've had a need for it. So it's pretty much a harvested class:http://martinfowler.com/bliki/HarvestedFramework.html.

It takes arbitrary URLS passed in the contructor and lets you change parts of them as needed.

Code: Select all

 
<?php
/**
 * @copyright Copyright (C) 2006,2007,2008 City of Bloomington, Indiana. All rights reserved.
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt
 *
 * Helper class for URL handling.  Parses URLs and allows adding parameters from variables.
 *
 * $url = new URL('/path/to/webpage.php?initialParameter=whatever');
 * $url->parameters['somevar'] = $somevar;
 * $url->somevar = $somevar;
 * echo $url;
 */
class URL
{
    private $protocol;
    private $script;
    public $parameters = array();
 
    public function __construct($script="",$parameters=array())
    {
        $this->parameters = $parameters;
        $this->setScript($script);
    }
 
    public function setScript($script)
    {
        $script = urldecode($script);
 
        if (preg_match('|://|',$script))
        {
            list($protocol,$script) = explode('://',$script);
            $this->protocol = "$protocol://";
        }
        else
        {
            $this->protocol = $_SERVER['SERVER_PORT']==443 ? 'https://' : 'http://';
        }
 
        # Parse any parameters already in the script
        if (ereg("\?",$script))
        {
            list($script,$parameters) = explode("?",$script);
 
            $parameters = preg_split('/[;&]/',$parameters);
            foreach($parameters as $parameter)
            {
                if (ereg("=",$parameter))
                {
                    list($field,$value) = explode("=",$parameter);
                    if ($value) { $this->parameters[$field] = $value; }
                }
            }
        }
 
        $this->script = $script;
    }
    public function getScript() { return $this->script; }
 
    public function getURL()
    {
        $url = $this->protocol.$this->script;
        if (count($this->parameters))
        {
            $url.= "?";
            foreach($this->parameters as $key=>$value) { $url.= urlencode($key).'='.urlencode($value).';'; }
        }
        return $url;
    }
 
    public function getProtocol()
    {
        if (!$this->protocol) { $this->protocol = 'http://'; }
        return $this->protocol;
    }
    public function setProtocol($string)
    {
        if (!preg_match('|://|',$string)) { $string .= '://'; }
        $this->protocol = $string;
    }
 
    public function __toString() { return $this->getURL(); }
 
    /**
     * Overloading methods
     * These will let us get and set parameters directly
     */
    public function __set($string,$value) { $this->parameters[$string] = $value; }
    public function __isset($string) { return isset($this->parameters[$string]); }
    public function __unset($string) {  unset($this->parameters[$string]); }
    public function __get($string)
    {
        if (isset($this->parameters[$string])) { return $this->parameters[$string]; }
    }
}
 
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Re: Parsing a URI

Post by Luke »

using your URL class:

Code: Select all

print new URL('/path/to/file.php/named/params/are/cool/?get=params&are=not&the=same');
the output wrote:http:///path/to/file.php/named/params/are/cool/?get=params;are=not;the=same;
What's with the semi-colons? :?
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Parsing a URI

Post by inghamn »

I've been using this in XHTML strict applications. as such, & is not a valid seperator in URL's. So, the option's either to replace all of them with & or use a different seperator. I've configured our Apache and PHP to use semi-colons.

It's certainly changeable, though
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Parsing a URI

Post by inghamn »

The W3 recommends using the semi-colon intead of the ampersand in order to validate. This requires a change in configuration for both Apache, and PHP. What's more, browsers, when they submit forms via GET, are still going to put the ampersands in anyway, so your configuration has to allow for both.

http://www.w3.org/TR/html401/appendix/n ... scii-chars

It's just a small pain to deal with in order to support XHTML Strict validation.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Parsing a URI

Post by alex.barylski »

For just parsing the URL you could use PHP's parse_url()...

Why are you manually trying to parse these from a request URI and not just use the $_REQUEST variable? Sorry if I missed something obvious but I just woke up. :)

This is generally what the Zend controller router does:

http://framework.zend.com/manual/en/zen ... asics.html
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Re: Parsing a URI

Post by Luke »

I'll just stick with ampersands and entity-ize them if I need to output them... no big deal.
Hockey wrote:Why are you manually trying to parse these from a request URI and not just use the $_REQUEST variable? Sorry if I missed something obvious but I just woke up.
In the first post I explain exactly why... did you read my first post??
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Parsing a URI

Post by alex.barylski »

but I'd also like to allow request creation on the fly. So something like this
I think is what I missed first time through :)

From what I can tell...you want your Request class to parse URI's so that you can programmatically change the URI and essentially add to what the environment has already setup for you???

I'm not sure I see the usefulness in doing that, but we have different requirements obviously so I'll leave that to your judgement...although if I were to implement something like this, I think I would go with getters/setters on the request object and use an AIP as opposed to manipulating a string.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Re: Parsing a URI

Post by Luke »

One thing I can think of that would make this method INCREDIBLY useful is web application unit testing later on:

Code: Select all

// ..snip
 
class Test_Something extends UnitTestCase {
 
    public function testControllerActionParamValue() {
        $front = Visi_Controller_Front::getInstance();
        $front->setRequest(new Visi_Request('/controller/action/named/params/'));
        $response = $front->run('/path/to/controllers');
        $this->assertEqual($response->getContext()->get('user'), array('name' => 'Dave', 'age' => 19));
    }
 
}
// snip..
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Parsing a URI

Post by alex.barylski »

I see...thats where I thought you were going...

Personally, I would rather just:

Code: Select all

class Test_Something extends UnitTestCase {
 
    public function testControllerActionParamValue() {
        $request = new Visi_Request();
        $request->setValue('ctrl', 'User');
        $request->setValue('action', 'Create');
        $request->setValue('params', array('fname' => 'blah'));
 
        $front = Visi_Controller_Front::getInstance();
        $front->setRequest(new $request);
        $response = $front->run('/path/to/controllers');
        $this->assertEqual($response->getContext()->get('user'), array('name' => 'Dave', 'age' => 19));
    }
 
}
Post Reply