I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.
Related
I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.
I am working on a website with php/mysql.
I have 3 files config.php, utils.php and member.php. code of the files are as below,
config.php - $objPath->docrootlibs is fine, I am sure there is no problem with this.
/* Other library files & their object */
require($objPath->docrootlibs.'/utils.php');
$objUtils = new utils();
require($objPath->docrootlibs.'/member.php');
$objMember = new member();
utils.php
class utils{
function getCurrentDateTimeForMySQL(){
return date("Y-m-d H:i:s");
}
}
members.php
class member{
var $userid;
var $username;
function __construct(){
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
}
Now when I am including the config.php inside a page home.php with simple php include statement and running that page then it gives me following error.
Notice: Undefined variable: objUtils in D:\wamp\www\site\libs\member.php on line 17
Fatal error: Call to a member function getCurrentDateTimeForMySQL() on a non-object in D:\wamp\www\site\libs\member.php on line 17
Line numbers in error above are different, I just copied the specific part from the code here.
I am not understanding why its giving error, because objects of utils class is defined on config page before including the member class. It should detect that object.
Please check and help me to understand and correct this error.
Thanks!
One Solution
Unlike JavaScript PHP will not bubble up through scopes, which means
public function __construct(){
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
does not know what $objUtils is, because there is no such object defined in the local scope. This means, you have to make the object available inside that scope. The cleanest and most maintainable way to do that is to inject the utils instance to the member instance, e.g.
public function __construct($utils){
$this->lastactivity = $utils->getCurrentDateTimeForMySQL();
}
However, since you seem to be using that value on construction only anyway, there is no reason why your member instance has to know how to use the utils object. So why not just insert the actual value right from the start, e.g.
public function __construct($lastActivity){
$this->lastactivity = $lastActivity;
}
// then do
$utils = new utils();
$member = new member($utils->getCurrentDateTimeForMySQL());
On globals
You definitely do not want to use the global keyword or static methods. Both couple back to the global scope. This means you can no longer use the member class without the global scope. This makes maintaining, reusing, refactoring and testing harder. OOP is about encapsulation and by reaching out from the class to the global scope you are breaking that encapsulation. Use Dependency Injection as shown above instead.
Apart from that, using globals will also make your code harder to read. A developer looking at the ctor signature of member is likely to assume there is nothing else to do than just call new member. That's a lie, because she also has to setup the utils instance. In other words, you are hiding dependencies. The developer has to look at the actual code to understand what's going on. Thus, make dependencies explicit.
Some more resources:
http://c2.com/cgi/wiki?GlobalVariablesAreBad
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
EDITs after comments
If you really need that utils object, inject the instance and assign it to a property inside the member instance in the ctor. Then you can access it with $this->utils->foo() from anywhere else inside the member instance.
However, Utility classes are almost always a sign of bad design. It is much more likely that they should be broken apart and divided into/onto other objects. Try to find out the reponsibilities. Maybe Member should not use Utils, but Utils should be something else that uses Members.
Out of curiosity, why do you need a utility method for MySql anyway? If you use a Timestamp column in MySql for lastActivity, it will automatically update whenever the row is updated. I am assuming you are setting the lastActivity and then store the member data?
Regarding performance: you should not bother about performance. Write readable and maintainable code first and foremost. If you think your performance is not good enough, profile the application with XDebug to see what is really making an impact.
As another comment states, use dependency injection. Insert the utilities object into the constructor. Do not introduce variables over the global scope, especially between different files. This gets very confusing and creates a mandatory order of some file includes.
class member {
...
public function __construct(utils $objUtils) {
$this->objUtils = $objUtils;
...
}
}
In calling code:
$member = new member(new utils);
As an aside, I find it humorous that you have a macro with a name that is longer than the operation it performs.
As another aside, do you need a utilities class? Can the utilities just be functions?
It sounds like config.php is in the global scope. You need to use the global keyword when using $objUtils
function __construct(){
global $objUtils;
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
Since your getCurrentDateForMySQL doesn't depend on anything else inside your utils object, why not make it a static function? That way you can turn your member() method into:
function __construct(){
$this->lastactivity = utils::getCurrentDateTimeForMySQL();
}
I have a class that I am using all over the place in my code. It contains settings and other core functionality. Here's what I'm doing right now to use the class.
$settings = new Settings();
$settings->loadSettings();
Then, when I need to the code in some random function in another class, I do something like this:
function abc() {
global $settings;
$variable = $settings->a;
}
I'm sick and tired of randomly calling global $settings all over the place to pull in that settings object. I don't want to use the $_GLOBALS array (I don't know why, I just don't want to).
I'm thinking I want to switch to have a static class variable called $settings inside of settings. The code would look like this:
Settings::$settings = new Settings();
Settings::$settings->loadSettings();
Then, whenever I want to use it, I never have to worry about sucking it in via the global operator:
function abc() {
$variable = Settings::$settings->a;
}
Good idea or bad idea?
Well it's probably an improvement on globals, because it solves all the ugly scoping issues that globals cause. Getting rid of the global operator is generally a good thing! What you are doing is not dissimilar to the singleton pattern, though it's considerably simpler. (See the "Singleton" section at http://php.net/manual/en/language.oop5.patterns.php for more information on the pattern.) Your solution is almost certainly fine for your purposes.
On the other hand, there may be better ways of achieving the same thing that decouple your code more. That is to say, each class becomes more capable of being used in another project without significant recoding. One way to do this would be to "inject" the settings object into each class:
class ABC {
private $settings;
public function __construct($settings) {
$this->settings = $settings;
}
public function someMethod() {
$variable = $this->settings->a;
}
}
This would be more work, but may improve the re-usability of your code. You could then, for example, write a different settings class for every project but use the same ABC class.
This process, where you "inject" an object into another object that depends on it, is called dependency injection. There are other, more complex ways of doing this, including complex containers. See http://fabien.potencier.org/article/11/what-is-dependency-injection for an interesting set of tutorials on the subject. They're probably incidental to your current needs, but may help either now or in the future.
It seems you are looking for a Singleton. Basically the idea is to have a class which has a public static method getInstance() which returns an instance of the class itself. The first time you call the method, it stores the instance in a private property, and all later time it returns the stored instance. In this way, whenever you call Settings::getInstance(), you are guaranteed to have a copy of the same object. Then you can store settings in this object.
im looking at the MVC pattern, and I can see in one example on phppatterns they're passing the model by reference - any benefit of doing this over a global var? Am I missing something obvious?
class MyView extends View {
var $model;
function __construct(&$model){
$this->model =& $model;
}
function productTable($rownum=1) {
$rowsperpage='20';
$this->model->listProducts($rownum,$rowsperpage);
while ( $product=$this->model->getProduct() ) {
// Bind data to HTML
}
}
}
Any reason why you would do this as apposed to using a global variable? i.e.
class MyView extends View {
global $model;
function __construct(){ }
function productTable($rownum=1) {
$rowsperpage='20';
$model->listProducts($rownum,$rowsperpage);
while ( $product=$this->model->getProduct() ) {
// Bind data to HTML
}
}
The problem with global variables is that:
They assume that there is only one implementation of model and view.
They assume that there is only one instance of the model and view (you could have several of each in your application).
They hide the interdependency between components; your view is very strongly affected by the model, but not having to explicitly pass a model into your view makes this implicit.
For other reasons why globals and singletons are "evil" (i.e. just really a poor design decision that you should never make), read avoid global variables, environment variables, and singletons.
Yes -- the advantage is that you could change the implementation of the model without having to modify the View.
I'd suggest to use another tutorial, the one you're using is a bit outdated, and isn't PHP 5. That said, you really shouldn't use global variables, that's never the solution!
But I don't really get why you should even wanna pass it by reference, in PHP 5 it's already passed by reference:
class foo {
public $a;
public function __construct($a) {
$this->a = $a;
}
}
$a = new foo(10);
$b = $a;
$a->a = 20;
echo $a->a.' => '.$b->a; // 20 => 20
If you have multiple (similar) model instances, on which you possibly would like to apply the same view, you would need to explicitly pass the model instance. A global variable assumes there could be only one model instance, and thus creates an artificial limitation.
In general not using global variables is better to help you control the scope of your variables.
I won't mention about why that is a bad thing, because it has been already discussed. One thing that I think you should know is that the code provided there is PHP 4 compatible. In PHP 5 by default objects are sent by reference.
Current situation:
I have the current version of my MVC Framework which uses classes as controllers.
I have some "vintage" modules from my old MVC Framework which uses simple, flat includes as controllers.
Much simplified that means:
New Version:
<?PHP
class blaController extends baseController {
private $intVar;
function dosomethingFunction() {
$this->intVar = 123;
$this->view('myView');
}
}
?>
Old Version:
<?PHP
$globalVar = 123;
// view "controllername" is automatically shown
?>
I'm now trying to write a wrapper to be able to use my old controllers in my new MVC without having to rewrite everything. To do so, I have a "wrapper" controller:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
$this->view('old_dosomething_view');
}
}
(Once again: This is VERY, VERY simplified - just to get the idea over. Not actual code.)
The problem with that approach is, that the previously global variable $globalVar now only exists inside of the method "dosomethingFunction" and cannot be accessed by the view.
This wouldn't be the case if I could force the require to behave as "in global scope" so that $globalVar would once again be available in global scope.
So: Is there some way to achieve "require_global" or something similar?
(One solution for my problem would be to modify my old controllers to start with a bunch of "global" commands, but I'd prefer a solution where I don't have to change so much of that old code.)
(Note: Please don't tell me that GLOBALS are bad. It totally misses the point of this question. Just accept that it is a requirement to keep some old code working in a newer, cleaner environment.)
You can add local variables defined within dosomethingFunction() to global scope:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
//begin added code
$vararr = get_defined_vars();
foreach($vararr as $varName => $varValue)
$GLOBALS[$varName] = $varValue;
//end added code
$this->view('old_dosomething_view');
}
}
Note, that for this to work as expected, you should call require before using any other thing in the function. get_defined_vars() returns only variables from the current scope, so no array_diff hacks are needed.
This is the easiest solution I can think of.
Use the get_defined_vars() function twice and get a diff of each call to determine what variables were introduced by the required file.
Example:
$__defined_vars = get_defined_vars();
require('old_dosomething.function.php');
$__newly_defined_vars = array_diff_assoc($__defined_vars, get_defined_vars());
$GLOBALS = array_merge($GLOBALS, $__newly_defined_vars);
$this->view('old_dosomething_view');
Hmmm, this is an issue I've never before seen. I suppose you could do this
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
// Force "old" globals into global scope
$GLOBALS['globalVar'] = $globalVar;
$this->view('old_dosomething_view');
}
}
But that's a pretty tedious, manual process as well, depending on how many globals we're talking about. I'll think about this, but I don't know of any "auto-magic" solution off the top of my head.
For anybody interested: My (so far) final version:
class wrapController extends baseController {
function dosomethingFunction() {
// ... do some initialisation stuff ...
$__defined_vars = array_keys(get_defined_vars());
require 'old_dosomething.function.php';
$__newly_defined_vars = array_diff(
array_keys(get_defined_vars()),
$__defined_vars,
array('__defined_vars')
);
foreach ($__newly_defined_vars as $var) {
$GLOBALS[$var] = &$$var;
}
$this->view('old_dosomething_view');
}
}
Ugly, but it works. Thanks for all your great help!
Have you tried Zend_Registry from Zend Framework?
The registry is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage.
http://framework.zend.com/manual/en/zend.registry.html