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
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've been searching and reading a lot about how is the best way (code-wise) to get application's config variables in a PHP environment. After that I've sum up that are two more generally used ways to manage with configuration files and variables.
But I'm a bit confused about it, one, method 1, is using an static class. The other one, method 2 is using an instantiable class.
First one is worse to unit testing than second one. And It's similar to a global variable. Isn't it?
Second one need a global varaible in order to use instantiated object.
I'll try to explain myself.
Facts:
- App's settings are kept on a INI file.
- This INI file has sections, in order to maintain configuration variables.
- I've got only one INI file.
- Class do some validation of configuration file.
- Code examples below aren't complete, it's only a sample to ilustrate my question.
Method 1: Using static class
This method use a Config static class, it uses static because only one Config object would be used in all application.
Code example:
class Config
{
static private $data;
static public function load($configFile) {
self::$data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
static public get($key) {
// code stuff to deal with sections and keys, but basically
return self::$data[$key];
}
}
On my application I create the static object, once, using this code:
\Config::load('/where/is/my/ini/file.ini');
In this case, every time i want to get a value i use:
$host = \Config::get('database.host');
function example()
{
echo \Config::get('another.value');
}
Method 2: Using instable object
In this scenario I use a Config class object.
Code example:
class Config {
private $data = array();
public function __construct($configFile) {
$this->data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
public function get($key) {
// code stuff to deal with sections and keys, but basically
return $this->data[$key];
}
public function __get($key) {
return $this->get($key);
}
}
To use it, first we need to instantiate an object and then get the value:
$settings = new \Config('/where/is/my/ini/file.ini');
$host = $settings->get('database.host');
echo $settings->database->host;
But when I need this value inside a function, I need to use a global variable, which I think isn't right at all:
global $settings;
$settings = new \Config('/where/is/my/ini/file.ini');
function example()
{
global $settings;
echo $settings->get('another.value');
}
What I miss leading?
Thanks in advance to read and answer my question.
Simply you may also use a php file to keep your configurations for example, config.php and then you may use require from anywhere to get it:
// config.php
<?php
return array(
'database' => 'mysql',
'pagination' => array(
'per_page' => 10
)
);
Then use:
$config = require "path/to/config.php";
print_r($config);
You may use this with a function too, for example:
function someFunc()
{
$config = require "path/to/config.php";
// Now use it
}
You may create a class to get the the configurations using a method like, for example:
class Config {
static function get()
{
$config = require "path/to/config.php";
return $config;
}
}
So you may use:
$config = Config::get();
This is just another simple idea, you may extend it to use it like:
$perPage = Config::get('pagination.per_page');
Just need to add some more code to make it working like this way, give it a try.
Update:
Btw, I built a package named IConfig for my own MVC Framework back in 2013, it's also available on Packagist. You may use it or check it's source code, maybe you'll get better idea and can build a better one. But probably there are a lot better ones available on the web as well.
I think the problem you're trying to solve isn't really specific to PHP and either of the two techniques you've described could be viable ways to handle global configurations.
Having said that, I agree with Rangad, the solution is to use Dependency Injection. At it's simplest this pattern just means passing dependency's to a class/object/function as arguments. For example,
class Thing () {
private $host = '';
function __contructor($host) {
$this->host = $host;
}
function getHost()
{
echo $this->host;
}
}
$thing = new Thing( Config::get('database.host') );
$thing->getHost();
$thing2 = new Thing (Config::get('someOtherDatabase.host') );
$thing2.getHost();
This encapsulates your classes. Now they can be used in tests or even other applications so long as the needed dependences can be provided.
The nice thing about this is that you can use it with either of your proposed config options and probably others. For example if you're looking for something simple there's Pimple, a PHP Dependency Injection container written by the creator of the Symphoy PHP framework that in my opinion is great for smaller PHP projects http://pimple.sensiolabs.org/.
I am looking into trying to simplify my PHP code some more, and I have yet to find an answer with this methodology one of my team members are using. Nor, have I ever saw this done before anywhere on the web.
Here is the code example from our web application which he is working on with me.
<?php
class ArticlesHandler {
public function __construct() {
require 'Articles.php';
$articles = new Articles;
}
}
?>
Is this proper to init one class within another class?
For me, this just seems not proper standard to init classes to work together.
Yes and no. It works, but this particular code can lead to a number of problems.
You should be using require_once instead of require to avoid possible errors of including the same file twice. As it is this code here will bring your app to a complete stop:
new ArticlesHandler;
new ArticlesHandler;
This creates a hard coupling to the Articles class. You should probably rather be using dependency injection and pass an instance of Article to the constructor of ArticlesHandler. See How Not To Kill Your Testability Using Statics.
Yes, it is proper and normal to call constructors in a constructor. There is nothing weird/bad about it.
This is what I normally do.
class Repository {
protected $_models = array();
public function getModel($model, array $params = array()){
require_once $model.'.php'; //Replace this with an autoloader
if(empty($this->_models[$model])){
if(!empty($params)){
$this->_models[$model] = new $model($params);
} else {
$this->_models[$model] = new $model();
}
}
return $this->_models[$model];
}
}
And call the other class like this.
class ArticlesHandler extends Repository {
public function __construct() {
$articles = $this->getModel('Articles');
}
}
it seem's right.
For me, this just seems not proper standard to init classes to work together.
you can extend Articles class if you want to use Articles class inside the ArticlesHandler
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.
Can you declare a function like this...
function ihatefooexamples(){
return "boo-foo!";
};
And then redeclare it somewhat like this...
if ($_GET['foolevel'] == 10){
function ihatefooexamples(){
return "really boo-foo";
};
};
Is it possible to overwrite a function that way?
Any way?
Edit
To address comments that this answer doesn't directly address the
original question. If you got here from a Google Search, start here
There is a function available called override_function that actually fits the bill. However, given that this function is part of The Advanced PHP Debugger extension, it's hard to make an argument that override_function() is intended for production use. Therefore, I would say "No", it is not possible to overwrite a function with the intent that the original questioner had in mind.
Original Answer
This is where you should take advantage of OOP, specifically polymorphism.
interface Fooable
{
public function ihatefooexamples();
}
class Foo implements Fooable
{
public function ihatefooexamples()
{
return "boo-foo!";
}
}
class FooBar implements Fooable
{
public function ihatefooexamples()
{
return "really boo-foo";
}
}
$foo = new Foo();
if (10 == $_GET['foolevel']) {
$foo = new FooBar();
}
echo $foo->ihatefooexamples();
Monkey patch in namespace php >= 5.3
A less evasive method than modifying the interpreter is the monkey patch.
Monkey patching is the art of replacing the actual implementation with a similar "patch" of your own.
Ninja skills
Before you can monkey patch like a PHP Ninja we first have to understand PHPs namespaces.
Since PHP 5.3 we got introduced to namespaces which you might at first glance denote to be equivalent to something like java packages perhaps, but it's not quite the same. Namespaces, in PHP, is a way to encapsulate scope by creating a hierarchy of focus, especially for functions and constants. As this topic, fallback to global functions, aims to explain.
If you don't provide a namespace when calling a function, PHP first looks in the current namespace then moves down the hierarchy until it finds the first function declared within that prefixed namespace and executes that. For our example if you are calling print_r(); from namespace My\Awesome\Namespace; What PHP does is to first look for a function called My\Awesome\Namespace\print_r(); then My\Awesome\print_r(); then My\print_r(); until it finds the PHP built in function in the global namespace \print_r();.
You will not be able to define a function print_r($object) {} in the global namespace because this will cause a name collision since a function with that name already exists.
Expect a fatal error to the likes of:
Fatal error: Cannot redeclare print_r()
But nothing stops you, however, from doing just that within the scope of a namespace.
Patching the monkey
Say you have a script using several print_r(); calls.
Example:
<?php
print_r($some_object);
// do some stuff
print_r($another_object);
// do some other stuff
print_r($data_object);
// do more stuff
print_r($debug_object);
But you later change your mind and you want the output wrapped in <pre></pre> tags instead. Ever happened to you?
Before you go and change every call to print_r(); consider monkey patching instead.
Example:
<?php
namespace MyNamespace {
function print_r($object)
{
echo "<pre>", \print_r($object, true), "</pre>";
}
print_r($some_object);
// do some stuff
print_r($another_object);
// do some other stuff
print_r($data_object);
// do more stuff
print_r($debug_object);
}
Your script will now be using MyNamespace\print_r(); instead of the global \print_r();
Works great for mocking unit tests.
nJoy!
Have a look at override_function to override the functions.
override_function — Overrides built-in
functions
Example:
override_function('test', '$a,$b', 'echo "DOING TEST"; return $a * $b;');
short answer is no, you can't overwrite a function once its in the PHP function scope.
your best of using anonymous functions like so
$ihatefooexamples = function()
{
return "boo-foo!";
}
//...
unset($ihatefooexamples);
$ihatefooexamples = function()
{
return "really boo-foo";
}
http://php.net/manual/en/functions.anonymous.php
You cannot redeclare any functions in PHP. You can, however, override them. Check out overriding functions as well as renaming functions in order to save the function you're overriding if you want.
So, keep in mind that when you override a function, you lose it. You may want to consider keeping it, but in a different name. Just saying.
Also, if these are functions in classes that you're wanting to override, you would just need to create a subclass and redeclare the function in your class without having to do rename_function and override_function.
Example:
rename_function('mysql_connect', 'original_mysql_connect' );
override_function('mysql_connect', '$a,$b', 'echo "DOING MY FUNCTION INSTEAD"; return $a * $b;');
I would include all functions of one case in an include file, and the others in another include.
For instance simple.inc would contain function boofoo() { simple } and really.inc would contain function boofoo() { really }
It helps the readability / maintenance of your program, having all functions of the same kind in the same inc.
Then at the top of your main module
if ($_GET['foolevel'] == 10) {
include "really.inc";
}
else {
include "simple.inc";
}
You could use the PECL extension
runkit_function_redefine — Replace a function definition with a new implementation
but that is bad practise in my opinion. You are using functions, but check out the Decorator design pattern. Can borrow the basic idea from it.
No this will be a problem.
PHP Variable Functions
Depending on situation where you need this, maybe you can use anonymous functions like this:
$greet = function($name)
{
echo('Hello ' . $name);
};
$greet('World');
...then you can set new function to the given variable any time
A solution for the related case where you have an include file A that you can edit and want to override some of its functions in an include file B (or the main file):
Main File:
<?php
$Override=true; // An argument used in A.php
include ("A.php");
include ("B.php");
F1();
?>
Include File A:
<?php
if (!#$Override) {
function F1 () {echo "This is F1() in A";}
}
?>
Include File B:
<?php
function F1 () {echo "This is F1() in B";}
?>
Browsing to the main file displays "This is F1() in B".