I'm trying to wrap a legacy application in the Zend2 MVC framework. Thanks to the Zend Skeleton Application and code samples (especially https://github.com/maglnet/MaglLegacyApplication), I've solved most of my issues.
The one big issue I haven't been able to solve is illustrated by the following "legacy" file:
<?php
$test = "test";
function echo_test(){
global $test;
echo "test = ";
var_dump($test); # Makes NULL explicit
}
echo_test();
In the Controller for a ZF2 module, I capture the output of the include using an output buffer and stick that into the response object:
...
chdir($filePath) # Fixes relative includes
ob_start();
include $filePathAndName;
$output = ob_get_clean();
$response->setContent($output);
return $response;
... and I get back test = NULL.
I've seen warnings that ZF2 namespaces can create issues for legacy files and done my legwork to try and clarify why. According to the PHP guide, "Without any namespace definition, all class and function definitions are placed into the global space". Indeed, my sample is only slightly more complicated than the one listed below this statement... but it doesn't seem to work.
I also see that "You can set a variable after declaring a namespace, but variables will always exist in the global scope. They are never bound to namespaces.".
I continued to research and have finally discovered that this approach "will import the contents of the include file into the method scope, not the class scope".
Is there any way to process the file outside the method scope?
This is nothing to do with PHP namespaces. If you're including a file from within a ZF controller action, it will execute within that function's variable scope. For your example to work you'd need to declare global $test; in the ZF action as well before including the file (which would be horrible).
Without knowing exactly what your legacy code looks like it's hard to suggest a good solution. If there are a set number of globals you need to work, you can global them at some point earlier in the ZF application (with the goal of removing that hack at a later date). If you don't know in advance what the globals are, or if there are a large number of them, you may need to edit the legacy code to try and refactor out the reliance on globals.
Another option (at least theoretically) is to use exec(), shell_exec(), passthru() (automatically prints output), or curl (see shell_exec link, but only if you're hosting it outside ZF2 to avoid an infinite loop). This approach has its own list of drawbacks including:
Security (but see escapeshellarg)
Access to $_SERVER (the first comment on the question here may help), $_GET (but see this technique), $_POST, and especially $_COOKIES
A bug (may be resolved) in the php command line
Related
I have custom config items in application/config/config.php.
Samples of my custom config items:
$config['website_title'] = 'ABC Website'; //Assume website title is fixed
.
.
.
etc
Now i can call $this->config->item('website_title') any where in my application. However, i don't find it efficient enough because i might have multiple $this->config->item('website_title') within the project. I came up with the following solution:
1.Create a function, within a helper, that return the config item as the following:
public function website_title() {
return $this->config->item('website_title');
}
2.Now i can call website_title() as many as i want.
Is this a good solution? Do you see any downsides?
Note: I try to avoid using global variables because i tried it and i
faced many unnecessary problems such as undefined variables,
surprised!
What do you mean by "efficient"? Runtime efficiency? Coding efficiency? Clarity?
In terms of runtime efficiency Truth's suggestion of using is perhaps the simplest and best. However, I prefer to code using a strict class/object implementation, and in reality defines are just global constants.
If you profile the vast majority of scripts, you will find that however you code your configuration referencing makes an immaterial impact on runtime, so I would suggest going for simplicity and clarity of coding every time.
One approach is to use a singleton class (there are lots of tutorials on doing this) and use the magic method __get() to allow you to dynamically overload parameter access. This is one case where I feel that you have to use a single class as these property methods only work with object (non-static) parameter references. Hence you can simply use:
$cfg = Configuration::get();
...
... $cfg->someConfigParam ... // to refer to a config parameter
...
... /* or even */ ... Configuration::get()->someOtherParameter ...
Note that $cfg in the above example essentially stores an object handle, so there is no material runtime cost in doing this, and you can put this statement at the top of each function or class constructor that references a config item if you don't want to litter your code with Configuration::get()->someOtherParameter type calls.
The Configuration::__get() access function , plus the class constructor can handle all the complexities of caching and access of the individual parameters. This also means that you con also encapsulate the source of the configuration: some application-specific D/B config table; one or more config files, ...; even cookies or URI parameters (so long as you include appropriate validation).
I personally don't recommend over loading with the __set() magic method as, IMO, overriding or setting a config parameter should be an explicit action, e.g. $cfg->setConfigItem( 'someValue', TRUE );
Here is a link to the documentation on my config class if you want some ideas.
I asked this sort of question on programmers once. I got a very good answer, simply use constants.
I.e. WEBSITE_TITLE
i have an application done in php and all configuration variables are loaded in a big $conf variable at the beginning of the script.
What is the better way to communicate this configuration variable to all other functions ?
make it a parameter of every function ? or use it with "global $conf;" statement in every function ?
is there a better way to do ?
Thanks
Use PHP constants.
For ponies sake, avoid using global variables at all costs :)
EDIT
Some explanations about "avoiding global variables at all costs" and possible alternatives:
https://stackoverflow.com/questions/357187/when-are-global-variables-acceptable/357361#357361
http://my.opera.com/zomg/blog/2007/08/30/globals-are-evil
https://stackoverflow.com/questions/1285700/what-are-some-good-tips-for-a-new-php-developer (especially section Scope of the accepted answer)
Make a configuration class that stores the options. Make it a singleton PHP Manual describes that here. This is just an alternative to global variables. It would allow you to define a method to load options from a file or a php array and store them in the class. Other classes can use the configuration object by getting the single instance and accessing the data.
I think this is better than a global variable as the other answer also says. But it still lets you define options as arrays, or even nested arrays if you want (and set up your class accordingly)
Your use of a single global-scoped variable $conf is perfectly fine. Many PHP applications do that. But there are drawbacks to combat.
In particular it's often more effort to write global $conf in each function where you want to access them. In that case I would recommend a simple global wrapper function instead:
function conf($key, $sub="") {
global $conf;
if (defined($key))
{ return constant($key); }
elseif ($sub)
{ return $conf[$key][$sub]; }
else
{ return $conf[$key]; }
}
This allows you to write conf("setting1") or conf("main", "opt3") whereever you need it. Still you can access the global $conf where that is more suitable. As extra bonus you can make this wrapper function more intelligent, by allowing it to query alternative settings etc. Also see how easy it is to also sneak in conf("CONSTANT") support.
Keeping this adds some flexibility in defining your configuration settings. Personally I use a similar approach, albeit with defining the array step-wise rather than at once:
$app_config["title"] = ...;
$app_config["editor.btns"] = ...;
define("RESTRICTED_MODE", true);
I'm preferring the array() approach, but transitioning to an ini-file for storage at a later point is not a problem. Also you can still make your config array read-only if the need arises. For that just define an:
class Read_Only_Array extends ArrayObject { function offsetSet() {} }
$conf = new ReadOnlyArray($conf);
So it's still accessible as array, but you easily established what others use cumbersome registries or syntactic workarounds for.
The "globals are evil" meme is completely baloney. It's parrotted on SO by cargo cult programmers with a desire for oversimplification and newcomers who glance over bold headlines without understanding the language semantics.
In your case, you just use a single $conf variable, and do not pollute the shared scope. When it is coherently accessed from the whole application, then it's not an issue. You should however strictly avoid to modify contents at runtime (use Read_Only_Array if need be). Create a secondary $app_var[] aray for that, and keep your config settings static.
In one PHP file, I have this code:
require_once $_SERVER['DOCUMENT_ROOT'] . '/custom/functions.php';
global $testVar;
var_dump($testVar);
In the functions.php file, I have this at the beginning, followed by a few other functions:
function pr($s) {
echo '<pre>', htmlspecialchars(print_r($s,true)), '</pre>';
}
$testVar = 'hello world';
When running the first file, the variable comes back as NULL. I added the global bit but it shouldn't be necessary. This is part of a Joomla module but I've never had problems including files before, it should just work like regular PHP. Why might this be happening?
First, try to use Joomla's path constants such as JPATH_BASE instead of $_SERVER['DOCUMENT_ROOT']. Joomla has a lot of useful constants, check it's documentation.
I've read your answer, and reading php documentation I tried to find a reason to why you need to use global keyword twice.
First, Variable scope.
The scope of a variable is the context within which it is defined. For the most
part all PHP variables only have a single scope.
(...)
However, within user-defined functions a local function scope is introduced.
Any variable used inside a function is by default limited to the local
function scope.
The variable isn't in a function scope, so that's why we thought the NULL was a strange behavior.
But then I read include and found something interesting:
(...)
Any variables available at that line in the calling file will be available
within the called file, from that point forward. However, all **functions**
and **classes** defined in the included file have the global scope.
I can't see any mention about the variables being global in this paragraph. So,it seens that, being cumbersome or not, your solution is the right thing to do when you want to use global variables like that.
In your situation, if doing this is cumbersome, I would create a simple class. If you have just helper functions in your file, create a class Util{} with a lot of methods and $testVar as an attribute.
I have found a solution that seems to work: using the global keyword both when setting the variable initially, and just before I need to use it.
(However this is quite cumbersome, and I'm still not sure why it happens, so if anyone has a better solution, feel free to post.)
I want to make a kind of "generic" function which gets executed and - depending on what to do - includes it's implementation file via include(). So for example, I might have exactly one function and exactly 20 procedure files for that function. The procedure files may look like do_this_procedure.php, do_that_procedure.php, etc.
As I'm new to PHP I'd like to know from PHP expertes wether this is fine with PHP or not, before I try it and only "believe" it works, and in reality a lot of things go wrong. So what do you think? Currently I think of an include just as an insertion of code right into that place before it gets compiled for execution.
From the include statement documentation:
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.
So yes, you can call include from within a function and use that to define the body of the function. You can even have the file that you include vary with each call to the function. The downside is the include will be evaluated with each call to the function; if the function is called many times, it could seriously impact performance.
Depending on exactly what you're trying to accomplish, an alternative is to follow a functional programming paradigm, which lets you construct functions at runtime. Before PHP 5.3, it's ugly, so I wouldn't recommend it unless you can require at least PHP 5.3.
You may try this too:
PHP Tokenizer.
//from external script:
<?php
var_dump(token_get_all(file_get_contents('myscript.php')));
?>
Including such file will be done on runtime, I believe. Basically, when PHP hits the include/require function, it will eval that file as an independent one.
I'm not sure whether variables passed to the function will be usable in the included file, but global-ing them should work fine.
#outis hit the nail on the head, but an alternative approach would be to create the function as a static method in a class and create an autoloader (via spl_autoload_register, etc.) to include and call the class method at runtime.
Then again, this really wouldn't really buy you anything unless you're already using an OO approach.
outis is correct, however this strikes me as a very ugly and messy way to implement the code. Why not just have different files with different declarations of a function including the function declaration.
Functions should be as short as possible - as a general rule of thumb I will go back and see if it is practical to refactor any function longer than 20 lines. Bearing this in mind, where's the benefit in including the body of a function from a seperate file?
C.
I have some PHP code similar to the following:
foreach ($settingsarray as $settingsfile)
{
include ($settingsfile);
// do stuff here
}
$settingsarray is an array of file names from a particular folder.
The problem is, that each $settingsfile defines constants (with the same names), which of course, can not be redefined.
What possible methods are there to prevent errors occurring in this situation?
Two options I can think of include, changing all the constants to variables and using PHP namespaces.
However, I'm not sure how I would go about using namespaces, it would require the declaration at the start of every $settingsfile? Is there a method of isolating constants, ariables and functions without using namespaces?
What I would really love, is to be able to do something as simple as:
foreach ($settingsarray as $settingsfile)
{
{//added braces to indicate where the isolation is
include ($settingsfile);
// do stuff here
}//what happens in here does not affect what happens outside of here
}
I should just note, this is part of a new feature, and is the only part of the code that loads all the $settingsfiles. The rest of the code only ever loads one file at a time. One reason I am using constants is so that I don't have to worry about defining variables "global" to be able to access them inside functions.
My answer is somewhat complex, but should work for you quite nicely. I'm assuming you have a ton of these settings files, since you're so averse to changing each one individually.
If you're able to use namespaces, I'll assume you've already upgraded to PHP 5.3RC2. Copy the following into a .php file, and change the namespace to your liking:
<?php
namespace myapp\config;
function define($key, $val) {
Config::set($key, $val);
}
class Config {
private $vars = array();
// This class should not be instantiated
private function __construct() {}
public function set($key, $val) {
self::$vars[$key] = $val;
}
public function get($key) {
return isset(self::$vars[$key]) ? self::$vars[$key] : NULL;
}
}
?>
Include that in your code, and now making everything work is a simple matter of changing the
<?php
in your settings files to
<?php namespace myapp\config;
If you have a ton of them, a quick 'sed' command in your terminal should take care of it quite nicely, all in one fell swoop.
If I lost you on how to use my code, here's an example:
<?php
require_once('the_php_file_i_just_gave_you.php');
use \myapp\config\Config;
foreach ($settingsarray as $settingsfile) {
include ($settingsfile);
$varname = 'key';
echo "In this file, '$varname' equals: " . Config::get($varname);
}
?>
Good luck!
Okay, so if I'm understanding you correctly, inside that loop you'd like to do things with the constants defined inside each $settingsfile and then basically get rid of them after each loop?
Unlike variables, a constant is a value that, once set, cannot be changed or unset during the execution of your script
src
Your only option is to change the define() declarations to variables.
not easy but you could read the php file with file_get_contents($settingsfile) then rewrite the constants, and eval the code.
$settingsstr = file_get_contents($settingsfile);
$settingsstr = preg_replace('/<\?\w*(.*)/', '\\1', $settingsstr);//get rid of the php open tag
$settingsstr = preg_replace('/define\("(\w+)"/', 'define("NAMESPACE_\\1"', $settingsstr);
eval($settingsstr);
You have a few options:
Use PHP Namespaces
Surprisingly, PHP actually does have support for namespaces. I was very surprised to find this since I have been working in PHP for years and have never heard of this feature. It is likely a newer feature and one that is not commonly used, so I would suggest not using this method
Use Class Constants
You could use classes and class constants to load your settings. Class constants are local to the defining class, so you will not need to worry about name collisions within the class. You would probably need to do more than include the file, but you could call a method on each class, such as defineConstants() or loadConfiguration() that would define the application constants, keeping the class constants for internal use.
Use Functions to Extend the Settings
Another idea is to create a settings array and separate functions to 'extend' those settings. That way you can over-write any configuration in subsequent functions without causing an error. Finally, if you want to put those in constants, you could loop over the array and use define() to define the constants.
There are many other ways to accomplish what you are trying to do, but these are just a few ideas.
A settings file should not contan PHP code.
It should be some kind of "standard" format.
http://www.php.net/parse_ini_file
In the first step (in your foreach) you read all files
Collect the variables into a temporary array
In another loop over this array, you can define them as constants.
If you routinely feel the need to "redefine constants" then something is amish in the application design.