I need to access $layers from inside the function isOk($layer), but everything i tried the outside function variable $layers is ok, but inside the function, even with global is return null.
Here is the code:
$layers = array();
foreach($pcb['PcbFile'] as $file){
if(!empty($file['layer'])){
$layers[$file['layer']] = 'ok';
}
}
// Prints OK!
var_dump($layers);
function isOk($layer){
global $layers;
// Not OK! prints NULL
var_dump($layers);
if(array_key_exists($layer, $layers))
return ' ok';
return '';
}
// NOT OK
echo isOk('MD');
I always use Object orientation, but this was something so simple that i made with a simple function... Why $layers is not being 'received' correcly inside the function?
Watch this...
HalfAssedFramework.php
<?php
class HalfAssedFramework {
public static function run($path) {
include $path;
}
}
HalfAssedFramework::run('example.php');
example.php
<?php
$layers = array();
foreach($pcb['PcbFile'] as $file){
if(!empty($file['layer'])){
$layers[$file['layer']] = 'ok';
}
}
// Prints OK!
var_dump($layers);
function isOk($layer){
global $layers;
// Not OK! prints NULL
var_dump($layers);
if(array_key_exists($layer, $layers))
return ' ok';
return '';
}
// NOT OK
echo isOk('MD');
Run example.php directly, and it should work. Run HalfAssedFramework.php, and it won't.
The problem is scope. When example.php is included inside the run function, all the code inside it inherits the function's scope. In that scope, $layers isn't global by default.
To fix this, you have a couple of options:
If you know example.php will never run directly, you can say global $layers; at the beginning of the file. I'm not sure whether this will work if the script is run directly, though.
Replace $layers with $GLOBALS['layers'] everywhere.
Add $layers as an argument to isOk.
Put this code within a class, as suggested by Geoffrey.
Not exactly answering the question, but have you considered not using globals? Globals really aren't that cool: they make your code harder to read, harder to understand and as a consequence, harder to maintain.
Consider something like that:
<?php
class LayerCollection
{
private $layers;
public function __construct($layers)
{
$this->layers = $layers;
}
public static function fromPcbFile($data)
{
$layers = array();
foreach ($data['PcbFile'] as $layer) {
if (!empty($layer)) {
$layers[$layer] = true;
}
}
return new self($layers);
}
public function hasLayer($layer)
{
return array_key_exists($layer, $this->layers);
}
}
$layers = LayerCollection::fromPcbFile($pcb);
var_dump($layers->hasLayer('MD'));
Doesn't it look better? You can then proceed to enrich LayerCollection as you need more ways of interacting with your layers. This is not perfect, since there's still a static method lying around (makes testing harder, a factory would be better suited for that job), but that's a good start for a refactoring.
More about why globals are evil: https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
Related
Good day. I'm trying to execute a function. i declare a global variable to get data (variable) outside the function, and the function i put inside a public function of a Class.
class Test {
public function execute(){
$data = "Apple";
function sayHello() {
global $data;
echo "DATA => ". $data;
}
sayHello();
}
}
$test = new Test;
$test->execute();
The expected result:
DATA => Apple
The real result:
DATA =>
the global variable is not getting the variable outside the function. Why it happened? Thank you for the help.
$data is not a global variable. It's inside another function, inside a class. A global variable sits outside any function or class.
But anyway your use case is unusual - there's rarely a need to nest functions like you've done. A more conventional, logical and usable implementation of these functions would potentially look like this:
class Test {
public function execute(){
$data = "Apple";
$this->sayHello($data);
}
private function sayHello($data) {
echo "DATA => ". $data;
}
}
$test = new Test;
$test->execute();
Working demo: http://sandbox.onlinephpfunctions.com/code/e91b98bb15fcfa71b1c6cbbc305b5a93df678e8b
(This is just one option, but it's a reasonable one, although since this is clearly a reduced, abstract example, it's hard to be sure what your real scenario would actually require or be best served by.)
I have this code (Is more complex, but I limited it to some lines).
function callfunction($data = "") {
$isdata = true; // Data already set Flag
if ($data == "") {
$isdata = false;
$data = randomLettersString(10);
}
if ($isdata) { // If data was set, environment files are needed.
/*
... Prepare environment files
*/
function cleanup() {
global $data;
/*
... test $data and cleanup environment files.
*/
}
register_shutdown_function('cleanup'); // Force cleanup
}
$ret = shell_exec(/* script that has chances of hanging */);
if($isdata) { // If data was set, environment is cleaned.
cleanup();
}
return $ret;
}
As you can see, I have a function that is called somewhere else in my code, and that function can have some $data. If the function is called with some $data, that means that, before calling the shell_exec script, the environment has to be prepared (some database calls, some files creation and such things).
The script is an script that calls some external APIs, so there's a chance that the script hangs or whatever (very rarely will happen, but as long as I don't have the control of those APIs, at least I want to have the control if the script times out for whatever reason).
Now, I'm not sure how can I access $data from the nested function. I have global $data; but I'm not sure, since $data is not a global variable but local variable inside the function.
Also, if the script does not hang and cleanup() is called after it, I supose that altough cleanup() is declared inside an if condition, it persists until the function ends so I can call it in another if condition, that's how PHP works, right?
And now the thing that scares me. If PHP works in a manner where every variable will last until the function ends, then if the code times out and PHP calls register_shutdown_function('cleanup'), then the nested cleanup() function will be called inside the context of the main callfunction() function, right? so, global $data will point to the data inside the function.
PHP does not have a concept of "nested functions".
function foo() {
function bar() {}
}
bar(); // error, bar not declared
foo();
bar(); // success!
This does not in any way mean that the functions are nested or that their scope is shared. Merely the function declaration of bar is nested inside foo. That means the function will not be declared until foo is executed. That's the same as this:
if (false) {
function bar() {}
}
bar(); // error
Once you execute foo, the function bar will be declared. Globally. Should you ever try to run foo again, PHP will try to redeclare bar and fail with an error.
I hope this explains the basic concept. That also means there's no special affordances for scope; bar does not have any more access to foo's scope than if you'd write its declaration outside of foo.
The exception here are anonymous functions:
function foo() {
$data = ..;
$bar = function () use ($data) {}
}
You can extend scope of selected variables into anonymous functions using use. This function is now truly scoped to within foo and can also be declared as many times as you want.
Having said all this, I'm not entirely sure what you're trying to do there, but this hopefully helps already.
PHP scopes are a little more tricky. Try this:
// define the $data var outside ('globally')
// if this is a class you could do
// public static $data; and access it static::$data from within its class
// or ClassName::$data from outside its class
var $data;
// then define the cleanup function outside
function cleanup(){
global $data;
}
// or if you are in a class
public function cleanup(){
// handle $data
var_dump(static::$data);
}
function callfunction($data = "") {
$isdata = true; // Data already set Flag
if ($data == "") {
$isdata = false;
$data = randomLettersString(10);
}
// if in class, we would need to push $data into the static var
//static::$data = $data;
if ($isdata) { // If data was set, environment files are needed.
/*
... Prepare environment files
*/
register_shutdown_function('cleanup'); // Force cleanup
// or if in a class
// register_shutdown_function(array('ClassName', 'cleanup'));
}
$ret = shell_exec(/* script that has chances of hanging */);
if($isdata) { // If data was set, environment is cleaned.
cleanup();
// or if in a class
// static::cleanup();
}
return $ret;
}
There is another approach (I'm not able to test it at the moment) it depends on PHP 5.3+, it makes use of PHP's closures. Note: This could also require pulling the cleanup function outside of the callfunction.
function callfunction($data = "") {
$isdata = true; // Data already set Flag
if ($data == "") {
$isdata = false;
$data = randomLettersString(10);
}
if ($isdata) { // If data was set, environment files are needed.
/*
... Prepare environment files
*/
function cleanup($data) {
// no need for globals here, hooray!
/*
... test $data and cleanup environment files.
*/
}
// set the callback as a PHP closure and allow it to use the $data var
register_shutdown_function(function() use ($data){
cleanup($data);
}); // Force cleanup
}
$ret = shell_exec(/* script that has chances of hanging */);
if($isdata) { // If data was set, environment is cleaned.
cleanup($data);
}
return $ret;
}
Let us know which approach works so that I can update the answer accordingly!
I have a loop which will make calls to the function. Variables are defined (and reassigned on each iteration) in the first loop which are required for the function to function.
Loop:
if ($something) {
while (!$recordSet->EOF) {
$variable1 = TRUE;
$variable2 = FALSE;
...
function1()
}
}
Function:
function function1() {
if ($variable1 && !$variable2) {
...
}
}
The variables will have boolean values, and the environment is limited to PHP 4.
I'm currently considering using global $variable1; in the while loop and function1, but I know globals are almost always frowned upon.
Usually I'd use define("variable1","a value"), but the values will be changed multiple times.
Any suggestions, or is global defining the best solution in this case?
Thanks.
EDIT: Totally forgot to mention. This file is actually a spaghetti legacy code, and function1 is called in a hundred different places, all with varying bits of information. Otherwise, I would have used arguments.
In the main scope, define global $variable1 and global $varible2.
Also do it in the function.
But this is the worst solution. You will confuse, if these variables changes somewhere else.
The best way I think is to refactore your code, and pass varables as parameters.
The other solution could be create a class for these 2 variables, and set/get them statically.
class variablesPlaceHolder {
private static $variable1;
private static $variable2;
public static function getVariable1() {
return self::$variable1;
}
public static function getVariable2() {
return self::$variable2;
}
public static function setVariable1($variable1) {
self::$variable1 = $variable1;
}
public static function setVariable2($variable2) {
self::$variable2 = $variable2;
}
}
And the include this class in file, where you want to use them, and call variablesPlaceHolder::setVariable1(anyValue) variablesPlaceHolder::getVariable1()
It may sound silly but I'm quite php outdated/unexperienced and coming from Java programming back to php, so I mix up the concepts.
If in a php webpage I declare a variable
$debug=TRUE;
and try to access it from below within a function
a(){
if ($debug){
echo "I'm here";
}
}
the variable doesn't exists or isn't initiated? The whole file is just simply:
<?php
$debug=TRUE;
a(){
if ($debug){
echo "I'm here";
}
}
?>
Do I need to make a session variable or something else? I'm quite clueless & the same concept is confusing me for the use of other variables within. Also for the further use of variables, I am trying to be forced to pass all the variables I need forward to the function where I use them and a class concept as in Java perhaps would be cleaner but is a kind of too much for this simplicity. Or do I need the functions (it's a form processor) to be declared as a class?
I know this is silly, but I looked through Google and forums and the problem seems to be so obvious and simple that it's hard to find a webpage or entry targeting this (or perhaps I'm asking the wrong question).
http://php.net/manual/en/language.variables.scope.php
<?php
$debug = TRUE;
function a() {
global $debug;
if($debug === TRUE) {
echo "I'm here....\n"
}
}
Better, instead of using globals you can pass it in as a parameter:
<?php
$debug = TRUE;
function a($debug = TRUE) {
if($debug === TRUE) ....
}
You can also use the $_GLOBALS array:
<?php
$debug = TRUE;
function a() {
if($_GLOBALS['debug'] === TRUE) ...
}
You can use constants, which are always in scope:
<?php
define('DEBUG', TRUE);
function a() {
if(DEBUG === TRUE) ...
}
You can also use a singleton:
function a() {
if(SingletonClass::get_instance()->debug === TRUE) {
...
}
}
You'll have to create a singleton class which extends StdClass() to get implicit ->get and ->set methods.
http://www.talkphp.com/advanced-php-programming/1304-how-use-singleton-design-pattern.html
did you want something like this:
<?php
$debug=TRUE;
function a($debug){
if ($debug){
echo "I'm here";
}
}
a($debug);//outputs "I'm here"
?>
A few things here a(){} isn't defined as a function
function a(){
}
Next, you shouldn't try use globals unless you absolutely want them. But, you could
$debug = TRUE;
function a(){
global $debug;
if($debug)
{
echo "it is";
}
}
then call a() whenever you want to check it.
I must say I don't think this is a great practice in how you are trying to debug.
Pass variables to a function, like this:
$debug = true;
function a($debug) {
var_dump($debug);
}
a($debug);
// Outputs bool(true)
i have alias to include() in my functions.php file:
function render_template($template)
{
include($template);
}
and simple template.html :
Hello, <?php echo $name ?>
But unfortunately, alias function gave me this output:
require 'functions.php';
$name = "world";
include('template.html'); // Hello, world
render_template('template.html'); // PHP Notice:Undefined variable: name
why is that? and how i can fix this?
thanks.
You have two more options to make it work around the scope issue. (Using global would require localising a long list of vars. Also it's not actually clear if your $name resides in the global scope anyway and always.)
First you could just turn your render_template() function into a name-resolver:
function template($t) {
return $t; // maybe add a directory later on
}
Using it like this:
$name = "world";
include(template('template.html'));
Which is nicer to read and has obvious syntactical meaning.
The more quirky alternative is capturing the local variables for render_template:
$name = "world";
render_template('template.html', get_defined_vars());
Where render_template would require this addition:
function render_template($template, $vars)
{
extract($vars); // in lieu of global $var1,$var2,$var3,...
include($template);
}
So that's more cumbersome than using a name-resolver.
The variable $name is not visible at the point where include is called (inside function render_template). One way to fix it would be:
function render_template($template)
{
global $name;
include($template);
}
Its a Scope Problem, you can set the variable to global, or encapsulate the whole thing a little bit more, for example like that:
class view{
private $_data;
public function __construct(){
$this->_data = new stdClass();
}
public function __get($name){
return $this->_data->{$name};
}
public function __set($name,$value){
$this->_data->{$name} = $value;
}
public function render($template){
$data = $this->_data;
include($template);
}
}
$view = new view;
$view->name = "world";
$view->render('template.html');
template.html :
Hello <?php print $data->name; ?>
If $name is in the global scope then you can get around it by declaring the variable global with the function. But a better solution would be to re-architect the code to require passing the relevant variable value into the function.
This is expected.
Look here.
You would be interested in the following quote, "If the include occurs inside a function within the calling file, then all of the code contained in the called file will behave as though it had been defined inside that function. So, it will follow the variable scope of that function. An exception to this rule are magic constants which are evaluated by the parser before the include occurs".