Is using superglobals directly good or bad in PHP?
Asked Answered
K

6

9

So, I don't come from a huge PHP background—and I was wondering if in well formed code, one should use the 'superglobals' directly, e.g. in the middle of some function say $_SESSION['x'] = 'y'; or if, like I'd normally do with variables, it's better to send them as arguments that can be used from there, e.g:

class Doer {
    private $sess;
    public function __construct(&$sess) {
        $this->sess =& $sess;
    }
} 

$doer = new Doer($_SESSION);

and then use the Doer->sess version from within Doer and such. (The advantage of this method is that it makes clear that Doer uses $_SESSION.)

What's the accepted PHP design approach for this problem?

Kittykitwe answered 16/8, 2010 at 23:24 Comment(2)
Move the data you need into variables, after validating them, as the data in there is suspect until validated.Koester
Your approach is uncommon, and aliasing the superglobal into a local attribute might confuse codevelopers. But it's certainly legal, and if your object utilizes or filters it (e.g. a config helper) probably a good idea.Outgoing
M
13

I do like to wrap $_SESSION, $_POST, $_GET, and $_COOKIE into OOP structures.

I use this method to centralize code that handles sanitation and validation, all of the necessary isset () checks, nonces, setcookie parameters, etc. It also allows client code to be more readable (and gives me the illusion that it's more maintainable).

It may be difficult to enforce use of this kind of structure, especially if there are multiple coders. With $_GET, $_POST, and $_COOKIE (I believe), your initialization code can copy the data, then destroy the superglobal. Maybe a clever destructor could make this possible with $_SESSION (wipe $_SESSION on load, write it back in the destructor), though I haven't tried.

I don't usually use any of these enforcement techniques, though. After getting used to it, seeing $_SESSION in code outside the session class just looks strange, and I mostly work solo.

EDIT
Here's some sample client code, in case it helps somebody. I'm sure looking at any of the major frameworks would give you better ideas...

$post = Post::load ();  
$post->numeric ('member_age');  
$post->email ('member_email');
$post->match ('/regex/','member_field');
$post->required ('member_first_name','member_email');
$post->inSet ('member_status',array('unemployed','retired','part-time','full-time'));
$post->money ('member_salary');
$post->register ('member_last_name'); // no specific requirements, but we want access
if ($post->isValid())
{
  // do good stuff
  $firstName = $post->member_first_name;
}
else
{
  // do error stuff
}

Post and its friends all derive from a base class that implements the core validation code, adding their own specific functionality like form tokens, session cookie configuration, whatever.

Internally, the class holds a collection of valid data that's extracted from $_POST as the validation methods are called, then returns them as properties using a magic __get method. Failed fields can't be accessed this way. My validation methods (except required) don't fail on empty fields, and many of them use func_get_args to allow them to operate on multiple fields at once. Some of the methods (like money) automatically translate the data into custom value types.

In the error case, I have a way to transform the data into a format that can be saved in the session and used to pre-populate the form and highlight errors after redirecting to the original form.

One way to improve on this would be to store the validation info in a Form class that's used to render the form and power client-side validation, as well as cleaning the data after submission.

Miniature answered 17/8, 2010 at 0:29 Comment(6)
Gimme! Would you publish your OOP wrappers for the input arrays? I'm using something similar, and would like to contrast the approach with something comparable.Outgoing
@mario: I can't publish the code, but I can elaborate a little to give you an idea. I'll edit the answer now.Miniature
that's alright. But mine is here for comparison: sourceforge.net/p/php7framework/wiki/input - just wanted to know which kind of utility features you use, or which filters you deem most useful.Outgoing
@mario: Cool. I really like the chaining feature. Thanks for sharing!Miniature
why not just sanitize the superglobals directly in their fields at start of the scripts then? all sanitization takes place at the same time, and you don't have to modify you data relations or invent singletons to emulate their global state.Offence
@FélixGagnon-Grenier: That's certainly one way to go, but note that this approach explicitly tries to get away from global state. It pulls a snapshot of the global state into a local variable that has predictable behavior and contents. After creating and validating the $post object, you can pass it around like any other data structure, so client code never looks at global state. Though it would be rare to have more than one in use at a time, it's definitely not a singleton.Miniature
H
4

Modifying the contents of the superglobals is considered poor practice. While there's nothing really wrong with it, especially if the code is 100% under your control, it can lead to unexpected side effects, especially when you consider mixed-source code. For instance, if you do something like this:

$_POST['someval'] = mysql_real_escape_string($_POST['someval']);

you might expect that everywhere PHP makes that 'someval' available would also get changed, but this is not the case. The copy in $_REQUEST['someval'] will be unchanged and still the original "unsafe" version. This could lead to an unintentional injection vulnerability if you do all your escaping on $_POST, but a later library uses $_REQUEST and assumes it's been escaped already.

As such, even if you can modify them, it's best to treat the superglobals as read-only. If you have to mess with the values, maintain your own parallel copies and do whatever wrappers/access methods required to maintain that copy.

Haveman answered 17/8, 2010 at 2:44 Comment(0)
G
4

I know this question is old but I'd like to add an answer.

mario's classes to handle the inputs is awesome.

I much prefer wrapping the superglobals in some way. It can make your code MUCH easier to read and lead to better maintainability.

For example, there is some code at my current job the I hate! The session variables are used so heavily that you can't realistically change the implementation without drastically affecting the whole site.

For example,

Let's say you created a Session class specific to your application.

class Session
{
    //some nice code
}

You could write something like the following

$session = new Session();
if( $session->isLoggedIn() )
{
   //do some stuff
}

As opposed to this

if( $_SESSION['logged'] == true )
{
   //do some stuff
}

This seems a little trivial but it's a big deal to me. Say that sometime in the future I decide that I want to change the name of the index from 'logged' to 'loggedIn'.

I now have to go to every place in the app that the session variable is used to change this. Or, I can leave it and find someway to maintain both variables.

Or what if I want to check that that user is an admin user and is logged in? I might end up checking two different variables in the session for this. But, instead I could encapsulate it into one method and shorten my code.

This helps other programmers looking at your code because it becomes easier to read and they don't have to 'think' about it as much when they look at the code. They can go to the method and see that there is only ONE way to have a logged in user. It helps you too because if you wanted to make the 'logged' in check more complex you only have to go to one place to change it instead of trying to do global finds with your IDE and trying to change it that way.

Again, this is a trivial example but depending on how you use the session this route of using methods and classes to protect access could make your life much easier to live.

Giffard answered 16/4, 2012 at 3:25 Comment(0)
G
1

I would not recommend at all passing superglobal by reference. In your class, it's unclear that what you are modifying is a session variable. Also, keep in mind that $_SESSION is available everywhere outside your class. It's so wrong from a object oriented point of view to be able to modify a variable inside a class from outside that class by modifying a variable that is not related to the class. Having public attribute is consider to be a bad practice, this is even worst.

Gabion answered 16/8, 2010 at 23:57 Comment(0)
I
0

I found my way here while researching for my new PHP framework.

Validating input is REALLY important. But still, I do often find myself falling back to code like this:

function get( $key, $default=FALSE ){
    return (isset($_GET[$key]) ? $_GET[$key]:$default);
}
function post( $key, $default=FALSE ){
    return (isset($_POST[$key]) ? $_POST[$key]:$default);
}
function session( $key, $default=FALSE ){
    return (isset($_SESSION[$key]) ? $_SESSION[$key]:$default);
}

Which I then use like this:

$page = get('p', 'start');

$first_name = post('first_name');
$last_name = post('last_name');
$age = post('age', -1);

I have found that since I have wildly different requirements for validation for different projects, any class to handle all cases would have to be incredibly large and complex. So instead I write validation code with vanilla PHP.

Indulge answered 25/1, 2021 at 17:48 Comment(0)
E
-3

This is not good use of PHP.

get $_SESSION variables directly:

$id   = $_SESSION['id'];
$hash = $_SESSION['hash'];

etc.

Euraeurasia answered 16/8, 2010 at 23:37 Comment(2)
And get a nice E_NOTICE if the variable doesn't exist in the array... -> -1Eichler
How do you get $_SESSION anyway?Euraeurasia

© 2022 - 2024 — McMap. All rights reserved.