This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 9 years ago.
I am trying to get figure this out and haven't been able to. I'm trying to create a function that will include different config files into the script when needed. I want to store all the variables in arrays and then use a function to include them.
Example
Config.php
$array = array(
'var1' => 'something',
'var2' => 'something else'
);
uses.php
class uses{
public static function file($directory, $file){
if(is_dir($directory))
{
if(is_file($directory.$file))
{
include $directory.$file;
}
else
{
echo "File Not Found $directory$file";
}
}
else
{
echo 'Dir Not Found';
}
}
}
index.php after I've included uses the file
uses::file(Config.php);
print_r($array);
I know if you include a file inside a function they won't reach past the scope of the function. This would be loaded in by an auto loader so I would be able to use it anywhere inside my script.
Thanks in advance, if you need any more information please let me know.
It appears to me you may be going about this the wrong way. But first off the problems with the code you presented. Your code appears to be asking for two parameters but you are only passing in one. This will make your is_dir / is_file calls always fail because both of the conditions will never be true at the same time so your file isn't include.
Also you don't appear to be assigning the returned value to anything that has a lifetime greater then that of run time of your static function so even if the file was included your config variable would end up being thrown away.
To clean up your existing code you could do something along these lines.
<?php
class Config {
static public $config;
public static function load($path, $file){
if (file_exists($path.$file) && is_file($path.$file)){
self::$config = include ($path.file);
}
}
}
Then you would change your included file to return the data instead of assigning it to a variable. Like
<?php
return array(
'var1' => 'something',
'var2' => 'something else'
);
Then you would be able to use it like this.
Config::load("/path/to/config/", "Config.php");
echo Config::$config["var1"];
While this will make the code behave the way you are trying to do it this is probably a bad idea. You should be using Dependency injection within your other classes instead of invoking the static property with your other classes. This will afford you a seam when doing testing and also allow your methods and classes to document in the constructor and method signatures exactly what is needed to perform a given task. See this video for more information on what DI is, and this google clean code talk to help understand why using statics like this is a bad idea.
Take a look at this: http://php.net/manual/en/function.parse-ini-file.php
But in simple terms you could so this:
config.inc.php
$config['var1'] = 'Hello';
$config['var2'] = 'World';
Then in your php:
include_once('config.inc.php');
or you could use this for an INI style include file: http://php.net/manual/en/function.parse-ini-file.php
config.inc.php
[first_section]
one = 1
five = 5
animal = BIRD
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
Then in your php:
$ini_array = parse_ini_file("sample.ini", true);
print_r($ini_array);
Related
I have Googled for the past couple hours trying to figure out how to do this. I just want to be clear that my issue is not this issue or that issue, because I am not trying to check inside the script if the variables are set. I am trying to check outside it, to see if they're set / passed to the included file before they're interpreted, or at least meaningfully interpreted to the point where an error is thrown. Let me explain.
A little Background
I am creating a utility package for internal usage at the company I work for. I have chosen to render templates one of two ways: including them or outputting the rendered string.
public function render($context = array()) {
do_action(self::TAG_CLASS_NAME.'_render_view', $this, $context);
if ( empty( $this->html ) ) {
ob_start();
$this->checkContext($context);
extract( $context );
require_once $this->getFullPath();
$renderedView = ob_get_contents();
ob_end_clean();
$this->html = $renderedView;
return $renderedView;
} else {
return $this->html;
}
}
public function includeView($context = array()) {
do_action(self::TAG_CLASS_NAME.'_include_view', $this, $context);
extract( $context );
include $this->getFullPath();
}
The problem
Inside of the render method, I start some output buffering. This is so I can have the interpreter evaluate the code and output the HTML as a string (without taking the eval() hit. Inside my unit tests, I experiemented with what would happen if I left out a context that was inside the template itself. For example: If I have a context array that looks like:
$context = array(
'message' => 'Morning'
);
And an associated template that looks like this:
<?php echo "Hello ".$name."! Good ".$message; ?>
Or this
<p>Hello <?php echo $name; ?>! Good <?php echo $message; ?></p>
Doesn't matter how it's formatted, as long as the context vars are passed to it correctly. Anyway, leaving out the $name in the context will result in a "Undefined variable: $name" E_NOTICE message. Which makes sense. How do you 'capture' that undefined variable before it creates the notice?
I have tried to use:
$rh = fopen($this->getFullPath(), 'r');
$contents = fread($rh, filesize($this->getFullPath()));
fclose($rh);
Where $contents outputs:
"<?php echo sprintf("Hello %s, Good %s.", $name, $greeting); ?>"
The next logical step (for me anyway, thus the question) is to extract the vars in that string. So I briefly started down the road of creating a regex to match on that string and capture the variables, but ended up on here, because I felt like I was duplicating work. I mean, the PHP interpreter already does this effectively, so there must be a way to utilize the built-in functionality. Maybe?
All this to say, I want to do something similar to this psuedo code:
protected function checkContext($context) {
require $filename;
$availVars = get_defined_vars()
if ( $availVars !== $context ) {
setUnDefinedVar = null
}
}
Having said that, this may not even be the right way to do it, but what is? Do I let the interpreter fail on an undefined variable upon inclusion of the file? If I let it fail, am I exposing myself to any security vulnerabilities? Note: I am not setting any variables in templates via $_GET or $_POST.
Any answers are much appreciated. Thank you ahead of time.
I recommend using getters and setters within your class. There may be a better solution but this is how I do it. Since you're trying to access variables in the global scope you would add the following method to the class calling the included file:
public function __get($variable)
{
if (array_key_exists( $variable, $GLOBALS ))
return $GLOBALS[$variable];
return null;
}
So now in your included file you would access variables by using the
$this->{$variableName}
In your specific case, it would look like...
<?php echo "Hello ".$this->name."! Good ".$this->message; ?>
However please note, if the variable requested is defined in the scope of the calling class then that particular member variable will be returned. Not one defined in the global scope.
A better explanation of this overloading operator may be found here PHP Magic Methods
In other words, say I have
$existingVariable = 'This is set';
echo thisFunction($existingVariable, $nonExistingVariable);
//included file
function thisFunction($existingVariable){
echo $existingVariable;
}
$nonExistingVariable is no longer there because the included file has changed.
So the way I understand it, $nonExistingVariable would = '' or NULL, right? Does this have any real impact on my code? I'll remove them (or add them back to the included file) before release, but I was just curious if having non-existing variables as an argument risked functionality issues.
It will not affect the function of your code, unless you are using func_get_args(); to work with your arguments instead of just specifying them (you are specifying them, so it won't have any affect)
I.E. you could be doing:
function test() {
$args = func_get_args();
$b = $args[0];
$c = $args[1];
echo "$b\n$c";
}
test('dog','cat');
outputs:
dog
cat
It will affect the readability and user-friendliness of your code moving forward though, as you may try and copy and paste a function call from an old area of code, and get stuck trying to figure out why the variable isn't getting passed into the function (because its no longer an argument).
Why not just remove it, if its not being used anymore?
if the variable doesn't exist then yes PHP will give you an error. "Undefined Variable". You can assign a NULL value, $nonExistingVariable= NULL; Or you can do
if (!empty($nonExistingVariable))
To prevent errors
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Reading and Writing Configuration Files
Most of my functions depends on settings.
As of now I'm storing my setting values in database.
For example to display ad in a page i'm checking my database whether to display ad or not
I mean like this
$display_ad = 'get value from database';
if ($display_ad) {
echo 'Ad code goes here';
}
This is fine. But the fact is I have more than 100 settings. So I think my databse load will be reduced if I define the value in a settings.php file like
define('DISPLAY_AD', true);
if (DISPLAY_AD) {
echo 'Ad code goes here';
}
But I'm not sure this is the right way. Is define() is the correct solution. Or is there any better and faster solution available?
Several options, like those mentioned, include .ini files (using parse_ini_file(), etc.), XML (some concoction with SimpleXML perhaps) but I prefer keeping config in native PHP.
The include() construct allows for one to return from the included file. This allows you to:
config.php
return [
'foo' => [
'bar' => [
'qux' => true,
],
'zip' => false,
],
];
elsewhere.php
function loadConfig($file) {
if (!is_file($file)) {
return false;
}
return (array) call_user_func(function() use($file) {
// I always re-scope for such inclusions, however PHP 5.4 introduced
// $this rebinding on closures so it's up to you
return include($file);
});
}
$config = loadConfig('config.php');
if ($config['foo']['bar']['qux']) {
// yeop
}
if ($config['foo']['zip']) {
// nope
}
Special care needs to be taken, as when you try to dereference a non-existent dimension, PHP will poop on you:
if ($config['i']['am']['not']['here']) { // poop
}
Creating a wrapper class/functions to manage configuration to your needs is reasonably trivial though. You can add support for cascading configuration (a la web.config in the ASP world), caching, etc.
define() is quite a good way of doing things. An alternative is to define a global array. such as
$config['display_ad']=true;
$config['something_else']='a value';
//...
function doSomething() {
global $config;
if ($config['display_ad']) echo 'Ad code goes here';
}
The latter way is what many projects use, such as phpmyadmin, the reason could be, that you cannot define() a non-scalar value, e.g. define('SOME_ARRAY',array('a','b')) is invalid.
The simplest thing to execute would be an ini file. You create a file that looks like this:
value1 = foo
value2 = bar
value3 = baz
Then, from PHP, you can execute this:
$iniList = get_ini_file("/path/to/ini/file/you/just/made");
if ($iniList['value1'] == 'foo') {
print "This will print because the value was set from get_ini_file."
}
If you have a lot of similar constants, that's better than dozens of define methods and quicker than database fetches.
you can allso make a class like here:
php.net
im currently working on some sort of upload with automatic video conversion. At the moment i am executing a php script via php shell command after the upload is finished so the user doesn't have to wait until the conversion is completed. Like so:
protected function _runConversionScript() {
if (!exec("php -f '" . $this->_conversionScript . "' > /dev/null &"))
return true;
return false;
}
Now in my conversion script file i am using functions from another class "UploadFunctions" to update the status in the database (like started, converted, finished...). The problem there is though that this UploadFunctions class inherits from another class "Controller" where for example the database connection gets established. Currently i am using spl_autoloader to search specific directories for the files needed (for example controller.php), but because the conversion script is out of context with the whole autoloader stuff it doesn't recognize the Controller class and throws an fatal php error.
Here is some code from the conversion script:
require_once('uploadfunctions.php');
$upload_func = new UploadFunctions();
// we want to make sure we only process videos that haven't already
// been or are being processed
$where = array(
'status' => 'queued'
);
$videos = $upload_func->getVideos($where);
foreach ($videos as $video) {
// update database to show that these videos are being processed
$update = array(
'id' => $video['id'],
'status' => 'started'
);
// execute update
$upload_func->updateVideo($update);
.........
Am i doing this completly wrong or is there a better way to accomplish this? If you need more code or information please let me know!
Thanks a lot
Here is my spl_autoload code:
<?php
spl_autoload_register('autoloader');
function autoloader($class_name) {
$class_name = strtolower($class_name);
$pos = strpos($class_name ,'twig');
if($pos !== false){
return false;
}
$possibilities = array(
'..'.DIRECTORY_SEPARATOR.'globals'.DIRECTORY_SEPARATOR.$class_name.'.php',
'controller'.DIRECTORY_SEPARATOR.$class_name.'.php',
'..'.DIRECTORY_SEPARATOR.'libs'.DIRECTORY_SEPARATOR.$class_name.'.php',
'local'.DIRECTORY_SEPARATOR.$class_name.'.php'
);
foreach ($possibilities as $file) {
if(class_exists($class_name) != true) {
if (file_exists($file)) {
include_once($file);
}
}
}
}
?>
I have my project divided into subfolders wich represent the functionality, for example upload, myaccount and gallery.. in every subfolder there are also 2 other folders: controller and local. Controller is the class controlling this part (upload for example) and local is the folder where i am putting the local classes wich are needed. The controller class gets called from the index.php wich is located in the sub-project folder. "libs" and "global" are just projectwide classes, like database, user and so on.
This is an example of my folder structure:
www/index.php // main site
www/upload/index.php // calls the controller for upload and initializes the spl_autoload
www/upload/controller/indexcontroller.php // functionality for the upload
www/upload/local/processVideo.php // this is the conversion script.
I am fairly new to spl_autoload function. In my opinion the spl_autoload is not getting called if my script is calling: "php -f processVideo.php", isn't it?
PHP relative paths are calculated from the path where PHP binary is called.
I suggest you to use __DIR__ constant to avoid that behavior
http://php.net/manual/en/language.constants.predefined.php
I was actually able to resolve the issue. I had to include the spl_autoload_register function inside the conversion script so that it was able to locate the files. This was an issue because the conversion script is not build into my framework an so it isn't able to load the classes from the framework autoloader.
SOLVED: The error had nothing to do with require or include. It was completely unrelated to the question. Sorry about that. :(
I thought I had an understanding of require/include until now. I have a file named file_one that has something like this, $user_data = return_user_data_as_array(). My second file file_two, calls $user_data. Now, when I do a include 'file_two.php' into the file_one page after the functions and variables have been called, the page returns with undefined variable user_data. I thought that if you include/require a file into your php code it would pick up any variables written in the original file? How would I fix this?
Edit: I also want to mention that although the undefined variable error is popping up on my screen, the "undefined" variables are correctly echoed out.
File One
//this require_once holds the function user_data()
require_once 'core/init.php';
$user_id = 1;
$profile_data = user_data($mysqli, $user_id);
//this calls file two
require_once 'file_two.php';
File Two or file_two.php
echo $profile_data['full_name'];
The key thing here is to have your include before not after the function call. Also just include the function and then call it in file two.
I think you must have an error elsewhere on the page. If you're defining the variable outside of a function or class and you're including the file that uses it after it's been defined it should work fine:
file_one.php
<?php
function return_user_data_as_array(){
return array('name'=>'BenD');
}
$user_data = return_user_data_as_array();
include('file_two.php');
?>
file_two.php
<?php
print_r( $user_data );
?>
Result:
Array
(
[name] => BenD
)
This should all work fine. Your error is probably arising elsewhere (for instance, you might be calling $user_data from inside a function without specifying global $user_data, or you're calling $user_data somewhere in file_one.php accidentally?)
EDIT
Now that you've shown some code, I imagine that the problem is that user_data() isn't returning an array that includes the full_name key. Try print_r($profile_data) to see what the array looks like. I'm guessing that the problem lies in what's being returned from user_data() (if anything! make sure it includes a return $x clause in there! I've spent a lot of time trying to figure out a logical error when the only problem was that my function didn't return anything)
The answer had nothing to do with the problem in the question. The code in the question is correct.