Log in

View Full Version : php function trouble



rbush
07-19-2013, 03:09 PM
I am trying to create a function to load a file only once, then I want to call it multiple times in my code but it doesn't seem to be working.
here is my function:


<?php
function load_sw_popup($xml){
$xml=simplexml_load_file("sw-popup.xml");
}
?>

an example of one of the places I am calling it:


<?php
load_sw_popup($xml);
echo $xml->recentPRs;
?>

jscheuer1
07-19-2013, 03:26 PM
By declaring $xml inside a function, I believe it's only available inside that function. If your desire is to create a GLOBAL, don't use a function. Instead just do:


$xml=simplexml_load_file("sw-popup.xml");

Then later for your other code, just do:


<?php
echo $xml->recentPRs;
?>

I think there are ways to have the function make a GLOBAL though, check global in the man:

http://php.net/manual/en/language.variables.scope.php

It shows how to take a GLOBAL and act on it in a function. It might be able to be used to create a GLOBAL in a function. But, as I said, if you want a GLOBAL, the easiest thing is to just not use a function to create it. I think this would do it though:


<?php
function load_sw_popup(){
$GLOBALS['xml']=simplexml_load_file("sw-popup.xml");
}
?>

Then later:


<?php
load_sw_popup();
echo $xml->recentPRs;
?>

rbush
07-19-2013, 05:24 PM
Thanks for your help!

Nile
07-20-2013, 02:37 AM
Actually, better than using a global variable (which is normally advised against), it's probably better just to use a reference.


<?php
function load_sw_popup(&$xml){
$xml = simplexml_load_file("sw-popup.xml");
}
?>

jscheuer1
07-20-2013, 03:42 AM
Nile, I may be missing something but I just tested that and it appears to simply be another syntax for creating a global variable:


<?php
function load_sw_popup(&$xml){
$xml='bob';
}
load_sw_popup($xml);
echo $GLOBALS['xml']; //echoes bob
?>

djr33
07-20-2013, 06:17 AM
It's a fairly complicated topic:
http://php.net/manual/en/language.references.pass.php
The basic idea: you can send a variable into a function and actually manipulate it in that function. It's sort of like saying "pretend this isn't a new scope". But it's not global, because that can be within a few levels. This becomes relevant if you call functions from within functions, or, often, with OOP.

It's not equivalent. You'll see that no longer works when embedded:

<?php
function load_sw_popup(&$xml){
$xml='bob';
}
function savebob() {
load_sw_popup($xml);
}
savebob();
echo $GLOBALS['xml']; //echoes nothing, or gives an error
?> (You were coincidentally already in global scope. This is an example of why scope can be confusing.)

Global variables should be used for system settings, for something that should always be set. (One problem can come from mixing in third-party scripts, in case of a name conflict, but that situation is relatively rare, not that it's easy to fix if it happens.)
Global variables should not be used just to get a value from one thing to another. Personally I strongly prefer to use return to do all of this if possible. I don't really like using 'reference' unless required, and it usually isn't.

jscheuer1
07-20-2013, 06:46 AM
It is equivalent for the example as first presented by Nile. In your example $xml isn't accessible anywhere I can see. You would have to make a getter function and/or $xml would have to be assigned as a property of something in order to be both accessible and not global.

In my previous post I was going to add something like:

To avoid using global variables you have to carry out whatever you are doing within the context of an object or function.

In other words, just using &$whatever doesn't prevent a global variable from being created. It's the context or scope of the function or object that determines that. But that wasn't the original question. Essentially the OP had created $xml in a limited local scope and was wondering why it wasn't available in the global scope.

djr33
07-20-2013, 07:21 AM
$xml would be available within the savebob() function. That's why it's not global. But it's the same $xml variable then within savebob() and also load_sw_popup() because it's sent by reference to load_sw_popup().
I was just replying to your reply to Nile to clear that up.


As for the original question, I think it would be solved most simply like this:

<?php
function load_sw_popup(){
return simplexml_load_file("sw-popup.xml");
}
?>

<?php
$xml = load_sw_popup();
echo $xml->recentPRs;
?>

Simple as that. At least that solves the immediate problem.

traq
07-20-2013, 08:13 PM
If part of the original question is loading the file only once, here's how you could modify Daniel's function to do it:


<?php

// this causes the function to load the xml file
$xml = load_or_get_sw_popup();

// this causes the function to return the previously loaded xml file
// (it does _not_ load it again)
$xml2 = load_or_get_sw_popup();

function load_sw_popup(){
// static variables persist (are not destroyed) between function calls
static $xml;
// if this is the first time we call the function, $xml will be NULL
if( ! $xml ){
// so we load the file
$xml = simplexml_load_file("sw-popup.xml");
}
return $xml;
}

jscheuer1
07-21-2013, 01:11 AM
If part of the original question is loading the file only once

Yes, I was wondering that too.

Nile
07-26-2013, 11:12 PM
It is equivalent for the example as first presented by Nile. In your example $xml isn't accessible anywhere I can see. You would have to make a getter function and/or $xml would have to be assigned as a property of something in order to be both accessible and not global.

That's true for the specific example I provided, however we can't assume the context in which the OP will be using the code we provide. As a general rule, it's better not to clutter any global scopes. My initial reaction to this thread and your response was that using a reference would be better than using a global variable unless there are sophistic intentions that the OP didn't mention. My post wasn't a direct response to what the OP wanted, but instead just a (in my opinion) better alternative to using a global variable within load_sw_popup()'s scope.

Generally, though, I do agree with djr33 that returning a value is in most times better than cluttering different scopes or using a reference (which can often result in confusion).

jscheuer1
07-27-2013, 01:12 AM
I will for now defer to both you and Daniel's take in PHP. In javascript I prefer to create scopes and/or assign values, objects, functions, etc. to a single object or function that gives accessibility from the global scope without polluting it with more than one variable or other named entity. Sometimes I prefer using no access to anything other than via an instance that's created for one or more page element(s), accessing it via that element(s) when needed. But that approach can be too limiting even in javascript and doesn't appear to have any direct equivalent in PHP, which never assigns anything to a page element (window, document or a tag). At least not that I know of.

traq
07-27-2013, 02:50 AM
I will for now defer to both you and Daniel's take in PHP. In javascript I prefer to create scopes and/or assign values, objects, functions, etc. to a single object or function that gives accessibility from the global scope without polluting it with more than one variable or other named entity. Sometimes I prefer using no access to anything other than via an instance that's created for one or more page element(s), accessing it via that element(s) when needed. But that approach can be too limiting even in javascript and doesn't appear to have any direct equivalent in PHP, which never assigns anything to a page element (window, document or a tag). At least not that I know of.

You can scope variables like that in PHP, but you have to get into object-oriented programming to do it. However, using objects to "scope" variables and functions isn't as convenient (or efficient) in php as it is in js. It has a lot to do with php using classical inheritance (class definitions) rather than prototypical inheritance (classes based on a literal, existing "prototype" object). Also with the fact that objects are basically an afterthought in php.

Not counting objects, you basically have two scopes in PHP: global and function, and they're largely exclusive. From a practical standpoint, it's not possible to avoid "littering" the global scope.

djr33
07-27-2013, 06:28 AM
You can scope variables like that in PHP, but you have to get into object-oriented programming to do it. However, using objects to "scope" variables and functions isn't as convenient (or efficient) in php as it is in js. It has a lot to do with php using classical inheritance (class definitions) rather than prototypical inheritance (classes based on a literal, existing "prototype" object). Also with the fact that objects are basically an afterthought in php.Hm, that makes sense, but I would have never thought of it that way. So to respond to John, there is a clear difference both in the design of PHP and JS, and how programmers of each think about designing these sorts of operations. Interesting.


Not counting objects, you basically have two scopes in PHP: global and function, and they're largely exclusive. From a practical standpoint, it's not possible to avoid "littering" the global scope. True. In order to avoid that, you'd need to create a main function to host the whole process. There's nothing technically wrong with that, but it would be kind of weird. (A relatively un-invasive way to do with would be by having a function that includes another page, then putting all of your code in there. Then the initial page would just pick a page, and also put it in a function-limited scope. Odd, but possible.)

The result for PHP is that in order to avoid naming conflicts, we can do three things:
1. Use prefixes on variables, like $dd_foo and $dd_bar if we're working on something related to Dynamic Drive. This isn't foolproof, of course, because it just works on the basis of hoping that all prefixes will be the same. (The logic is equivalent to database table naming.)
2. [My favorite] Use arrays to store specific-topic values, such as $dd['foo'] and $dd['bar']. Assuming you know that $dd is available, now you can store everything you need in that. For example, when working with login systems, I often have something like $user available in the global scope, but lots of 'variables' inside it (as an indexed array).
3. Use constants instead of variables. These are just like variables (but without the $) except that they can never be changed. The benefit of this is that you will get an error if you try to change them, so you can never have a naming conflict-- or, more accurately, you'll know if you have a naming conflict. These are often used in third-party software (eg, forums) where something like __DBNAME__ might be defined, etc. (The __ prefixes are used to further differentiate them.)