SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 32
  1. #1
    SitePoint Enthusiast
    Join Date
    Dec 2005
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Superglobal wrapper object help

    Hey, I've been looking at the wrapper object for superglobals that kyberfabrikken showed us at the following link: http://www.sitepoint.com/forums/show...&postcount=132

    I was wondering how one would use this? For my simple news page I simply query my database with a dao and then iterate through an array for each record. The same is true for other uses. Something like this would seem more useful for inserting data rather than requesting data, since it could be used to add slashes to user input. Maybe I'm just not getting the point of this wrapper?

  2. #2
    SitePoint Wizard chris_fuel's Avatar
    Join Date
    May 2006
    Location
    Ventura, CA
    Posts
    2,751
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Judging from the code, what looks to be done is that the so called "wrapper" provides access to common http request variables, but sanitizing them ahead of time due to magic quotes. Magic quotes is a sinisterly evil functionality with good intentions, but bad execution. It would basically escape input that was going into a database, or came from a $_POST field. However, this was optional, which created a whole load of fun conditions where people relied on magic quotes, come to find out the server they were on didn't support them, and having rounds with strange debugging sessions. The code is a centralized method to strip the effects of magic quotes if they are enabled, as well as provide a centralized class method for accessing elements vs. accessing them seperately. One nice feature it has is the ability to automatically switch between $_POST/$_GET based on what type of server request is being established.

  3. #3
    SitePoint Enthusiast
    Join Date
    Dec 2005
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks chris, but I realized that purpose. I think I communicated my question poorly. Let me correct that.

    As I said before, when I make a request for data I use a dao which will ultimately return an array. I use PostgreSQL, so I use pg_fetch_array() for this. The data is then given a chance to be processed by my model after being retrieved, which is where I would normally use a function to do something similar to what this wrapper object does. I would take the array of data that I have (which isn't in a POST/GET array) and then strip it of slashes and then pass it on to my view.

    What I don't understand is the purpose of this wrapper object. Maybe I can understand its use for $_SESSION, but for $_POST/$_GET I don't understand in what situation this will actually be used since my dao doesn't store data in a $_POST/$_GET array.

    To my knowledge, POST/GET data is SENT, not received. When I build a form, I specify what method to use for the form, my choices being POST or GET. In what situation do I receive data in POST/GET form? That's the only situation that this script is built for, so therefore to my limited knowledge this wrapper object doesn't actually do anything, because it will never be used.

    I could imagine an object specifically used for adding slashes to data before the data is inserted into a database, but not the inverse.

    Hopefully now my question is more clear.

  4. #4
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The main benefit of a wrapper to the superglobals, is to be able to pass them around, rather than using them as globals. It is partially a stylistic issue. If the rest of your code is highly modular, and shuns global variables, it's a bit of an eyesore to have acces to superglobals all over the place.
    As you say yourself, GET and POST are normally considered constant, and so it isn't as important to localize their scope as with read/write globals. However, even though GET/POST are intended to be read only, it isn't enforced. This can lead to some serious mess if someone decides to write to them.
    Furthermore - if you are passing the input variables in, rather than using them through global scope, it's possible to mock the code that make use of it. The only way you could do this with globals, is to write to the globals and then restore them afterwards. This issue is especially important when it comes to testing your code, but avoiding globals is generally speaking a good practice.

  5. #5
    SitePoint Enthusiast
    Join Date
    Dec 2005
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So if I understand you correctly, the purpose of the wrapper object is to protect against bad behavior (writing to POST/GET)? I don't know, it still isn't making any sense.

    If someone is going to write to the POST/GET globals in an unusual way, then why decode (stripslashes) the data? Unless you're grabbing data from a database you wouldn't normally stripslashes. If data being inserted into a database has had slashes added then normally you wouldn't want to take the slashes away unless magic_quotes is on, in which case you'd check to see if it was on before adding the slashes. Data being put into POST/GET unusually probably doesn't have slashes anyway. Malicious data added to POST/GET (I'm not sure how that would work) wouldn't have slashes.

    I could see the value of this wrapper object if, instead of decoding, it encoded the data. Right now it just doesn't make sense :/

  6. #6
    SitePoint Zealot Serberus's Avatar
    Join Date
    Oct 2005
    Location
    Herts, UK
    Posts
    113
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wrapping these variables in a class makes more sense when you use TDD - allowing you to mock the request object.

    If you don't write unit tests I can see why you might see this as overkill. I certainly have a hard time convincing some of the other developers at work that this is worthwhile.

    You could argue that porting your app to a CLI environment may be easier since you have a layer of abstraction but I've never had to do this with a web app.

  7. #7
    SitePoint Enthusiast
    Join Date
    Dec 2005
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah, ok. I didn't catch it when kyber used 'mock', but when you used that word I decided to look it up. Wikipedia was informative on mock objects.

    Now I understand why the wrapper object would stripslashes. The data isn't going to be loaded into a database, and even if it is it will only be temporary.

    One of these days I'll need to look at TDD some more. I don't have enough experience to teach myself it just yet, but I can see the advantages of it.

  8. #8
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Certainly being able to Mock the request is one (big) advantage but that isn't the only advantage in doing so, and shouldn't alone be the reason for the encapsulation.

    There are other advantages and those have been widely discussed before. Also, your not only just striping slashes due to the mock but you do it as a preventitive measure, and is considered best practice.

    The bigger the application you work on (LOC) then it's a safer (saner?) bet that the encapsulation will give you long term, greater flexability.

  9. #9
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by mijokijo
    Something like this would seem more useful for inserting data rather than requesting data, since it could be used to add slashes to user input. Maybe I'm just not getting the point of this wrapper?
    I think you've misunderstood; One of the points of the wrapper is to remove slashes from the GPC data in the occasional cases where magic_quotes is turned on. So yes, it's a way of normalizing user input. But the main benefit is being able to work with the data in an object oriented way, which is advantageous in testing, amongst other things.

  10. #10
    SitePoint Enthusiast
    Join Date
    Dec 2005
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I fail to see how encapsulating the POST/GET globals is very useful.

    The only time I can see this even being used is right before data is inserted into a db. So in my model that inserts news, for example, right before I define the SQL to be used to insert the data, I would call this object to wrap the superglobals, then I would use methods like offsetGet() instead of straight $_POST/$_GET in the SQL definition. Is this a decent example?

  11. #11
    SitePoint Evangelist
    Join Date
    Mar 2006
    Location
    Sweden
    Posts
    451
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've been reading here in the Application Forum guite a while now, and almost every day I see how globals are bad. And sure, I can agree, but sometimes you really need them, like for a request object, and a database-object. So I'm thinking, how about having $GLOBALS['request'] contain the request-object, and $GLOBALS['db'] contain the database-object? If everyone could agree to do it, it wouldn't be tighly coupled, right? Since everyone would be using it, it would be easy to use the same classes in different projects with small changes. Or am I waay off?

  12. #12
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wysiwyg
    So I'm thinking, how about having $GLOBALS['request'] contain the request-object, and $GLOBALS['db'] contain the database-object? If everyone could agree to do it, it wouldn't be tighly coupled, right? Since everyone would be using it, it would be easy to use the same classes in different projects with small changes. Or am I waay off?
    Well, yes you are. In this case, tight coupling means that the relationship between to separate objects is hardcoded into the code, thus making it impossible to change at runtime. The db example is the easiest to drive home an argument against, so I'll pick on that; If everybody agrees on using $GLOBALS['db'] for their database connection object, you would a) assume that there is only ever one database connection in the entire application, b) assume that everybody is using the same driver/abstraction layer. What happens when you mix two applications, where one stores the result of mysql_connect and another stores an instance of PEAR_DB ?

    Quote Originally Posted by mijokijo
    ... then I would use methods like offsetGet() instead of straight ...
    offsetGet is a "magic" method. When an object implements ArrayAccess, the object will act as an array. For example :
    PHP Code:
    class Foo implements ArrayAccess
    {
        function 
    offsetGet($offset) {
            return 
    "test:".$offset;
        }

        function 
    offsetSet($offset$value) {}
        function 
    offsetExists($offset) {}
        function 
    offsetUnset($offset) {}
    }
    $foo = new Foo();
    $qux $foo["bar"];
    echo 
    $qux// will output "test:bar" 
    So you shouldn't call offsetGet directly.

    Quote Originally Posted by mijokijo
    Unless you're grabbing data from a database you wouldn't normally stripslashes.
    You shouldn't need to use stripslashes on a result from database. You escape data before inserting, but you shouldn't unescape it when reading.

    Quote Originally Posted by mijokijo
    If data being inserted into a database has had slashes added then normally you wouldn't want to take the slashes away unless magic_quotes is on, in which case you'd check to see if it was on before adding the slashes.
    And you need to know if the data being inserted did come from http request or somewhere else. It's getting complex.

    Quote Originally Posted by mijokijo
    The only time I can see this even being used is right before data is inserted into a db. So in my model that inserts news, for example, right before I define the SQL to be used to insert the data, I would call this object to wrap the superglobals, then I would use methods like offsetGet() instead of straight $_POST/$_GET in the SQL definition. Is this a decent example?
    Not at all. The idea is to avoid escaping data too early, which is what magic_quotes does. The request object would be created very early in the application, while the database access would happen very late. In the span between theese two points in time, a lot might happen with the data, so it's very unconvenient to escape them at input time. Unless you want your database access layer to be aware of the http request, you shouldn't rely on magic quotes.

  13. #13
    SitePoint Evangelist
    Join Date
    Mar 2006
    Location
    Sweden
    Posts
    451
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I see your point, thanks for clearing it up.

  14. #14
    Web-coding NINJA! silver trophy beetle's Avatar
    Join Date
    Jul 2002
    Location
    Dallas, TX
    Posts
    2,900
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I actually prefer an implementation like this. Largely a style choice I guess. Obviously a PHP5 implementation
    PHP Code:
    <?php

    class ArrayWrapper implements ArrayAccess
    {
        protected 
    $arr;

        function 
    __construct$arr )
        {
            
    $this->arr $arr;
        }

        public function 
    get$key$notFoundValue=false )
        {
            if ( 
    $this->offsetExists$key ) )
            {
                try {
                    return 
    $this->offsetGet$key );
                }
                catch ( 
    Exception $e )
                {
                    
    //    TODO
                
    }
            }
            return 
    $notFoundValue;
        }

        public function 
    set$key$value )
        {
            try {
                
    $this->offsetSet$key$value );
            }
            catch( 
    Exception $e )
            {
                
    //    TODO
            
    }
        }

        public function 
    delete$key )
        {
            try {
                
    $this->offsetUnset$key );
            }
            catch ( 
    Exception $e )
            {
                
    //    TODO
            
    }
        }

        final public function 
    offsetExists$offset )
        {
            return 
    array_key_exists$this->arr );
        }

        final public function 
    offsetGet$offset )
        {
            return 
    $this->arr[$offset];
        }

        public function 
    offsetSet$offset$value )
        {
            
    $this->arr[$offset] = $value;
        }

        public function 
    offsetUnset$offset )
        {
            unset( 
    $this->arr[$offset] );
        }

        final public function 
    mapCallback$callback )
        {
            
    recursive_array_map$callback$this->arr );
        }
    }

    abstract class 
    GPCWrapper extends ArrayWrapper
    {
        final public function 
    offsetSet$offset$value )
        {
            throw new 
    Exception"Not Implemented" );
        }

        final public function 
    offsetUnset$offset )
        {
            throw new 
    Exception"Not Implemented" );
        }

        final public function 
    toQueryString$urlEncode=true$forHTML=false )
        {
            
    $qs = array();
            
    $callback $urlEncode 'urlencode' NULL;
            foreach( 
    array_keys$this->arr ) as $key )
            {
                if ( 
    is_array$this->arr[$key] ) )
                {
                    foreach( 
    array_keys$this->arr[$key] ) as $subKey )
                    {
                        
    $qs[] = call_user_func$callback$key '[' $subKey ']' ) . '=' call_user_func$callback$this->arr[$key][$subKey] );
                    }
                } else {
                    
    $qs[] = call_user_func$callback$key ) . '=' call_user_func$callback$this->arr[$key] );
                }
            }
            return 
    implode$forHTML '&nbsp;' '&'$qs );
        }

        protected function 
    sanitize()
        {
            if ( 
    get_magic_quotes_gpc() )
            {
                
    $this->mapCallback'stripslashes' );
            }
        }
    }

    class 
    PostWrapper extends GPCWrapper
    {
        function 
    __construct()
        {
            
    parent::__construct$_POST );
            
    $this->sanitize();
        }
    }

    class 
    GetWrapper extends GPCWrapper
    {
        function 
    __construct()
        {
            
    parent::__construct$_GET );
            
    $this->sanitize();
        }
    }

    class 
    CookieWrapper extends GPCWrapper
    {
        function 
    __construct()
        {
            
    parent::__construct$_COOKIE );
            
    $this->sanitize();
        }

        final public function 
    set$key$value$expire=30$secure=0$path='/'$domain='' )
        {
            if ( 
    '' == $domain ) { $domain $_SERVER['HTTP_HOST']; }
            
    $expire 60 60 24 $expire;
            return 
    setcookie$key$value$expire$path$domain$secure );
        }

        final public function 
    delete$key$expire=30 )
        {
            return 
    $this->set$key''$expire );
        }
    }

    class 
    QueryString extends GPCWrapper
    {
        function 
    __construct$fromArray=array() )
        {
            
    $this->arr recursive_array_copy$fromArray );
            
    $this->sanitize();
        }

        final public function 
    set$key$value )
        {
            
    ArrayWrapper::offsetSet$key$value );
        }

        final public function 
    delete$key )
        {
            
    ArrayWrapper::offsetUnset$key );
        }
    }

    function 
    recursive_array_map$callback$var )
    {
        if ( 
    is_array$var ) )
        {
            foreach ( 
    array_keys$var ) as $key )
            {
                
    recursive_array_map$callback$var[$key] );
            }
        } else {
            
    $var call_user_func_array$callback, Array( $var ) );
        }
    }

    function 
    recursive_array_copy$arr )
    {
        
    $copy = array();
        foreach ( 
    array_keys$arr ) as $key )
        {
            if ( 
    is_array$arr[$key] ) )
            {
                
    $copy[$key] = recursive_array_copy$arr[$key] );
            } else {
                
    $copy[$key] = $arr[$key];
            }
        }
        return 
    $copy;
    }

    ?>
    And then

    PHP Code:
       <?php
       
    require_once( 'ArrayWrapper.php' );
       
    $qs = new QueryString$_GET );
      
    $qs->set'someKey''someValue' );
    $qs->delete'someOtherKey' );

    echo 
    '<a href="somelink.php?' $qs->toQueryStringtruetrue ) . '">Link</a>';
      
    ?>
    Last edited by beetle; Jun 23, 2006 at 08:59.
    beetle a.k.a. Peter Bailey
    blogs: php | prophp | security | design | zen | software
    refs: dhtml | gecko | prototype | phpdocs | unicode | charsets
    tools: ide | ftp | regex | ffdev




  15. #15
    Afraid I can't do that Dave Hal9k's Avatar
    Join Date
    Mar 2004
    Location
    East Anglia, England.
    Posts
    640
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What about the session superglobal?

  16. #16
    Web-coding NINJA! silver trophy beetle's Avatar
    Join Date
    Jul 2002
    Location
    Dallas, TX
    Posts
    2,900
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I just kinda wrote that all up in the last 30 mins. A mix of kyberfabrikken's code and my own development style.

    You could certainly go on to do $_SESSION and $_REQUEST and whatever else. The above isn't meant to be production level code - I didn't even handle the exceptions.
    beetle a.k.a. Peter Bailey
    blogs: php | prophp | security | design | zen | software
    refs: dhtml | gecko | prototype | phpdocs | unicode | charsets
    tools: ide | ftp | regex | ffdev




  17. #17
    Afraid I can't do that Dave Hal9k's Avatar
    Join Date
    Mar 2004
    Location
    East Anglia, England.
    Posts
    640
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by beetle
    You could certainly go on to do $_SESSION and $_REQUEST and whatever else. The above isn't meant to be production level code - I didn't even handle the exceptions.
    Oh, sure, it was really meant as an open question, nice code beetle

  18. #18
    Afraid I can't do that Dave Hal9k's Avatar
    Join Date
    Mar 2004
    Location
    East Anglia, England.
    Posts
    640
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I guess something like this for a $_SESSION:

    PHP Code:
    class SessionWrapper implements ArrayAccess

    {

        public 
    $Session;
        
        function 
    __construct() 
        {
            
    $this->Session = &$_SESSION;
        }
        
        
        function 
    delete($offset)
        {
            
    $this->offsetUnset($offset);
        }

        function 
    offsetExists($offset)
        {
            return isset(
    $this->Session[$offset]);
        }
        
        function 
    offsetGet($offset) {
            return 
    $this->Session[$offset];
        }
        
         function 
    offsetSet($offset$value) {
                    
    $this->Session[$offset] = $value;
              }
       
        function 
    offsetUnset($offset) {
            unset(
    $this->Session[$offset]);
        }


    Passing the $_SESSION by reference.

    Edit:

    I suppose even though it's referencing the global directly, at least the programmer has to pass the object around to actually access it? Is that the whole point? So practically you could swap out the $_SESSION for a normal array and it'd basically keep the same functionality? Though it'd lose persistence though page requests...

  19. #19
    Web-coding NINJA! silver trophy beetle's Avatar
    Join Date
    Jul 2002
    Location
    Dallas, TX
    Posts
    2,900
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Inside my api, I'd do it like this
    PHP Code:
    <?php

    class SessionWrapper extends ArrayWrapper 
    {
        function 
    __construct()
        {
            
    session_start();
            
    parent::__construct$_SESSION );
        }
        
        function 
    destroy()
        {
            unset( 
    $this->arr );
            
    session_destroy();
        }
    }

    ?>
    beetle a.k.a. Peter Bailey
    blogs: php | prophp | security | design | zen | software
    refs: dhtml | gecko | prototype | phpdocs | unicode | charsets
    tools: ide | ftp | regex | ffdev




  20. #20
    SitePoint Wizard chris_fuel's Avatar
    Join Date
    May 2006
    Location
    Ventura, CA
    Posts
    2,751
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm kind of iffy on $_SESSION for that kind of stuff, though mainly with regards to the data sanitation. Sessions aren't affected by global quotes like $_POST and friends, and session data that's dependant upon user input should be sanitized BEFORE going into the session variable.

    I'm also confused on this bit of code:

    PHP Code:
        public function get$key$notFoundValue=false )
        {
            if ( 
    $this->offsetExists$key ) )
            {
                try {
                    return 
    $this->offsetGet$key );
                }
                catch ( 
    Exception $e )
                {
                    
    //    TODO
                
    }
            }
            return 
    $notFoundValue;
        } 
    when would $notFoundValue ever be true?

  21. #21
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by chris_fuel
    when would $notFoundValue ever be true?
    Whenever someone calls it as $obj->get('non-existant key', true)?

    Kind of defeats to purpose of ArrayAccess though.
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  22. #22
    SitePoint Wizard chris_fuel's Avatar
    Join Date
    May 2006
    Location
    Ventura, CA
    Posts
    2,751
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sweatje
    Whenever someone calls it as $obj->get('non-existant key', true)?

    Kind of defeats to purpose of ArrayAccess though.
    That's sort of what I was trying to say, why would someon call it with true.

  23. #23
    Web-coding NINJA! silver trophy beetle's Avatar
    Join Date
    Jul 2002
    Location
    Dallas, TX
    Posts
    2,900
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's not intended to be a boolean. MAybe it's not the best name (sorry, like I said, I typed it up on lunch and didn't spend alot of time on semantics), and hard to understand without any user docs. It's a way of retrieveing a specified value in your return should the given key be non-existant. The default for that just happens to be FALSE, but NULL might be more appropriate?

    PHP Code:
      $Get = new GetWrapper;
      
    $status $Get->get'status''ok' ); // "ok" is the default status 
    Also,
    Quote Originally Posted by chris_fuel
    I'm kind of iffy on $_SESSION for that kind of stuff, though mainly with regards to the data sanitation. Sessions aren't affected by global quotes like $_POST and friends, and session data that's dependant upon user input should be sanitized BEFORE going into the session variable.
    I didn't implement my session class as subclass to GPCWrapper (which has the sanitization method), it's a subclass of ArrayWrapper (which implements ArrayAccess and a couple accessor/mutator methods of my own). If you didn't want to wrap $_SESSION, then you wouldn't have to.
    beetle a.k.a. Peter Bailey
    blogs: php | prophp | security | design | zen | software
    refs: dhtml | gecko | prototype | phpdocs | unicode | charsets
    tools: ide | ftp | regex | ffdev




  24. #24
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by chris_fuel
    That's sort of what I was trying to say, why would someon call it with true.
    As beetle said:

    PHP Code:
    $obj->get('key which may or may not be there''default value if not there'); 
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  25. #25
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm...

    PHP Code:
    // ...
    $status $Get->get'status''ok' ); 
    But should the wrapper know about a default? I would think it be better for the client to know that information no?


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •