homepage Welcome to WebmasterWorld Guest from 23.22.128.96
register, free tools, login, search, pro membership, help, library, announcements, recent posts, open posts,
Become a Pro Member
Home / Forums Index / Code, Content, and Presentation / PHP Server Side Scripting
Forum Library, Charter, Moderators: coopster & jatar k

PHP Server Side Scripting Forum

    
where's my variable?
if a=global and b=local, what's c?
lucy24




msg:4527222
 5:17 am on Dec 12, 2012 (gmt 0)

As we all know, I don't speak php. But this is a philosophical question rather than a "how to" question. Unless, ahem, something causes someone to scream You can't do it that way!

I've got a family of php includes that make navigation footers. Details vary by directory, but the basic pattern is

#1 declare some variables specific to this file (If anyone cares: populate one array, and set one number based on the size of the array. Definitely not enough to start mucking about with databases.)

#2 some if-and-then blahblah to make sure the following function will get passed a variable. (Kind and value don't matter, there just has to be something.) The variable comes from the file that did the including.

#3 one or two function(s) using the variable solidified in #2, and also the global variables from earlier

No problem so far. But then I've got another include following basically the same pattern, only this one is called by the function in the first include. (The variables have different names, though I'm pretty sure php doesn't really care.)

This does not work. The inner file gets included all right and tight, and the code executes happily-- including the function-within-a-function --EXCEPT the bits that require it to recognize the variables from #1. This part does not work. No way, no how.

Tentative explanation: They're not global variables, because they weren't declared outside the uber-function. And they're not local (i.e. non-global) variables, because they weren't declared within the immediate function that uses them.

So far I have found three ways to make it work. There are probably more.

First way is to mess with the outer include file so the part that calls the inner include isn't a function but just, uhm, raw text. Works but feels creepy. Things belong inside functions.

Second way is to-- UGH! --put all the variable declarations for the inner include inside of the function that uses them. Inverse creepy: some things don't belong inside functions.

Third way is to pass those #1 variables to the function along with the #2 variable. This I can live with, although it feels silly.

Philosophical question: Where are the blasted variables? If they're neither global nor local, what are they and how do I get at them?

 

swa66




msg:4527308
 10:18 am on Dec 12, 2012 (gmt 0)

Did you read the definitions of the scope of variables in PHP ?
Ref: [php.net...]

There is first of all no scope associated with a file. So you should just consider the include as transparent: the text is put in the source code where the include is for all the scope is concerned.

Next you have variables that are defined outside of a function: they are global.
Variable used in functions are all local (and there is no automatic access to global variables).
To get access from within a function to a global variable: you need to declare it inside the function as a global.

Once you get that, there are also static variables: they have a scope of a function, but they retain their value between calls to the function (a persistent local variable). They get their initial value compile time so you can't use expressions in setting the initial value.

For variables in child functions (function defined within a function): they too only have the option of global or local, there is NO option to give them the "parent" scope., So if you need that, you need to either pass it as a parameter to the child function or declare the variable global in the parent function.

lucy24




msg:4527328
 12:07 pm on Dec 12, 2012 (gmt 0)

Did you read the definitions of the scope of variables in PHP ?

I later found a place that described the exact situation I've got-- a variable defined in the non-function part of an included file that's called from within another function. It said, in essence, You Can't Do It. Without the expected follow-up of "Here's what you do instead." So I'm left trying to wrap my mind around the idea of a disappearing variable.

or declare the variable global in the parent function

--which kinda defeats the point of having an include file :) since I'd have to go back and edit the (currently) seven different files that use it. That's on top of the previous edit when I replaced seven almost-identical slabs of plain text (that is: echo this, echo that, echo the other) with a single call to an include.

I had to delve deep into the Comments section of the Includes tutorial page to find how to get at an include that doesn't live in the same directory as its calling file. Apparently it wasn't important enough to mention in the main how-to. And then how to pass a variable when you can't use a query. Think I had to go trawling message boards for that one. At least the answer came in the form "Nope, can't do it-- but you shouldn't need to, because..." Conclusion: it can use a variable that I set in a whole nother document in a different directory, but it can't see a variable that's right there under its nose. Who makes this stuff up anyway?

Can you slap a Global label on something you haven't defined yet?

swa66




msg:4527334
 12:23 pm on Dec 12, 2012 (gmt 0)

--which kinda defeats the point of having an include file

actually not:

Again the include files have no importance for the scope at all, and you don't even need to define a global variable at the global level, you can define it in those functions that want to use it (just be careful: it's also available outside of functions).

e.g.:

<?php

// no variable $a defined here
// you could use it, but as long as you don't there's no problem.

function foxtrot() {
global $a;
// can use $a with a global scope

// ...

function golf() {
global $a;
//can access the global variable a (just like it's parent can, so they both can)
}

// ...
}

// ...

function hotel() {
// no global here
$a=1;
// will not interfere with the global variable $a
function juliet() {
global $a;
// can use $a with a global scope, will not interfere with the parent's $a
}


}
?>


Can you slap a Global label on something you haven't defined yet?

Yes: you do it in every function that needs to access it (child functions too), you do not need to define it at all on the top level.

penders




msg:4527406
 4:38 pm on Dec 12, 2012 (gmt 0)

Or, to reference the global variable $a you can use
$GLOBALS['a'], instead of having to declare global $a inside the function.

Just thought I'd comment on the use of nested/child functions mentioned above... (Although I realise in this instance that this would seem to be a by-product of including a file inside a function that contains function definitions.) In PHP <5.3, whilst nested functions are syntactically OK, they aren't really supported and are best avoided if at all possible.

- Nested functions are declared in the global scope, the same as all other functions. So, there is no such thing as parent/child scope with respect to functions.

- The nested function is only declared when the outer/parent function is called. (This is more obvious when the file is included.)

- If the outer function is called a second time then the nested function is redeclared and you get a fatal error. (Unless you conditionally declare the nested function, or conditionally include your file-to-include - UGH, starting to get messy!)

- There is never a need to nest functions.

(Note I'm not talking about anonymous functions in PHP 5.3+)

g1smd




msg:4527449
 5:49 pm on Dec 12, 2012 (gmt 0)

I think lucy is a little bit off in understanding the relationship of "included" files to the "top level" script.

Imagine cutting the content of each one of the included files and pasting their content in place of the respective "include" directive that referred to it, to make one very long file with all the code in it.

There's no "passing of variables" to the include file. All the code is just there in one long line.

The only "passing of variables" is of that to and from functions.

coopster




msg:4527643
 4:15 am on Dec 13, 2012 (gmt 0)

If the functions were part of a class (in which they are then called methods) you can overload the getter/setter methods. I use this exclusively for exactly these situations -- except you need to know which class instance your potential variable was defined/set. Resource: [php.net...]

lucy24




msg:4527666
 6:06 am on Dec 13, 2012 (gmt 0)

Good page title, coopster, it overloaded my brain :)

--which kinda defeats the point of having an include file
actually not

I meant that as things currently exist, I'd have to backtrack and edit those seven outer files rather than the one inner file. And ideally, once something is set up as an include I will never need to look at what's outside it again. Unless, ahem, I decide to move into the present millennium and put site navigation at the top of the page where people expect to find it. Eeuw. Then everything would need to be edited.

I think lucy is a little bit off in understanding the relationship of "included" files to the "top level" script.

Well, I did say I don't speak php
:: whine ::
Comes from having a linguistics background. If the text I need happens to be written in Old French, I cannot let myself be deterred by the fact that I don't know Old French. And if I need something to be a little bit dynamic, and the only way to achieve this is with php, and I don't happen to know php, well, that's just tough ;)

Imagine cutting the content of each one of the included files and pasting their content in place of the respective "include" directive that referred to it, to make one very long file with all the code in it.

php equivalent of what really happens in the html?

Interesting experiment. It doesn't work literally, where "doesn't work" = the new fatter file simply pretends it doesn't exist, and "literally" = pasting the inner file in place of the "include" line. You have to pull out the inner function and park it outside. Then everything is copacetic.

In PHP <5.3, whilst nested functions are syntactically OK, they aren't really supported and are best avoided if at all possible.

:: detour here to look things up ::
MAMP uses 5.3. (There's a Prefs setting.) Two of my three live sites were inexplicably on 5.2 even though 5.3 is supposed to be the default. I have now changed them.

If the outer function is called a second time then the nested function is redeclared and you get a fatal error.

Does "a second time" mean within the same page? I foresee a lot of grief coming from the fact that some details of php vocabulary are identical to javascript... and some are not. But all of this is offset against manually editing 10 pages every time I change one thing in one directory.

you can define it in those functions that want to use it

That's one of those things that give me the creeps. A transitory $count here, a passing $newname there, sure. But populating an array inside of a function just doesn't look right. Matter of fact what I really wanted was named constants, but I couldn't find them.

And then there's the quibbling detail that technically none of this stuff needs to be structured as functions at all. The whole thing just executes once from beginning to end. But having all those processes flopping around loose without any kind of container ... brr.

g1smd




msg:4527716
 9:03 am on Dec 13, 2012 (gmt 0)


delete

[edited by: g1smd at 9:05 am (utc) on Dec 13, 2012]

g1smd




msg:4527717
 9:03 am on Dec 13, 2012 (gmt 0)

copacetic

::diverts to dictionary site:: Again. :)

swa66




msg:4527731
 9:33 am on Dec 13, 2012 (gmt 0)

Lucy,

You're obviously right to want to structure things. There's no doubt about that.

What I doubt is that nested functions are the answer you need. As was pointed out, they have a very peculiar meaning and come with quite some trouble of their own. I doubt they cover a real need to be honest (or I have missed it somehow).

I guess it's clear by now that include files are nothing more than a sort of automated "cut&paste": it's to make our life easier but it doesn't add semantical structure in your programming.

PHP comes with a solution for getting more structure where you use object oriented programming (OOP). Essentially instead of having include files that contain functions, (and that are all in one flat namespace & scope), there are classes you can create.

Now the basic concept is that you create a class. A class has it's variables and it's methods (functions). If done the right way, a class for widgets manages all that needs to be (can be) known about widgets and can be done to them.
Using this, you create a new instance of the class: an object.

If you follow this model of programming, you can take your code up to a new level. It uses a whole different way of thinking about programming and is without a doubt the more suitable approach for larger tasks. It can be used for smaller things too, but there the advantage you get is much less.
There's an obvious learning curve to this.

Ref: [php.net...]

penders




msg:4527972
 12:32 am on Dec 14, 2012 (gmt 0)

In PHP <5.3, whilst nested functions are syntactically OK...


Sorry, I probably wasn't too clear regarding PHP 5.3 and "nested functions". Nested functions (where a function is simply declared inside of another function) behave the same, regardless of which version of PHP. They behave as stated above.

Aside... What PHP 5.3 provides is something called "anonymous functions [php.net]" (or lambda functions, or closures) and these can be nested inside of an ordinary function and inherit the scope of the parent function in which they are defined - like closures in JavaScript. However, whilst these anonymous functions still use the function keyword and are defined in a similar way to regular functions, they do use a slightly different syntax (they are "anonymous" / no name) and are essentially a different animal. We are not talking "anonymous functions" here.

Does "a second time" mean within the same page?


Yes, although by the sounds of it you are probably only calling these functions once - to generate your footer(?) - so it's not going to be an issue.

The following would cause a problem (in any version of PHP)...

/* FILE: bar.inc.php */ 
function bar() {
// Do something...
}


function foo() { 
include 'bar.inc.php';
echo 'OK';
}
foo(); // "OK"
foo(); // Fatal error: Cannot redeclare bar()...

lucy24




msg:4527984
 1:55 am on Dec 14, 2012 (gmt 0)

by the sounds of it you are probably only calling these functions once - to generate your footer(?) - so it's not going to be an issue.

Exactly. I don't think it's physically possible to call a function twice in that situation. Unless the server gets the hiccups-- and isn't that what we have custom 500 pages for?

The following would cause a problem (in any version of PHP)...

You mean if fn bar has already been called in its own right before it gets called all over again from within fn foo? As opposed to

function foo() {
for $count = 1 to eight trillion [well, you know what I mean]
{ bar($count) }
}

function bar($stuff-here)
{ do-something-to-the-$stuff }

which it's perfectly happy with?

Heh. Would be a pretty feeble function if it could only be used once:
function foobar()
{ exit
or break
or self-destruct
or whatever the appropriate word is }

penders




msg:4528078
 12:53 pm on Dec 14, 2012 (gmt 0)

You mean if fn bar has already been called in its own right before it gets called all over again from within fn foo?


No. If you substitute "called" for "declared" then your statement is pretty much correct. In my example above, the fn bar is only declared when "bar.inc.php" is included, inside of the foo function (bar is not actually called at all in my example - although it could be). It is only included when the foo function is called. Until the foo function is called, the bar function does not exist. These nested functions (or included files) are not processed when the PHP is first parsed/compiled, only at runtime.

Your example is OK.

Would be a pretty feeble function if it could only be used once


Yes, it would. Which is why you need to be careful when functions get "nested".


...what I really wanted was named constants, but I couldn't find them.


If a constant is really what you require then yes, this could well solve your global/local scope problems. Named constants (created using the define() [php.net] function) are available in all scopes... global and local, without any messing around.

define('MY_CONSTANT','Hello World'); 
echo MY_CONSTANT; // "Hello World"
function foo() {
echo MY_CONSTANT; // "Hello World"
}


(In PHP 5 you also have class constants, created using the const keyword - but these are a slightly different animal.)

lucy24




msg:4528263
 10:55 pm on Dec 14, 2012 (gmt 0)

If a constant is really what you require

They're constants in the sense that their value is not going to change during the life of the page. That is, I could go off and add or rename some other page in the same directory, and then the relevant section of the include file would change, since that's what the include is for. But the first page wouldn't know this unless it re-loaded. At which point everything starts from scratch anyway. At least I hope it does.

penders




msg:4528547
 12:26 am on Dec 16, 2012 (gmt 0)

A defined constant (as mentioned above) would probably work OK here. They can be conditionally assigned any string (from an expression, another variable, anything), which cannot "change during the life of the page".

lucy24




msg:4530098
 3:31 am on Dec 21, 2012 (gmt 0)

Postscript:

They can be conditionally assigned any string

... but unfortunately constants can not be made into arrays. No way, no how. And one of my two global wannabes is an array. I found a place that showed how to fake it using serialize, resulting in a single long string that can be shoved into a constant, and then deserialized when you want to use it. This will be good to know in the future, but isn't worth it here.

To avoid having to pass a variable to a function, I can split the inner include in two. One part, containing all the variable declarations, goes at the beginning of the outer include-- or almost anywhere, so long as it's outside the function. (The other part has to stay where it is because it creates a single cell within a table.) Now everything can be called a global, no problem.

In case anyone ever got around to wondering: they are php includes rather than humble html includes because auto-linking drives me bonkers. I need the navigation list to show what page and directory you're currently in. And there comes a point where doing it by hand just doesn't cut it, even if everything else is hand-coded ;)

:: currently feeling stoked because some Canadian academic landed on one of my pages during a search, and ended up spending half an hour on the site, winding up with downloading a couple of games that had nothing to do with their original query, and in fact they never got near the page that would probably have answered their question, thank you very much google dot ca ::

g1smd




msg:4530139
 10:56 am on Dec 21, 2012 (gmt 0)

I usually have an include right at the beginning that defines as much stuff as possible.

The upside is that all the definitions are in one place. The downside is that the definitions might be a long way from where they are actually used.

swa66




msg:4530339
 3:06 am on Dec 22, 2012 (gmt 0)

FWIW, and this goes for OT ...

I need the navigation list to show what page and directory you're currently in. And there comes a point where doing it by hand just doesn't cut it, even if everything else is hand-coded


The easiest trick I ever used
to add a class or id on the body of every page to indicate where in the menu structure it needs to be

And then in the menu that's included everywhere via ssi add classes to every item you need to match them up with the right body.

e.g.:

index.html:

...
<body id="home">
...
<!--#include virtual="/menu.include"-->
...


menu.include:

<ul class="menu">
<li class="home"><a href="/">home</a></li>
<li class="services"><a href="/services/">services</a></li>
...
</ul>


services.html:

...
<body id="services">
...
<!--#include virtual="/menu.include"-->
...


in your css:

//style your menu without highlighting the current item
.menu li {
...
}
.menu li a {
...
}
//override on higher specificity the highlights
#home .home , #services .services , ... {
...
}
#home .home a, #services .services a, ... {
...
}


You can use other selectors etc. as you need, it's just an example...

lucy24




msg:4530342
 4:10 am on Dec 22, 2012 (gmt 0)

The easiest trick I ever used

Well, for a given definition of "easy" ;) since it seems to require editing the stylesheet every time you add a page. Most of my navigation includes use the simple query
?page=${DOCUMENT_NAME}
unless the document name is index.html which generally goes to a different file anyway. (Yes, it still works if the query has gone missing, or if there isn't one.)

You can style a link but you can't use CSS to omit it altogether. {display: none} or {visibility: hidden} won't do, since that would simply make the whole line invisible. Like those maddening street signs that name the cross street but assume you already know what street you're currently on.

Closest would be {color: inherit; text-decoration: none;} but even then the link is really still there, you just hope the user won't look too closely. If they really do want the current page all over again, that's what the browser's Reload button or the page's own Back To Top links are for.

swa66




msg:4530457
 5:21 pm on Dec 22, 2012 (gmt 0)

Well, for a given definition of "easy" since it seems to require editing the stylesheet every time you add a page. Most of my navigation includes use the simple query

I use it for dropdown menus and well then all that's highlighted is the top level menu, so true I need to edit the CSS when (iF) I create a new category ...

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Code, Content, and Presentation / PHP Server Side Scripting
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved