Jump to content
Larry Ullman's Book Forums

Ultimate Session Wrapper Class


Recommended Posts

As per the previous topic on Session Best Practices, I have put together a class that I believe is the simplest, most comprehensive, and secure I have ever seen on the web or in books!

 

It is an amalgamation of a session class I found on Github here: https://gist.github.com/Nilpo/5449999 and the session security functions Hartleysan wrote.  I also added a few other things as well!  It uses static functions, so it doesn't have to be instantiated.  To use it, just call the start() function with the following, optional, parameters:

 

Session::start(boolean $regenerate_session_id = false, int $limit = 0, string $path = '/', string $domain = null, boolean $secure_cookies_only = null);

 

The first parameter is used to tell the _init() function if you want to regenerate the session id.  The other 4 parameters deal with cookie settings and all correspond to those sent to the set_session_cookie_params() function.

 

So, you can do something like :

Session::start(true);

Session::start(true, 0, '/my_public_pages/', 'www.example.com');

etc...

If you want to use the CustomException.php class, it can be copied from here: https://gist.github.com/Nilpo/4312309

 

I have thought about adding the session name parameter to the start() function, as well as moving the session_save_path() out of the class and putting it before the session class is used as I've heard that it can cause problems with regenerating the session id (however, I haven't seen this in the tests that that I've done):

 

ini_set('session.save_path', '/path/to/session/files');

 

Here is the Session class:

<?php
 
/**
 * Session Helper Class
 *
 * A simple session wrapper class.
 *
 * Recommended for use with PHP 5.4.0 or higher. (Not required.)
 *
 * Copyright (c) 2013 Robert Dunham
 *
 * Additions by Jon Hartley and Matt Ulrich
 */
 
include_once (CUSTOM_EXCEPTION_CLASS);
 
if ( !class_exists('CustomException') ) {
    class CustomException extends Exception {}
}
class SessionHandlerException extends CustomException {}
class SessionDisabledException extends SessionHandlerException {}
class InvalidArgumentTypeException extends SessionHandlerException {}
class ExpiredSessionException extends SessionHandlerException {}
 
//defined('CHECK_ACCESS') or die('Direct access is not allowed.');
 
class Session
{
    /* The diecrotry name for the session */
    private static $SESSION_DIR = 'd79252d7dea8e2812b4ebf29ffc603ed/';
    
    /* The name used for the session */
    private static $SESSION_NAME = 'f7eac143c2e6c95e84a3e128e9ddcee6';
    
    /**
     * Session Age.
     * 
     * The number of seconds of inactivity before a session expires.
     * 
     * @var integer
     */
    protected static $SESSION_AGE = 1800;
    
    /**
     * Writes a value to the current session data.
     * 
     * @param string $key String identifier.
     * @param mixed $value Single value or array of values to be written.
     * @return mixed Value or array of values written.
     * @throws InvalidArgumentTypeException Session key is not a string value.
     */
    public static function write($key, $value)
    {
        if ( !is_string($key) )
            throw new InvalidArgumentTypeException('Session key must be string value');
        self::_init();
        $_SESSION[$key] = $value;
        self::_age();
        return $value;
    }
    
    /**
     * Reads a specific value from the current session data.
     * 
     * @param string $key String identifier.
     * @param boolean $child Optional child identifier for accessing array elements.
     * @return mixed Returns a string value upon success.  Returns false upon failure.
     * @throws InvalidArgumentTypeException Session key is not a string value.
     */
    public static function read($key, $child = false)
    {
        if ( !is_string($key) )
            throw new InvalidArgumentTypeException('Session key must be string value');
        self::_init();
        if (isset($_SESSION[$key]))
        {
            self::_age();
            
            if (false == $child)
            {
                return $_SESSION[$key];
            }
            else
            {
                if (isset($_SESSION[$key][$child]))
                {
                    return $_SESSION[$key][$child];
                }
            }
        }
        return false;
    }
    
    /**
     * Deletes a value from the current session data.
     * 
     * @param string $key String identifying the array key to delete.
     * @return void
     * @throws InvalidArgumentTypeException Session key is not a string value.
     */
    public static function delete($key)
    {
        if ( !is_string($key) )
            throw new InvalidArgumentTypeException('Session key must be string value');
        self::_init();
        unset($_SESSION[$key]);
        self::_age();
    }
    
    /**
     * Echos current session data.
     * 
     * @return void
     */
    public static function dump()
    {
        self::_init();
        echo nl2br(print_r($_SESSION));
    }
 
    /**
     * Starts or resumes a session by calling {@link Session::_init()}.
     * 
     * @see Session::_init()
     * @return boolean Returns true upon success and false upon failure.
     * @throws SessionDisabledException Sessions are disabled.
     */
    public static function start($regenerate_session_id = true, $limit = 0, $path = '/', $domain = null, $secure_cookies_only = null)
    {
        // this function is extraneous
        return self::_init($regenerate_session_id, $limit, $path, $domain, $secure_cookies_only);
    }
    
    /**
     * Expires a session if it has been inactive for a specified amount of time.
     * 
     * @return void
     * @throws ExpiredSessionException() Throws exception when read or write is attempted on an expired session.
     */
    private static function _age()
    {
        $last = isset($_SESSION['LAST_ACTIVE']) ? $_SESSION['LAST_ACTIVE'] : false ;
        
        if (false !== $last && (time() - $last > self::$SESSION_AGE))
        {
            self::destroy();
            throw new ExpiredSessionException();
        }
        $_SESSION['LAST_ACTIVE'] = time();
    }
    
    public static function regenerate_session_id() {
      
        $session = array();
        
        foreach ($_SESSION as $k => $v) {
        
            $session[$k] = $v;
            
        }
        
        session_destroy();
        
        session_id(bin2hex(openssl_random_pseudo_bytes(16)));
        
        session_start();
        
        foreach ($session as $k => $v) {
        
            $_SESSION[$k] = $v;
            
        }
      
    }
    
    /**
     * Returns current session cookie parameters or an empty array.
     * 
     * @return array Associative array of session cookie parameters.
     */
    public static function params()
    {
        $r = array();
        if ( '' !== session_id() )
        {
            $r = session_get_cookie_params();
        }
        return $r;
    }
    
    /**
     * Closes the current session and releases session file lock.
     * 
     * @return boolean Returns true upon success and false upon failure.
     */
    public static function close()
    {
        if ( '' !== session_id() )
        {
            return session_write_close();
        }
        return true;
    }
    
    /**
     * Alias for {@link Session::close()}.
     * 
     * @see Session::close()
     * @return boolean Returns true upon success and false upon failure.
     */
    public static function commit()
    {
        return self::close();
    }
    
    /**
     * Removes session data and destroys the current session.
     * 
     * @return void
     */
    public static function destroy()
    {
        if ( '' !== session_id() )
        {
            $_SESSION = array();
 
            // If it's desired to kill the session, also delete the session cookie.
            // Note: This will destroy the session, and not just the session data!
            if (ini_get("session.use_cookies")) {
                $params = session_get_cookie_params();
                setcookie(session_name(), '', time() - 42000,
                    $params["path"], $params["domain"],
                    $params["secure"], $params["httponly"]
                );
            }
 
            session_destroy();
        }
    }
    
    /**
     * Initializes a new session or resumes an existing session.
     * 
     * @return boolean Returns true upon success and false upon failure.
     * @throws SessionDisabledException Sessions are disabled.
     */
    private static function _init($regenerate_session_id = false, $limit = 0, $path = '/', $domain = null, $secure_cookies_only = null)
    {
        if (function_exists('session_status'))
        {
            // PHP 5.4.0+
            if (session_status() == PHP_SESSION_DISABLED)
                throw new SessionDisabledException();
        }
        
        if ( '' === session_id() )
        {
            $site_root = BASE_URI;
            $session_save_path = $site_root . self::$SESSION_DIR;
            session_save_path($session_save_path);
            
            session_name(self::$SESSION_NAME);
            
            $domain = isset($domain) ? $domain : $_SERVER['SERVER_NAME'];
            
            session_set_cookie_params($limit, $path, $domain, $secure_cookies_only, true);
            
            session_start();
            
            if ($regenerate_session_id) {
                self::regenerate_session_id();
            }
            
            return true;
        
        }
        
        self::_age();

        if ($regenerate_session_id && rand(1, 100) <= 5) {
            self::regenerate_session_id();
            $_SESSION['regenerated_id'] = session_id();
        }
        
        return true;
        
    }
}

If anyone would like to make any edits to it, then please do so, otherwise, enjoy!

 

Matt

Link to comment
Share on other sites

Larry,

 

Thanks for the kind reply!

 

I have done more research and I had a couple of questions:

 

1)  The properties/functions are all static, but should I change them into non-static properties/functions where the class has to be instantiated before using it?  Antonio said before that using "static" classes is bad, and I have read that on many sites as well, as you are doing nothing more than creating a namespace for the static properties and functions so they don't crowd the global namespace.  However, it just seems that a static class is the cleanest thing to do here.  There is only a single session per user at any time, and it just seems that a single, fixed class would be more appropriate.  What do you think?

 

2)  I noticed that when I regenerate the session id, the save_path and cookie settings remain unchanged even though session_destroy() and session_start() have been called in the regenerate_session_id() function.  I thought all settings had to be done again each time the session is started.  If anyone could explain to me what is going on, I would be much appreciative!

 

Thanks,

 

Matt

Link to comment
Share on other sites

I found an error in this guys class!

 

If you look at the _read() function, it is first checking if the $key exists in the session.  Then it calls the _age() function just before reading the session variable.  Normally this is fine, but if the session has gone past it's age limit, the _age() function calls the destroy() function, which destroys the session.  Then, when it reaches the point where it tries to read the session variable, it will give an error saying it can't find the variable blah, blah.  I discovered this by accidentally hardcoding the $SESSION_AGE to 30, then I was always getting an error after 30 seconds of no activity.  The guy who wrote the class probably never found this out.

 

What I did to fix this was pass a boolean back from the _age() function:

    private function _age()
    {
        $last = isset($_SESSION['LAST_ACTIVE']) ? $_SESSION['LAST_ACTIVE'] : false ;
        
        if (false !== $last && (time() - $last > self::$SESSION_AGE))
        {
            return $this->destroy();
        }

        $_SESSION['LAST_ACTIVE'] = time();
        
        return false;
    }

Then, in the read() function I check the result of the boolean to determine if it should get the value from the session:

    public function read($key, $child = false)
    {
        if ( !is_string($key) )
        {
            return false;
        }
        if (isset($_SESSION[$key]))
        {
            if(self::_age() === false) {
                if (false == $child)
                {
                    return $_SESSION[$key];
                }
                else
                {
                    if (isset($_SESSION[$key][$child]))
                    {
                        return $_SESSION[$key][$child];
                    }
                }
            }
        }
        return false;
    }

Sorry for the confusion about that!

Link to comment
Share on other sites

 

I'm curious why you would separate session handling functionality into a separate class?

Testability for one. The problem you discovered and similar loop holes in logic could get caught by that. Also, in larger teams, you'll not see regression, i.e the same problems comming back. Another point is that everything $_REQUEST-related in PHP is so similar it could benefit from inheritance, exactly why I made a class in the first place.

 

 

Why in the world would you want to write an interface

 

Due to composition. Wouldn't it make you happy to pass your whole Session object into a JSON parser, being able to foreach a Session object, sort session values internally or similar? By providing interfaces, you Sorter class doesn't give a damn if it gets a Session or a Dog object. (It would probably like a dog better thought) A key concept here is seperations of concerns, and that is also why you'll see loads of classes for something as simple as a session.

 

 

This kind of unnecessary abstraction is the exact reason why sites using frameworks are generally slow!  I`m not trying to be hard on you, but I really want to know what I am missing here.

 

That's what caching is for. While it takes time to generate object-oriented object graphs (or threes, or list) it gives you power to focus on your domain. When you application is no longer simple CRUD work, but you need to verify something like that rules and algorithms for car parking does what it's supposed to, you don't want procedural code that's hard to understand. Maintainability, extensibility and other fancy words are very much more important from a business perspective. eZ Publish takes 12 seconds to load in dev enviroment (mainly due to logging) on a project I'm working on, but throw Varnish on top of it, and it can serve millions upon millions of requests a day. Best part? Load is BETTER the more traffic the site gets.

 

 

 What are the drawbacks of using static functions?

 

They can reach from huge to non-existing. There's so many good explanation online, so I won't bore you with mine. In a setting like Sessions, they aren't really important if you ask me, as sessions are a very specific thing. That's why I've also gone that pretty much the same road as you did in your Session class above.

 

Nice job, Matt.

Link to comment
Share on other sites

Antonio,

 

Thanks for the detailed response.  I understand most of what you said but I did have a couple of questions.

 

Due to composition. Wouldn't it make you happy to pass your whole Session object into a JSON parser, being able to foreach a Session object, sort session values internally or similar? By providing interfaces, you Sorter class doesn't give a damn if it gets a Session or a Dog object. (It would probably like a dog better thought) A key concept here is seperations of concerns, and that is also why you'll see loads of classes for something as simple as a session.

 

I am a little confused here.  I thought an interface was just a programming structure used to enforce certain properties on a class.  It isn't an object, and it isn't a class either.  That being the case, why would you want to pass one to a sorter?  Sorry to sound so naive, but things have gotten much more complicated with 00P in recent years!

 

 

Maintainability, extensibility and other fancy words are very much more important from a business perspective. eZ Publish takes 12 seconds to load in dev enviroment (mainly due to logging) on a project I'm working on, but throw Varnish on top of it, and it can serve millions upon millions of requests a day. Best part? Load is BETTER the more traffic the site gets.

 

I know what you're saying, but the site that Jon and I are making isn't Amazon.com, where we have a huge team of developers that all need to be on the same page.  It is a small - medium size site that doesn't have a tremendous amount of pages.  The traffic may get a little high at times, but nothing heavy.  That being said, I think going overboard with the OOP paradigm will just add more work and confusion to an already big project.  I just think that people make a simple thing very complicated by all the objects passing objects to other objects.  There is no reason for it.  We do have a lot of functions, but they are all organized into separate include files which are assigned to constants in the config file.  When we want to use a function we include the constant (i.e. file) and call the function.  Easy!  No instantiation, no confusion about why the response that came back from a complicated inherited class is what it is, no objects sitting in memory, etc...  I think that most people jump on the OOP train because it's become a trend, and if you're not doing it there is something wrong with you, or you're a NOOB.  Sorry, but I was doing OOP from the beginning and in most web development environments I just don't see the need for it!

 

I did have one OOP question though (he, he).  I just changed the class so that it wasn't using static functions, and thus has to be instantiated with the "new" operator.  When I did that, I noticed that every function that used to use the static session class didn't have access to the session object anymore.  Don't worry, I changed everything from Session::read('id'); to $session->read('id');.  What I figured out is that the new $session object isn't global anymore.  I have to pass it to the function in order for the function to use it.  I think that is one more benefit to using a static class.  What do you think?

 

Matt

Link to comment
Share on other sites

First off, it's been an eventful night out with some drinks, so please excuse me if I don't make much sense.

 

To take you first question, implementing an interface is the only real way for a Sorter class to trust the object has sorting capabilities. This is even more clear in a fully object-oriented language like Java where you need to define return type from a method. Because a class implements an interface like "Sortable" we can trust it to be sortable. This is a contract more than anything else, but it defines expected behaviors.

 

A session object might not need to be sortable (because it doesn't make sense in itself) but what about JSONStructurable, Serializable, Hashable or Expirable? These are all acceptable actions to do to a session, and the interface would be an reinforcer of that. The interfaceces would of course return the session as JSON, in serializable form, as an identifiable hash or communicate that is possible to expire through interfacing.

 

----------

 

To move on to your next statement. While we are not building amazon, every programmer have a different style of coding. Object orientation is goos at defining where the task we need solved happens and where we solve it. Separation of concern is one of those buzzwords that describes object orientation. OOP is all about mapping programming problems to a real world domain, and helping us to simplify code.

 

While you describe OOP as advanced, abstracted and hard to understand, I would argue that is plain and simply wrong. Badly coded OOP is even worse than any procedurally coded project, but properly designed, sensible OOP simplifies any programming problem you might have into human-readable, plain simple english. While this may sound like an Utopian dream world, this is what the programming world is working towards.

 

I believe very much in readable code. The perfect example in that regard is the Laraval PHP framework. While being a 100% OOP web development framework, you get your logged in user by calling Auth::user(). To send an Email in the background using a queue (pageload finished and job executing in the background) all you got to do is call Queue::push('SendEmail', array('message' => $message)); This is similar to human-readable language, and works like expected. While lots of logic is dispensed in the background, the incredible Facade pattern eliminates any object instantiation or similar that is irrelevant to the task. Still, in the core system, everything is made simpler by the fact that the system relies in real objects. 

 

My point here is that while OOP might seem like an obstacle to "code that simply works", OOP is about hiding complexity where it belongs. OOP "client code", as the code I exemplified by Laravel is, should be no harder to understand than procedural code. If it is, it's simply shitty object-orientation, and shitty code exists for both procedurally and object-oriented code without a doubt.

 

Sorry again for the long intoxicated ramble. I hope I'm able to explain myself clearly here. Have a great night (or day) everyone.

Link to comment
Share on other sites

Antonio,

 

Sorry for not responding yet!  I have been pretty busy and I wanted to think about what I wanted to say before leaving a reply.  I am enjoying the discussion too!

 

I will write soon!

 

Matt

Link to comment
Share on other sites

  • 2 weeks later...

Antonio,

 

Sorry it took me so long to respond.  I was ver busy recently with work!

 

Thank you for your detailed response, but I was a little confused about the following:

 

To take you first question, implementing an interface is the only real way for a Sorter class to trust the object has sorting capabilities. This is even more clear in a fully object-oriented language like Java where you need to define return type from a method. Because a class implements an interface like "Sortable" we can trust it to be sortable.

 

So you mean to say that some object that you pass to the Sorter class should implement an interface so that we know that it can be sorted?  I could see that being done in some large company where there are a lot of developers and rules need to be enforced, but why on earth would you want to do that with a small web site?  And how does the Sorter class even know that you implemented the interface?  Correct me if I'm wrong, but you could technically pass it whatever you wanted.  An interface is nothing more than a description of the functions that an object should have.  If you choose not to implement the interface, then you don't necessarily have those functions.

 

Also, I took a look at Laravel, and although I have heard that it is one of the better frameworks, I was absolutely appalled!  I looked at the session API and there are 6 CLASSES FOR SESSIONS!  Most of them contained what appeared to be useless methods (boot(), provides(), register(), etc...).  Of all those, I could only see one or two that made any sense at all (CookieSessionHandler and CacheBasedSessionHandler) and all of them extended or implemented an abstract class or an interface!  Antonio, what is going on?

This is NOT the solution to modern web development.  It's the problem!!!  Instantiating class, after class, after class, and calling function, after function, after function, and subsequently getting thoroughly lost in code to accomplish what I'm not sure, is just a waste of time and energy.  And, I don't want to burst anyones bubbles, but OOP is also slow!  This is not up for debate, it is a fact!  Do you ever wonder why, when you go to a site ran by a CMS (i.e. Wordpress or Joomla) it takes a few seconds for the page to load?  It's because you have to instantiate 10 different classes and call 16 different methods to write something as simple as "Hello World" on the screen!  Doesn't this sort of thing sound pretty absurd to you?  If so, then why are you choosing to participate in it?

 

Matt

  • Upvote 1
Link to comment
Share on other sites

Caching. Object orientation IS slow, but that was however never the problem it tried to solve. Readability and abstractions (high-level vs low-lever code) is OOPs biggest strength, and the reason why people use Java, C# or even PHP. If speed is all that matters, write assembly or C++, because PHP will never be able to compete with that.

 

If you don't implement the interface, the Sorter won't accept that object. It's a contract, and it's other developers job to abide to it. If all their method return "LOL" that's on them really. Nothing is fool-proof, but an interface is communicating it's intent way better than anything else currently available. If you don't abide to the contract as a develop, an exception will be thrown.

 

Why I'm choosing to participate in it? Because it works. It's proven time and time again that procedural code ends up as messy spaghetti code that is unmaintainable and ends up labeled as "legacy". Sure, as a one-man team you'll be able to make it work, but no way for larger organizations

  • Upvote 1
Link to comment
Share on other sites

This is a great conversation! Just to chime in, performance can be measured in many ways. Application performance is one, but the amount of time it takes to develop a project is another. Arguably, developer time is the much more expensive and important of the two (i.e., application performance is easy to improve; developer time is not recoverable). 

 

Being opposed to OOP because it's slow is a fairly narrow look at the scope of development. (Also, for what it's worth, WordPress is not truly OOP, although it has classes.)

  • Upvote 2
Link to comment
Share on other sites

Antonio, I do agree with you about the abstractions! It definitely cleans up the code when functionality is organized that way. I am not opposed to using a little OOP on a web site for this very purpose. It is why I made the Session class (which really isn't true OOP, but a namespaced set of static functions). Also, I am still not completely sure I know what you mean about the Sorter not accepting an object that doesn't implement the interface. How does the sorter know this? What I thought was that if the object you pass doesn't have the functions defined by the interface (i.e. it doesn't implement it), then when the Sorter tries to call them an error will be thrown.

Larry, thanks for the response and please don't get me wrong. I am not opposed to OOP in general! I think it's a very great and powerful tool in many situations. However, I just don't think it's right for web development in most of the ways it is being used. Here's why I think that way, and it's something that I think is lost on developers who are using it to build web sites. As everyone knows (or should know), the web is "stateless" by it's very nature, so with every request the entire page must be rebuilt. If server side code is used to build the page, then that is run and then removed from memory. This is true even with an AJAX request as the server side file will be called once and then destroyed. This environment is greatly different than a native desktop or local machine environment where code is ran, variables are stored in memory, things are calculated and restored in memory to be used at some later time, etc... With the web, this all happens in one short time span. Then everything is cleared from memory (in theory) and the whole thing starts over with the next page request. Of course, there are ways to pass state between page requests with sessions, but this is simply a hack to get around the original problem that the web is "stateless". That being said, if you are using OOP then you are going to instantiate a bunch of classes and store them in memory. Objects certainly don't take up that much space (especially since memory is cheap these days), but it still takes time to write and retrieve them to / from memory. Then, you are going to call twice as many functions (this is on top of the constructor calls made to create the initial objects), pass objects all over the place, and do calculations which are stored in objects and rewritten to memory. This is much slower than just using procedural code (functions are already expensive in PHP). Lastly, after the whole page has been created and sent off to the client, you are going to destroy everything only to start this whole process over again with the next page request. Certainly, some PHP files are stored in cache to speed things up, but that's not the point. The point is that you are creating all this code and using all this processing in a situation that I feel doesn't really need it in the first place. And all to do what, to make it easier for the developer!

 

That being said, I do agree with you that developer time is an important consideration, and in situations where there are customers with strict deadlines, then using OOP and frameworks definitely is acceptable, or even unavoidable.  I am merely talking about best case scenarios where I have full control over how a site is made.

 

Also, as you know Hartley San and I are good friends and often work together on sites.  As you are well aware he is a very intelligent developer, and he has definitely influenced my coding practices in many ways, but please don't think at all that he has coerced me in any way in my attitudes towards OOP and frameworks.  I think the one reason we get along so well is that from the very beginning we shared the same overall attitude towards the increasing, and usually unnecessary, overcomplexity that exists in modern web development.  With the heavy reliance on frameworks like JQuery, Codeigniter, Doctrine, etc... nobody really knows how to code any more!  Who needs to though?  The framework will do all the thinking for me.  And even if I wanted to, I don't have any time because it's all spent learning 10 different framework APIs.  OOP is a different problem, but it all leads to the same result, over complex code and bloat!

Link to comment
Share on other sites

The point is that you are creating all this code and using all this processing in a situation that I feel doesn't really need it in the first place. And all to do what, to make it easier for the developer!

 

I don't know. I think that's a REALLY compelling reason. I'm not saying I'd use or recommend using OOP and/or a framework on every project. But on any site of moderate or greater complexity, I'd consider it borderline foolish to not adopt OOP and/or a framework. Just my opinion, of course. 

 

The comparison here is between performance on the one side, and developer time, security, maintainability, reliability (fewer bugs), upgrade-ability on the other. When making that comparison, what price do you put on being able to build the site 4x faster? Or knowing it has no bugs because it's been unit tested? Or having it be more secure because the code base has been established and hardened? Or knowing that you'll be able to make changes easily in the future? 

 

And, again, through caching, more memory, load balancers, etc., you can easily (and relatively cheaply) improve the performance, making it a non-problem. So now the comparison is between a non-problem and many serious and expensive problems.

 

Or, going the other way, if performance is the sole or just primary consideration, we shouldn't be using PHP at all, we should just have static HTML pages. But that's ridiculous. So we sacrifice some performance to have dynamic functionality, just like OOP sacrifices some performance to have faster developer time et al. 

 

My rule of thumb would be that simpler projects (e.g., a dozen-ish scripts or fewer) and beginning to intermediate programmers are prudent to use a procedural approach. More complex projects and more advanced programmers should generally be differing to an OOP approach.

Link to comment
Share on other sites

I just wrote up a long response in reply to your points, Antonio and Larry, but the more I read and re-read my write-up, the more I realized that my thoughts boiled down to one, underlying question:

Why does OOP make bigger, complex projects easier to maintain?

 

I ask because I have worked on some massive PHP projects, and I have yet to ever see a case where OOP simplified things.

On the flip side, in every case I've ever used or seen OOP in PHP, I feel like it's added a lot of bloat to the code, and by direct correlation, dev time.

Certainly, if you don't use OOP, then you need some logical way of organizing your code, but that to me does not imply that OOP is better.

 

So, to keep this discussion going and interesting, please tell me specifically how OOP makes bigger/complex projects easier than if procedural code is used.

 

Thanks.

Link to comment
Share on other sites

Good question. And I love when assumptions are challenged. I think the simple answer is that OOP is theoretically easier to maintain because good OOP obliterates the need to watch context. Responsibilities are made atomic and independent enough that you can change X without affecting A, Q, R, and Y. A well-written procedural function will do this to some degree, too, but not as completely. And procedural code is inherently context-sensitive. 

 

Second, OOP does a better job reducing redundancies. 

 

The caveat to these statements are that it assumes the OOP is done well. Also, in terms of the development time, in the real world, OOP is easier/faster to maintain and work with *once you know the code*. Before you're really comfortable with the code, an OOP project can be X times slower to work with and maintain because it's just not as easy to look at and get.

Link to comment
Share on other sites

Thanks for the response, Larry.

I think those are fair statements, but I still feel like the same thing can be easily accomplished with procedural code.

 

Of course, with procedural code, you have to be more disciplined with namespacing, the hierarchy you set up with helper functions, etc. (because the code won't automatically do it for you like OOP does), but I think that the benefit of procedural code (like you eluded to) is that it is easier to learn, quicker to write, and less bloated.

 

Really, when it comes down to it, I think that well written code, whether it be OOP or procedural, is going to be quite similar in structure, but with that said, I just find procedural to be less time-consuming and less bloated, which is why I tend to prefer it (at least in PHP and what I use PHP for).

 

Anyway, thanks a lot for sharing your thoughts.

Link to comment
Share on other sites

I would definitely agree that well-written code, regardless of OOP or procedural, will have some of the same qualities. And given a choice, I'd vastly prefer to cope with terrible procedural code than terrible OOP. 

 

And although I wouldn't say OOP is more "bloated", it clearly requires a lot more code.

 

But at the end of the day, I think it's a great thing that PHP supports both approaches and choosing the route that works best for you and your projects is not only possible, but what I would recommend. So "which is why I tend to prefer it" is the ultimate decision maker. 

  • Upvote 1
Link to comment
Share on other sites

  • 1 year later...

Hi, I have a query. I am making use of this class in a framework. I keep experiencing a problem on my local development machine (running windows). The problem has never occurred after I have uploaded the site to my hosting servers (Linux).

 

Basically I get my user to login. The Session is started and information stored correctly for the User class. However when I try and access the Session from another class it does not exist/contain the information. This problem is intermittent. It does not always occur and I have not been able to replicate it on purpose.

Normally leaving the user logged in until the session times out clears what ever is causing the issue... but not always. Does anyone know why this may be happening? (I have tried setting the session to last for more than 24 hours so I do not think it is a time related issue)

Link to comment
Share on other sites

Cookies can be unreliable when working locally, so my inclination is that your session cookies aren't being maintained which means the associated session data is not being found. You can confirm by watching the value of the session ID to see if it changes. 

Link to comment
Share on other sites

 Share

×
×
  • Create New...