Is it bad practice to use variable variables in PHP? - php

For example, a simple MVC type system:
/api/class/method rewritten into PHP variables using .htaccess/nginx.conf, then doing something like:
<?php
// Set up class + method variables
$className = some_class_filter($_GET['class']);
$method = some_method_filter($_GET['method']);
// Check if class exists and execute
if(file_exists(BASE . "/controllers/" . $className . ".class.php")) {
require BASE . "/controllers/" . $className . ".class.php";
$$className = new $className();
// Execute the method
$$className->$method();
} else {
// Spit out some error based on the problem
}
?>
Is this horribly bad practice? If it is bad practice, can someone explain exactly why? And if so, is there a better way to do what I'm doing?
EDIT Essentially the reason I'm using variable variables is to make it simple to expand the core system - i.e. - adding in a new controller is nice and simple. I definitely understand the security risks of allowing essentially any function or class to be instantiated without some kind of filter.
The 'some_filter_here' could be a list of controllers that are allowed - whitelist as some here have mentioned.

Yes, this is rather bad practise. Do you need a variable variable for that instance? In other words, do you need more than one class & method to be instantiated in a given request? Your URI structure suggests not. If not, you could just use:
$object = new $className();
$object->$method();
Otherwise, you might want to do:
$objects = array();
$objects[$className] = new $className();
$objects[$className]->$method();
This avoids polluting the scope with variable variables, which are harder to track.
As far as the existence checks for your class in a given directory, this should be a sufficient whitelist (presuming an attacker cannot write to that directory).
EDIT: As a further check, you may want to consider checking method_exists on the object before calling the method.

Since you're writing the "some_class_filter" and "some_method_filter" code, I'd say it's OK. You also have a error or default handler I see, so in the end, I'd say it's alright.
I believe many MVC frameworks operate in a similar fashion anyway.

They're not desirable, but it's fine to use them how you have.
A couple of pointers, though: your code does have a vulnerability where an attacker could traverse your directory with $_GET parameters like ?class=../base. If that file exists, your file_exists() call will return true and your application will attempt to include it and instantiate it as a class.
The safe scenario would be to whitelist those parameters to be letters, numbers and underscores only (if you separate words with underscores, i.e. .php).
Also, I prefer the syntax of using call_user_func and call_user_func_array. Using these functions in your code would look as follows:
<?php
$class_name = $_GET['class'];
$method_name = $_GET['method'];
$parameters = $_GET;
unset($parameters['class'], $parameters['method']); // grabs any other $_GET parameters
if (file_exists(BASE.'/controllers/'.$class_name.'.class.php')) {
require BASE.'/controllers/'.$class_name.'.class.php';
$controller = new $class_name();
$response = call_user_func_array(array($controller, $action_name), $parameters);
}
else {
header('HTTP/1.1 404 Not Found');
// ...and display an error message
}

Related

Php Mysqli Global DB define for Classes ? [duplicate]

I have a config.php that is included to each page. In config I create an array that looks something like:
$config = array();
$config['site_name'] = 'Site Name';
$config['base_path'] = '/home/docs/public_html/';
$config['libraries_path'] = $config['base_path'] . '/libraries';
//etc...
Then I have function.php, that is also included to almost each page, where I have to use global $config to get access to it - and this is what I would like to get rid of!
How do I access $config in the other parts of my code without using global?
Could anyone explain, WHY I shouldn't use global in my example? Some say it's a bad tone, others say it's not secure?
EDIT 1:
Example of where and how I use it:
function conversion($Exec, $Param = array(), $Log = '') {
global $config;
$cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
foreach ($Param as $s)
{
$cmd .= ' ' . $s;
}
}
EDIT 2:
Putting all of this in the class, as suggested by Vilx, would be cool but in this case, how would I tie it with the following loop that is extracting config key and value from database. I oversimplified the idea of assigning $config array, here is an example:
$sql = "SELECT * from settings";
$rsc = $db->Execute($sql);
if ( $rsc ) {
while(!$rsc->EOF) {
$field = $rsc->fields['setting_options'];
$config[$field] = $rsc->fields['setting_values'];
#$rsc->MoveNext();
}
}
EDIT 3:
Besides, I have to access other vars from functions that are set in config and it's few of them, e.g.:$db, $language and etc.
If I put them in the class will it really solve anything? If I use global what does it really change?
EDIT 4:
I read PHP global in functions where Gordon explains in the very nice way why you shouldn't use global. I agree on everything but I don't use global in my case to reassign the variables, which will result in, like he said, <-- WTF!!, ;)) yeah agree, it's crazy. But if I just need to access database from a function just by using global $db where is the problem in this case? How do you do this otherwise, without using global?
EDIT 5:
In the same PHP global in functions deceze says: "The one big reason against global is that it means the function is dependent on another scope. This will get messy very quickly."
But I'm talking here about basic 'INIT'. I basically set define but use vars - well that is wrong in technical way. But your function is not depended on anything - but the name of one var $db that you could keep in mind? It's really global need to use $db, where is the DEPENDENCY here and how to use it otherwise?
P.S. I just had a thought, that we're facing the conflict here of two different minds, e.g.: mine (yet NOT well understanding object-oriented programming) and those who could be called gurus (from my current point of view) in OOP - what looks obvious for them for me arises new questions. I think that's why this question is being asked over and over again. Personally for me it's gotten more clear after all but still there are things to clarify.
The point against global variables is that they couple code very tightly. Your entire codebase is dependent on a) the variable name $config and b) the existence of that variable. If you want to rename the variable (for whatever reason), you have to do so everywhere throughout your codebase. You can also not use any piece of code that depends on the variable independently of it anymore.
Example with global variable:
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
Anywhere in the above lines you may get an error because the class or some code in SomeClass.php implicitly depends on a global variable $config. There's no indication of this whatsoever though just looking at the class. To solve this, you have to do this:
$config = array(...);
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
This code may still fail somewhere if you do not set the correct keys inside $config. Since it's not obvious what parts of the config array SomeClass needs or doesn't need and when it needs them, it's hard to recreate the correct environment for it to run correctly. It also creates conflicts if you happened to already have a variable $config used for something else wherever you want to use SomeClass.
So instead of creating implicit, invisible dependencies, inject all dependencies:
require 'SomeClass.php';
$arbitraryConfigVariableName = array(...);
$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();
By passing the config array explicitly as a parameter, all the above problems are solved. It's as simple as handing the required information around inside your app. It also makes the structure and flow of the application and what talks to what much clearer. To get to this state if your application is currently a big ball of mud may take some restructuring.
The bigger your codebase gets, the more you have to decouple the individual parts from each other. If every part is dependent on every other part in your codebase, you simply cannot test, use or reuse any part of it individually. That simply devolves into chaos. To separate parts from each other, code them as classes or functions which take all their required data as parameters. That creates clean seams (interfaces) between different parts of your code.
Trying to tie your question together into one example:
require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';
// establishes a database connection
$db = new Database('localhost', 'user', 'pass');
// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);
// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);
// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);
// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);
I'll leave to implementation of the individual classes up as an exercise for the reader. When you try to implement them, you'll notice that they're very easy and clear to implement and do not require a single global. Every function and class gets all its necessary data passed in the form of function arguments. It should also be obvious that the above components can be plugged together in any other combination or that dependencies can easily be substituted for others. For example, the configuration does not need to come from the database at all, or the logger can log to a file instead of the database without Foo::conversion having to know about any of this.
Example implementation for ConfigManager:
class ConfigManager {
public function loadConfigurationFromDatabase(Database $db) {
$result = $db->query('SELECT ...');
$config = array();
while ($row = $result->fetchRow()) {
$config[$row['name']] = $row['value'];
}
return $config;
}
}
It's a very simple piece of code that doesn't even do much. You may ask why you'd want this as object oriented code. The point is that this makes using this code extremely flexible, since it isolates it perfectly from everything else. You give one database connection in, you get one array with a certain syntax back. Input → Output. Clear seams, clear interfaces, minimal, well defined responsibilities. You can do the same with a simple function.
The extra advantage an object has is that it even further decouples the code that calls loadConfigurationFromDatabase from any particular implementation of that function. If you'd just use a global function loadConfigurationFromDatabase(), you basically have the same problem again: that function needs to be defined when you try to call it and there are naming conflicts if you want to replace it with something else. By using an object, the critical part of the code moves here:
$config = $configManager->loadConfigurationFromDatabase($db);
You can substitute $configManager here for any other object that also has a method loadConfigurationFromDatabase. That's "duck typing". You don't care what exactly $configManager is, as long as it has a method loadConfigurationFromDatabase. If it walks like a duck and quacks like a duck, it is a duck. Or rather, if it has a loadConfigurationFromDatabase method and gives back a valid config array, it's some sort of ConfigManager. You have decoupled your code from one particular variable $config, from one particular loadConfigurationFromDatabase function and even from one particular ConfigManager. All parts can be changed and swapped out and replaced and loaded dynamically from anywhere, because the code does not depend on any one particular other piece.
The loadConfigurationFromDatabase method itself also does not depend on any one particular database connection, as long as it can call query on it and fetch results. The $db object being passed into it could be entirely fake and read its data from an XML file or anywhere else instead, as long as its interface still behaves the same.
I've solved this with a class:
class Config
{
public static $SiteName = 'My Cool Site';
}
function SomeFunction
{
echo 'Welcome to ' , Config::$SiteName;
}
fcortes' suggestion to use constants is also a good one. I'd only like to suggest to give all constants a prefix, like CFG_SITE_NAME, so to avoid accidental name clashes with other constants.
For your case I would create an only file constants.php with definitions (if your purpose is these "variables" never be changed in execution time):
define('SITE_NAME','site name');
define('BASE_PATH','/home/docs/public_html/');
...
Include this constants.php in all files where you will need it:
include_once('constants.php');
There is a big discussing between object-oriented and procedural approaches (and more generally, between declarative and imperative ones) and each approach has its upsides and downsides.
I used 'Config' class that was a Singleton (an OOP version of global). It worked good for me until I had discovered a need to use several of previously-developed solutions together in one application - since all configs were global and referred by the same class (the same variable name, in your case) they conflicted and I had to switch to proper config every time I called code from other sub-application.
You have two ways:
a) either design your application in a way you got used to and you are familiar with (that will be better because you already have experience in it and you can predict how long the development will take and what problems may or may not arise); and after you will stuck into limitations of your current approach, refactor to avoid the globals;
b) look how its done in OOP frameworks (see at least three or four, i.e. Cake, CodeIgniter, Zend, Symfony, Flow3) and either borrow something, or switch to using a framework (or maybe you will be more sure that you do everything right).
I created an easy small class:
class Config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
}
Config::set( 'my_config', 'the value' );
echo 'the config value is: ' . Config::get('my_config');
this can easly be refactored to have a function isSet( $key ) or maybe a setAll( $array ).
EDIT: Now the syntax should be valid.
you can easily modify this class like follows:
class Config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
public static function setAll( array $array ) {
self::$config = $array;
}
public static function isKeySet( $key ) {
return isset( self::$config[ $key ] );
}
}
Config::setAll( array(
'key' => 'value',
'key2' => array( 'value',
'can be an',
'array' ) ) );
Config::set( 'my_config', 'the value' );
if( Config::isKeySet( 'my_config' ) ) {
echo 'the config value is: ' . Config::get('my_config');
}
You still need to include the file in any another file that uses configs, or use an autoloader.
EDIT 2:
It's pretty much the same as using a global, with the difference you don't need to state that you want to use it in the beginning of every function. If you want to use Configs globally, then the Configs have to be, in some way global. When putting something in the global scope, you need to argue if this can be dangerous information to an other class not meant to see this information... default configurations? I think it's safe to have in the global scope, and then you just need something that is easy to modify and customize.
If you decide that it's dangerous information, that should not be reachable for a class other then the class it's meant for, then you might want to check in to Dependency injection. With dependency injections a class will take an object in it's constructor, placing it privately in a variable to use. This object can be an object from a configuration class, and then you need a wrapper class creating first the configuration object, and then the Template object injecting the configurations. This is a design often seen in more complex design patterns, like for instance Domain Driven Design.
config.php
<?php
class config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
if( config::isKeySet( $key ) ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
}
public static function setAll( array $array ) {
self::$config = $array;
}
public static function isKeySet( $key ) {
return isset( self::$config[ $key ] );
}
}
// set valuable values
config::setAll( array(
'key' => 'value',
'key2' => array( 'value', 'can be an', 'array' ),
'database' => array( "username" => "root", "password" => "root")
)
);
config::set( 'my_config', 'the value' );
?>
config.usage.php
<?php
require_once 'config.php';
$database_credentials = config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];
echo '<br> the config value for password is ' . $database_credentials['password'];
function additionalFunctionality($database_credentials)
{
echo '<br> the config value for password is ' . $database_credentials['password'];
}
?>
config.usage.too.php
<?php
require_once 'config.php'; // put this first
require_once 'config.usage.php'; // include some functionality from another file
$database_credentials = Config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];
additionalFunctionality($database_credentials); // great
?>

Stop using `global` in PHP

I have a config.php that is included to each page. In config I create an array that looks something like:
$config = array();
$config['site_name'] = 'Site Name';
$config['base_path'] = '/home/docs/public_html/';
$config['libraries_path'] = $config['base_path'] . '/libraries';
//etc...
Then I have function.php, that is also included to almost each page, where I have to use global $config to get access to it - and this is what I would like to get rid of!
How do I access $config in the other parts of my code without using global?
Could anyone explain, WHY I shouldn't use global in my example? Some say it's a bad tone, others say it's not secure?
EDIT 1:
Example of where and how I use it:
function conversion($Exec, $Param = array(), $Log = '') {
global $config;
$cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
foreach ($Param as $s)
{
$cmd .= ' ' . $s;
}
}
EDIT 2:
Putting all of this in the class, as suggested by Vilx, would be cool but in this case, how would I tie it with the following loop that is extracting config key and value from database. I oversimplified the idea of assigning $config array, here is an example:
$sql = "SELECT * from settings";
$rsc = $db->Execute($sql);
if ( $rsc ) {
while(!$rsc->EOF) {
$field = $rsc->fields['setting_options'];
$config[$field] = $rsc->fields['setting_values'];
#$rsc->MoveNext();
}
}
EDIT 3:
Besides, I have to access other vars from functions that are set in config and it's few of them, e.g.:$db, $language and etc.
If I put them in the class will it really solve anything? If I use global what does it really change?
EDIT 4:
I read PHP global in functions where Gordon explains in the very nice way why you shouldn't use global. I agree on everything but I don't use global in my case to reassign the variables, which will result in, like he said, <-- WTF!!, ;)) yeah agree, it's crazy. But if I just need to access database from a function just by using global $db where is the problem in this case? How do you do this otherwise, without using global?
EDIT 5:
In the same PHP global in functions deceze says: "The one big reason against global is that it means the function is dependent on another scope. This will get messy very quickly."
But I'm talking here about basic 'INIT'. I basically set define but use vars - well that is wrong in technical way. But your function is not depended on anything - but the name of one var $db that you could keep in mind? It's really global need to use $db, where is the DEPENDENCY here and how to use it otherwise?
P.S. I just had a thought, that we're facing the conflict here of two different minds, e.g.: mine (yet NOT well understanding object-oriented programming) and those who could be called gurus (from my current point of view) in OOP - what looks obvious for them for me arises new questions. I think that's why this question is being asked over and over again. Personally for me it's gotten more clear after all but still there are things to clarify.
The point against global variables is that they couple code very tightly. Your entire codebase is dependent on a) the variable name $config and b) the existence of that variable. If you want to rename the variable (for whatever reason), you have to do so everywhere throughout your codebase. You can also not use any piece of code that depends on the variable independently of it anymore.
Example with global variable:
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
Anywhere in the above lines you may get an error because the class or some code in SomeClass.php implicitly depends on a global variable $config. There's no indication of this whatsoever though just looking at the class. To solve this, you have to do this:
$config = array(...);
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
This code may still fail somewhere if you do not set the correct keys inside $config. Since it's not obvious what parts of the config array SomeClass needs or doesn't need and when it needs them, it's hard to recreate the correct environment for it to run correctly. It also creates conflicts if you happened to already have a variable $config used for something else wherever you want to use SomeClass.
So instead of creating implicit, invisible dependencies, inject all dependencies:
require 'SomeClass.php';
$arbitraryConfigVariableName = array(...);
$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();
By passing the config array explicitly as a parameter, all the above problems are solved. It's as simple as handing the required information around inside your app. It also makes the structure and flow of the application and what talks to what much clearer. To get to this state if your application is currently a big ball of mud may take some restructuring.
The bigger your codebase gets, the more you have to decouple the individual parts from each other. If every part is dependent on every other part in your codebase, you simply cannot test, use or reuse any part of it individually. That simply devolves into chaos. To separate parts from each other, code them as classes or functions which take all their required data as parameters. That creates clean seams (interfaces) between different parts of your code.
Trying to tie your question together into one example:
require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';
// establishes a database connection
$db = new Database('localhost', 'user', 'pass');
// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);
// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);
// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);
// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);
I'll leave to implementation of the individual classes up as an exercise for the reader. When you try to implement them, you'll notice that they're very easy and clear to implement and do not require a single global. Every function and class gets all its necessary data passed in the form of function arguments. It should also be obvious that the above components can be plugged together in any other combination or that dependencies can easily be substituted for others. For example, the configuration does not need to come from the database at all, or the logger can log to a file instead of the database without Foo::conversion having to know about any of this.
Example implementation for ConfigManager:
class ConfigManager {
public function loadConfigurationFromDatabase(Database $db) {
$result = $db->query('SELECT ...');
$config = array();
while ($row = $result->fetchRow()) {
$config[$row['name']] = $row['value'];
}
return $config;
}
}
It's a very simple piece of code that doesn't even do much. You may ask why you'd want this as object oriented code. The point is that this makes using this code extremely flexible, since it isolates it perfectly from everything else. You give one database connection in, you get one array with a certain syntax back. Input → Output. Clear seams, clear interfaces, minimal, well defined responsibilities. You can do the same with a simple function.
The extra advantage an object has is that it even further decouples the code that calls loadConfigurationFromDatabase from any particular implementation of that function. If you'd just use a global function loadConfigurationFromDatabase(), you basically have the same problem again: that function needs to be defined when you try to call it and there are naming conflicts if you want to replace it with something else. By using an object, the critical part of the code moves here:
$config = $configManager->loadConfigurationFromDatabase($db);
You can substitute $configManager here for any other object that also has a method loadConfigurationFromDatabase. That's "duck typing". You don't care what exactly $configManager is, as long as it has a method loadConfigurationFromDatabase. If it walks like a duck and quacks like a duck, it is a duck. Or rather, if it has a loadConfigurationFromDatabase method and gives back a valid config array, it's some sort of ConfigManager. You have decoupled your code from one particular variable $config, from one particular loadConfigurationFromDatabase function and even from one particular ConfigManager. All parts can be changed and swapped out and replaced and loaded dynamically from anywhere, because the code does not depend on any one particular other piece.
The loadConfigurationFromDatabase method itself also does not depend on any one particular database connection, as long as it can call query on it and fetch results. The $db object being passed into it could be entirely fake and read its data from an XML file or anywhere else instead, as long as its interface still behaves the same.
I've solved this with a class:
class Config
{
public static $SiteName = 'My Cool Site';
}
function SomeFunction
{
echo 'Welcome to ' , Config::$SiteName;
}
fcortes' suggestion to use constants is also a good one. I'd only like to suggest to give all constants a prefix, like CFG_SITE_NAME, so to avoid accidental name clashes with other constants.
For your case I would create an only file constants.php with definitions (if your purpose is these "variables" never be changed in execution time):
define('SITE_NAME','site name');
define('BASE_PATH','/home/docs/public_html/');
...
Include this constants.php in all files where you will need it:
include_once('constants.php');
There is a big discussing between object-oriented and procedural approaches (and more generally, between declarative and imperative ones) and each approach has its upsides and downsides.
I used 'Config' class that was a Singleton (an OOP version of global). It worked good for me until I had discovered a need to use several of previously-developed solutions together in one application - since all configs were global and referred by the same class (the same variable name, in your case) they conflicted and I had to switch to proper config every time I called code from other sub-application.
You have two ways:
a) either design your application in a way you got used to and you are familiar with (that will be better because you already have experience in it and you can predict how long the development will take and what problems may or may not arise); and after you will stuck into limitations of your current approach, refactor to avoid the globals;
b) look how its done in OOP frameworks (see at least three or four, i.e. Cake, CodeIgniter, Zend, Symfony, Flow3) and either borrow something, or switch to using a framework (or maybe you will be more sure that you do everything right).
I created an easy small class:
class Config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
}
Config::set( 'my_config', 'the value' );
echo 'the config value is: ' . Config::get('my_config');
this can easly be refactored to have a function isSet( $key ) or maybe a setAll( $array ).
EDIT: Now the syntax should be valid.
you can easily modify this class like follows:
class Config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
public static function setAll( array $array ) {
self::$config = $array;
}
public static function isKeySet( $key ) {
return isset( self::$config[ $key ] );
}
}
Config::setAll( array(
'key' => 'value',
'key2' => array( 'value',
'can be an',
'array' ) ) );
Config::set( 'my_config', 'the value' );
if( Config::isKeySet( 'my_config' ) ) {
echo 'the config value is: ' . Config::get('my_config');
}
You still need to include the file in any another file that uses configs, or use an autoloader.
EDIT 2:
It's pretty much the same as using a global, with the difference you don't need to state that you want to use it in the beginning of every function. If you want to use Configs globally, then the Configs have to be, in some way global. When putting something in the global scope, you need to argue if this can be dangerous information to an other class not meant to see this information... default configurations? I think it's safe to have in the global scope, and then you just need something that is easy to modify and customize.
If you decide that it's dangerous information, that should not be reachable for a class other then the class it's meant for, then you might want to check in to Dependency injection. With dependency injections a class will take an object in it's constructor, placing it privately in a variable to use. This object can be an object from a configuration class, and then you need a wrapper class creating first the configuration object, and then the Template object injecting the configurations. This is a design often seen in more complex design patterns, like for instance Domain Driven Design.
config.php
<?php
class config {
private static $config = array();
public static function set( $key, $value ) {
self::$config[$key] = $value;
}
public static function get( $key ) {
if( config::isKeySet( $key ) ) {
return isset( self::$config[$key] ) ? self::$config[$key] : null;
}
}
public static function setAll( array $array ) {
self::$config = $array;
}
public static function isKeySet( $key ) {
return isset( self::$config[ $key ] );
}
}
// set valuable values
config::setAll( array(
'key' => 'value',
'key2' => array( 'value', 'can be an', 'array' ),
'database' => array( "username" => "root", "password" => "root")
)
);
config::set( 'my_config', 'the value' );
?>
config.usage.php
<?php
require_once 'config.php';
$database_credentials = config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];
echo '<br> the config value for password is ' . $database_credentials['password'];
function additionalFunctionality($database_credentials)
{
echo '<br> the config value for password is ' . $database_credentials['password'];
}
?>
config.usage.too.php
<?php
require_once 'config.php'; // put this first
require_once 'config.usage.php'; // include some functionality from another file
$database_credentials = Config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];
additionalFunctionality($database_credentials); // great
?>

is it acceptable to recycle or reuse variables?

I've looked everywhere, and I can't seem to find an answer one way or another. Is it acceptable (good or bad practice) to reuse or recycle a variable? It works, and I've used this method a few times, but I don't know if I should be doing this or not. I'm trying to get away from using static methods, and moving into dependency injection.
in this example, the $table_name is set elsewhere.
class DbObject {
private $db = NULL;
protected $table_name;
public function __construct($dbh, $item) {
$this->db = $dbh;
$this->$table_name = $item;
}
// counts items in database //
public function count_all() {
try {
$sql = 'SELECT COUNT(*) FROM ' . $this->table_name;
$stmt = $this->db->query($sql);
$stmt->setFetchMode(pdo::FETCH_COLUMN, 0);
$result = $stmt->fetchColumn();
return $result;
} catch (PDOException $e) {
echo $e->getMessage());
}
}
To use this I would use it like this:
$total_count = new DbObject(new Database(), 'items');
$total_count = $total_count->count_all();
Is this an acceptable way to code?
Thanks for your help.
Reusing variables for different purposes is a maintenance error waiting to happen.
It's bad practice. A well named variable can do wonders for aiding code comprehension.
Reusing variables is especially fragile in weakly typed dynamic languages such as PHP.
[In the past, I have been guilty of errors in code when reusing local loop variables like i and j ...]
The main reason to avoid reusing variables is that if you reuse a variable without properly re-initializing it, the old value will "leak" through, causing unpredictable effects and even security vulnerabilities. For example:
$foo = $_GET['input'];
# use $foo
if ($a == $b) {
$foo = 1;
} else {
# $foo = 2; # Commented out for some reason
}
# Value $foo supplied in URL leaks through to here
Generally, reuse of variables will not damage performance if the compiler uses single-static assignment form (SSA) as an intermediate form during optimization (part of what SSA does is giving separate names to such reused variables). So don't worry about performance - worry about maintainability.
What about when you need call another method of the DbObject ?
I prefer give the variable name what it is:
$dbo = new DbObject(new Database(), 'items');
$total_count = $dbo->count_all();
//so you call still do things next
$result = $dbo->get_all();
If you like using common names often through out a script (e.g. $i for counting iterations in a loop) you should have no problems if you make it a habit to call unset() whenever you're done with using a variable in a particular case.
People tend to re-use "throw-away" variables like i and j, but it is usually bad form to reuse other local variables for a different purpose. That leads to reader confusion about where a variable is initialized and whether updates in one part of the code will affect another part of the code. There may also be ill-effects on a compiler's optimization suite (perhaps suggesting a variable should be saved when it isn't necessary).
Sometimes the variables could conflict or you could get them mixed out. So I would only reuse $i and $row unless you unset() the variable when you are done.

A potential PHP memory hog or not?

Hey, i'm in need of advice. Is this little function i wrote "good" or is it going to be a resource hog?
It's used in the following way:
$login = load_class('LoginClass');
or
load_class('LoginClassTwo', false); // for singletons and stuff
$loginTwo = LoginClassTwo::getInstance();
Here's the function
function load_class($class, $instantiate = TRUE){
static $obj = array(); // holds the instancec of the classes
$l = strtolower($class); // the name of the file is the name of the class but lowercased
if (isSet($obj[$l])) { // Do we have an instance?
return $obj[$l];
}
$file = 'classess/' . $l . '.class.php';
if (file_exists($file) && is_readable($file)) { // Can we read the file?
include $file;
if ($instantiate == FALSE) { // Do we need to instantiate?
$obj[$l] = TRUE;
} else {
$obj[$l] = new $class;
}
return $obj[$l];
}
return FALSE; }
I'm concerned that this method is ineffective and it's going to consume too much memory or am i wrong? And is there a better practice for this?
This is a common pattern, known as a registry, or service locator.
There can be an issue with a global registry of objects, in that these objects are not reclaimed until the script ends. If one of those objects uses a lot of memory, then there you go. However, in it self, this isn't a problem, memory wise.
You should consider which objects you want to hold on globally though. It's a generally accepted truthism that global objects contribute to the overall complexity and coupling of a program. Maybe you could pass some of them as parameters in the constructor, rather than addressing them globally? That entirely depends on the use case of course.
Lastly - php has a feature called autoload, whereby it will load a class from file, if it's not already defined. You should hook in to this instead of putting the logic in your registry.
I don't see any memory hog kind of thing in your code.. If you experiance any such thing. it is probably the class you are loading
PHP has a native __autoload function for classes. it is run any time you attempt to create a new object from a class that does not yet exist. the function will try to include a class file using the name of the class as the class file. this is why many projects use one file per class. this way you never need to manually load a class again, and classes never get loaded unless needed. See the following link.
http://www.php.net/manual/en/language.oop5.autoload.php

"__class magic method" (mediating references to class names by a custom code)

You can redirect calls to some properties/functions by using __get, __call.
Is there a way to do it for classes?
I would like to convert all mentions of some_class_name in the code to, MY_VERSION_some_class_name (not only for one class, it's a pattern).
This would be easy if methods / properties were the target of this renaming policy.
Can you think of a way to do it for classes in PHP?
Edit: I need this for referencing to different variants of classes in different situations. I need one class name to be resolved to different variants of this class, depending on a condition known at runtime, and persistent through the whole session.
Thanks
p.s.
If you are curious why I want to do this, look at Maintaining variants of an application
You can't convert all mentions of some_class_name in the code to another class. However, you can use variables as class names:
$className = "MyClass";
$obj = new $className;
$className::myMethod();
All you have to do is change the variable and you will be using a different class. If you have to do this for a lot of classes, you might want to create some sort of factory for it.
$factory = System::getFactory();
$obj = $factory->getAuthObj();
Basically, the System class would return a different object based on what class needed to be used for that particular run time.
Aiden's untested approach: variable variables and generating a string:
<?php
$dog = "12";
function getDogClass($myNum)
{
global $dog;
// Do something dynamic here;
$dog = "Dog_" . $myNum;
return "dog";
}
class Dog_13rc1
{
function __construct()
{
printf("Woof!");
}
}
class Dog_12
{
function __construct()
{
printf("Grrrr");
}
}
$myObj = new ${getDogClass('13rc1')}();
?>
As long as the call to getDogClass() returns a string of the name of a variable in scope then you are good.
The output is woof.
This means the only refactoring you need is to find/replace occurences of the class name with that call.
But this is grim. and probably slow. Also, a maintenance/bug-tracking nightmare type of hack.
The magic function you want is __autoload:
function __autoload($class_name) {
// include your special class in here
include $class_name . '.php';
}
Read more here: http://php.net/manual/en/language.oop5.autoload.php

Categories