8

Using globals makes your code hard to test thus more prone to bugs, not secure and unpredictable. That's why we pass the variables we want inside a function/object. So my question is simple:

Do $_POST, $_GET, etc violate the encapsulation principle?

I'm thinking that, to retain control of those variables in an OO way, an ideal solution would be to add some lines like this to the code:

// Convert the $_GET array to an object
$get = json_decode(json_encode($_GET), FALSE);  // stackoverflow.com/a/1869147
// Stop it from being included from anywhere
unset($_GET);

// Small example of what could be done later on
$DB = new PDO(/* ... */);
$Person = new Person($DB, $get->id);

I haven't seen this anywhere, not even a tutorial nor recommendation. Also, we can clearly see how the code above is much easier to be tested than one that includes $Person = new Person($DB, $_GET['id']); or even (the ugly) $Person = new Person($DB); as you can use a mock $get object.

Is the code above in the right direction or am I missing something?

EDIT: After some investigation (Zend framework and Cake PHP) as Alexander Kuzmin suggested, it seems to be the right thing to go. They're probably too big for me to dig into the code ATM, but I'll keep it in mind.

migrated from stackoverflow.com Nov 17 '13 at 7:25

This question came from our site for professional and enthusiast programmers.

  • 1
    Encapsulation always seemed silly to me to adhere to as a "principle". It's just a feature; either a language has it, or it doesn't. – Ignacio Vazquez-Abrams Nov 12 '13 at 11:45
  • I'm not native so I wasn't sure how to call it and ended up writing principle. Please feel free to edit the title/text if you see it could have a better wording. – Francisco Presencia Nov 12 '13 at 11:47
  • 6
    This is smart and also the way most PHP frameworks solve it. So yeah, I think you're onto something. – Alexander Kuzmin Nov 12 '13 at 11:48
  • I was just investigating there, @AlexanderKuzmin ! It seems to be the best, updated. – Francisco Presencia Nov 12 '13 at 11:50
  • Can I ask why does it have the opinion based close tag? While the second sub-question I agree it's opinion based (but just for the specific example), I think the main question is valid. – Francisco Presencia Nov 12 '13 at 11:51
8

I’m not quite sure why you apply json_decode to $_GET to “convert it to an a array”; $_GET already is an array.

Using the super-globals ($_GET, $_POST etc) is a violation of the encapsulation principle. But there has been to be a line drawn where you stop encapsulating things. Request data is a good candidate for encapsulation, but don’t get sucked down the rabbit hole of trying to encapsulate all the things.

Most frameworks usually wrap PHP’s super-globals into some form of request object. Doing this then makes it easier to mock for tests etc. The simplest approach would be:

<?php
class Request
{
    public $get;
    public $post;
    public $session;
    public $cookie;

    public function __construct($get, $post, $session, $cookie)
    {
        $this->get = $get;
        $this->post = $post;
        $this->session = $session;
        $this->cookie = $cookie;
    }
}

$request = new Request($_GET, $_POST, $_SESSION, $_COOKIE);

It’s simple and rudimentary, but does the job. It’s also advisable to filter the data at this point, to defend against XSS injections.

But it’s wrapped in a Request object. The Request object has four arrays, and these arrays can easily be mocked:

$get = array(
    'foo' => 'bar'
);
$post = array();
$session = array(
    'user' => 1
);
$cookie = array();

$request = new Request($get, $post, $session, $cookie);
  • I'm trying to convert them to objects, as stated in the comment // Convert the $_GET array to an object. Furthermore, this answer stackoverflow.com/a/1869147 is why I'm doing it. That small detail apart, thank you so much for such a complete answer with extra tips, that's very similar to what I intended to do. – Francisco Presencia Nov 12 '13 at 15:37
  • 2
    I wouldn’t convert $_GET data to an object. Arrays aren’t dirty. I feel people shy away from arrays because they feel they’re “not OOP”, just like no one would dare use a <table> in HTML for fear of being un-semantic, even with tabular data. Take the $_GET array. What happens if I pass array data from my form, i.e. <input type="checkbox" name="foo[]" /> or <select name="bar[]" multiple="multiple">? Are you going to convert them to objects or leave them as is? Just leave the $_GET array as an array, as intended. – Martin Bean Nov 12 '13 at 15:47
  • I thought also about that case with multiple level arrays, and I assumed that it would make them sub-objects, but assumptions are not good. I converted it (dirty so far) to an object to be able to modify the getter and setter which would give me more flexibility to, for example, check against XSS injection on the output, but that can be done in your proposed scheme also. – Francisco Presencia Nov 12 '13 at 16:03
  • Yeah, you may as well just leave them as multi-dimensional arrays. Arrays aren’t dirty and doesn’t make your code “non-OOP” if you use them as a data structure. Don’t be scared of them. If your data doesn’t have any methods or properties (like GET and POST data) then it probably doesn’t need to be an object. – Martin Bean Nov 17 '13 at 11:35
  • Bit of a side issue/niggle... I'd query the decision to include the $_SESSION in the request object, because of PHP's special handling of the $_SESSION superglobal (you'd need to implement getters/setters and make sure the session is started early etc.) and it's not really part of the "request" IMO. – MrWhite Nov 13 '14 at 2:18
2

Using superglobals $_{POST,GET,SERVER} or whatever surely violates the encapsulation.

This problem grows when you want do create "local requests" within the server side of you application as many frameworks do nowadays.

I'm not used to work with frameworkds, but what I usually do is to create a Request/Response pair at the beginning of my processing. The request contains the values these global parameters.

If I want to create a server-side subrequest, I have two options: use the current context or create a entirely new one. So, I think you shouldn't unset these superglobal variables because MAYBE you'd want do use them again. Also, for this reason, I disagree that request params should be singletons.

By containing only values, not references to these superglobals, a change in one Request object will never affect another, so, the problem with global state is solved.

So, basically, I have two options:

// Using global context
$request = new Request(array(
    'post' => $_POST,
    'get' => $_GET
));

// or creating a new context

$request = new Request(array(
    'post' => ['someKey' => 'someValue'],
    'get' => ['queryParam' => 'queryValue'],
));
0

POST- and GET-vars are sent to the server in one bulk and php has to make sense of them. In a way, it makes sense to have them available globally, so the developer can choose where to process them.

Many frameworks (like CakePHP for example) read the Parameters and then place them all in some array, object or similar structure. After that, they are handled like any other data and are passed to all methods needing them.

  • 1
    Do you mean it works that way because PHP cannot inject a local variable in the current main script like index.php? – Francisco Presencia Nov 12 '13 at 11:56
  • No, but providing the variables through global array gives more flexibility for implementing some form of parsing by the developer. – Lars Ebert Nov 12 '13 at 13:20
0

Encapsulation is a good principle when there's a chance that you might need multiple instances of something. But there's only one set of parameters to a web page. If they were in a class instead of global variables, they would probably be singletons. There's no significant improvement going from global variables to singleton classes, it's just a different syntax to access them. They're still inherently global objects, it's just more cumbersome because you have to get a handle on the class instance and pass it around.

Since these parameters are accessed so frequently, the PHP designers made a decision to make them easy to access, rather than adhere to rigid design principles. It's a good idea to make the most common operations convenient, otherwise programmers will curse you for making them retype the same, long thing every time.

  • While I agree about the frequent access to them, I don't agree with If they were in a class instead of global variables, they would probably be singletons since singletons are also in the global scope. I'm just saying about deleting altogether that global state and making it local, borrowing this idea from The Clean Code Talks - "Global State and Singletons". There is expressed my main concern at asking this question: testing. Unfortunately it seems like you didn't read the whole question but only the title – Francisco Presencia Nov 12 '13 at 12:17
  • 1
    My point is that since there's only one client connection and one set of parameters, these are inherently global. – Barmar Nov 12 '13 at 12:21
  • It's a tradeoff: convenience versus strict adherence to design principles. If you get rid of the superglobals, you'll have to pass $get around from function to function. – Barmar Nov 12 '13 at 12:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.