I'm building an MVC framework in PHP that will need to set a number of default configuration variables/constants. Example config vars would be where to save log files, whether to log queries, Doctrine settings, amongst many others. Because I want developers to be able to create new projects with minimal fuss, these config vars should have default values. However to make this framework truly useful, I need them to be able to override these default values either in a project bootstrap file, or from within a controller or model. I would love to use constants, but they cannot be overwritten. I feel as though there must be a simple solution that I just don't see (perhaps a design pattern?). Any advice would be greatly appreciated, thanks.
In a situation like this, I would probably :
Create a class that deal with everything configuration-related
That class would contain methods to get/set configurations options ; whatever your application needs
It would also define default values, when suitable
Use a .ini or .xml file, in which configuration values can be re-defined
When instanciating the class :
You already have the default values
You parse the .ini or .xml file
Each value defined in that configuration file is used to override the corresponding default value that what defined in the class.
A solution a bit more complex, but maybe better, might be to :
Still have that configuration class, but not use it to store any default values
Have one .ini or .xml file to store default values
Have one .ini or .xml file in which people can override the values defined in the default one
Load the file containing the default values, and then the one containing the specific ones
Advantages of this solution are :
No configuration value stored in a PHP class
All configuration options that can be overriden are already defined in a .ini / .xml file, which means people just have to copy/paste a line to their specific file to override : no need to go take a look at the PHP class and think "how do I translate this to a config file ?"
A couple more notes :
You might want to use some kind of caching-mecanism, to not re-parse the files at each request
Zend_Config and Zend_Config_Ini might be helpful ; even if you don't use those, as you are writing your own framework, you might want to take a look at what they do -- If I remember correctly, Zend_Config_Ini allows for hierarchy and inheritance in .ini files
Have fun!
I'd suggest using several ini files: a default.ini, and then as much override.ini-s as you need. Then simply load them with parse_ini_file() and merge into one config with array_merge(). Quick and simple.
Here's the design pattern I would use. I would create a simple class to facilitate it:
class Configuration {
String get($key) {...}
String set($key, $value) {...}
}
Initial implementation could all be hard-coded with default values. Later, though, you could slip in the reading of a server and/or project specific configuration file. It would give you flexibility to add as needed.
(I also noticed that the php.ini configurations seem to have the behavior you want, but I don't see how you leverage that system directly.)
This question can come with a wide variety of answers. My personal recomendation would be to store values into SQLite and have a seperate script to access and change those values. For other way continue to read.
This is pretty simple depending on how object oriented you want to be and how simplified you want to make things for your users. Your install instructions could simply instruct users to edit a file directly. In which case you could simply instruct users to edit a file of constants directly.
Most applications that follow this route implement it with detail explination
<?php
/**
* Global application configuration
*/
class AWConfig {
/**
* true or false
* If the is set to true debug messages will be added to the application logs
*/
public $DEBUG_MODE = true;
/**
* Path to Station database
* ex (/homepages/26/3/htdocs/databases/stations.db)
*/
public $DB_STATION = '/homepages/26/3/htdocs/databases/stations.db';
/**
* Path to logs database
* ex (/homepages/26/3/htdocs/databases/stations.db)
*/
public $DB_LOGS = '/homepages/26/d175338743/htdocs/weather/dev/metrics/beta2/databases/metriclogs.db';
/**** DO NOT EDIT BELOW THIS LINE *****/
public $LIST_STATION_LIMIT = 10;
public $MAX_COMPARE = 6;
}
?>
If you want to hide these details from the user than an initial setup script would be best that would prompt user for details and write a file (config.php, config.ini, config.xml, or to a database) the settings they chose. Then a different script to edit in the future. Good example would be Joomla CMS.
Related
I have a config file which is written in PHP as below,
<?php
class WebConfig {
public static $WEBPATH = '/customers';
public static $ACCOUNTPATH = '/empaccountpath';
public static $INFO = '/accountInfo';
const ACCOUNT_STATUS = '/account_status';
const ENABLE_SEARCH = '/enable_search';
}
?>
So I would like to develop an interface in PHP, which has ability to edit the file values such as $WEBPATH, $ACCOUNTPATH and const values.
Simple PHP editor script will do the above work. But it doesn't check the syntax.
Please suggest how to do it in PHP efficiently.
Better solutions
Many other configuration storage formats are better suited for this kind of thing. Look into php file returns array, ini, json, xml or yaml.
"PHP file returns array" is a simple PHP file which looks like this
return(
array(
'config_key' => 'config_value'
)
);
?>
The return value of this file can be retrieved by your code when you're including it: $x = include('file.php'); and $x will have the values in the array.
INI is simple & intuitive to read or write for humans. It has a limited structure. PHP can read it with one function but it has write capabilities only in a separate (non-default) package. Which means you have to generate the ini files yourself.
JSON is "fairly" simple & intuitive to read or write for humans. It has a flexible structure extensible structure. PHP can read and write to a JSON file using only a couple of functions. Unfortunately PHP does not retain JSON pretty-print formatting so after you overwrite a file, it'll be all in one line, harder to read after that.
XML is simple & intuitive to read for humans, it can be very informative because it's quite verbose about what everything is. I has a structure almost as flexible as JSON and it is extensible. PHP can read and write to an XML but doing so means using a handful of lines of code (5-10 for simple stuff).
YAML is another option which is easy to read and write for humans, PHP doesn't have direct YAML support but there are other options (see below). It's structure is flexible and extensible. For me personally understanding YAML has been less intuitive.
Zend_Config can be used as an interface to reading/writing any of the above formats and can be used to abstract the file format itself and offer your application a format-agnostic way of handling configurations.
You could also use the database to store your configuration or a separate SQLite database dedicated to storing configurations in general -- this is usually used when many configurations need to be retained in a fine grained searchable format that allows various types of layered overriding (ex.: general defaults, controller defaults, action defaults, particular case defaults).
How to do it in PHP
You don't need to create a language parser as #geomagas said. Using include or require is enought, the PHP interpreter will load the "new" class into memory and ensure it is available.
All you need to do is create a template file in which to replace some values such as:
<?php
class WebConfig {
public static $WEBPATH = '$_replace_webpath';
public static $ACCOUNTPATH = '$_replace_accountpath';
public static $INFO = '$_replace_info';
const ACCOUNT_STATUS = '$_replace_account_status';
const ENABLE_SEARCH = '$_replace_enable_search';
}
And then load read the file, and replace it with the current values such as:
$config_template = file_get_contents('/path/to/config/template.php.template');
str_replace(
array('$_replace_webpath' ... ),
array('/customers' ... ),
$config_template
);
PrestaShop uses PHP files for configuration. It rewrites them when needed.
Directly to access the PHP Class file too danger....Many security issues....
Could we use simple html form to let user to edit.
Using JSON format to store into file.
PHP encode & decode by json_encode() & json_decode(), when save & read the value.
I am using codeigniter for a project that is used by a variety of companies.
The default version of our software is up and running and works fine - however some of our customers want slightly different view files for their instance of the system.
Ideally what I would like to do is set a variable (for example VIEW_SUFFIX) and whenever a view file is loaded it would first check if there was a suffix version available if there was use that instead.
For example if the system had a standard view file called 'my_view.php' but one client had a VIEW_SUFFIX of 'client_1' - whenever I called $this->load->view('my_view') if the VIEW_SUFFIX was set it would first check if my_view_client_1 existed (and if it did use that) or if not use the default my_view.php.
I hope that my question is clear enough... If anyone has done this before or can think of a way to do it I would really appreciate it.
EDIT:
Ideally I would like a solution that works without me changing every place that I am calling the view files. Firstly because there are a few files that may want different client versions and also because the view files are called from a lot of controllers
I had a similar requirement for which I created a helper function. Among other things, this function can check for a suffix before loading the specified view file. This function can check for the suffix and check if the file exists before loading it.
Unfortunately, the file checking logic would be a bit brittle. As an alternative, you can implement a MY_Loader class that will override the basic CI_Loader class.
Something like this in your application/core/MY_Loader.php:
class MY_Loader extends CI_Loader {
protected function _ci_load($_ci_data)
{
// Copy paste code from CI with your modifications to check prefix.
}
}
Could you not do this
// some method of creating $client
// probably created at login
$_SESSION['client'] = 'client_1';
$client = (isset($_SESSION['client'])) ? $_SESSION['client'] : '';
$this->load->view("your_view{$client}", $data);
I have a system.xml in my module, which starts with this:
<config>
<sections>
<dev>
<groups>
<my_module>
<label>...
I want to get the value of this label, from a different module. How do I do it? My first thought, was Mage::getConfig('sections/dev/groups/my_module/label'), but this doesn't work - it seems the <sections> area of the config is not accessible. I also can't figure out where magento is loading this value, which it must do at some point, or it wouldn't be able to display it.
To be completely clear: I am not trying to get the config data value as stored in the core_config_data table, that's no trouble. I want to be able to get the other attributes relating to it - like the group label, or the sort order of the fields, and to do that I need to be able to read the <sections> area of the config.
The system.xml files are never merged with the global configuration. They're only loaded when Magento builds the user interface for the
System -> Configuration
section of the backend admin application. Other than that the application has no use for them.
If you want to grab the label, you'll need to load the full system.xml configuration yourself. Something like this should work.
//load and merge `system.xml` files
$config = Mage::getConfig()->loadModulesConfiguration('system.xml');
//grab entire <sections/> node
var_dump($config->getNode('sections')->asXml());
//grab label from a specific option group as a string
var_dump((string)$config->getNode('sections/dev/groups/restrict/label'));
As mentioned in another answer in this thread, there's also an adminhtml/config model class which wraps some of this logic in a getSection method, so you could do something like this.
Mage::getSingleton('adminhtml/config')->getSection('dev')->groups->my_module->label
If you look at the source of getSection
#File: app/code/core/Mage/Adminhtml/Model/Config.php
public function getSections($sectionCode=null, $websiteCode=null, $storeCode=null)
{
if (empty($this->_sections)) {
$this->_initSectionsAndTabs();
}
return $this->_sections;
}
and follow the call stack through to _initSectionsAndTabs
#File: app/code/core/Mage/Adminhtml/Model/Config.php
protected function _initSectionsAndTabs()
{
$config = Mage::getConfig()->loadModulesConfiguration('system.xml')
->applyExtends();
Mage::dispatchEvent('adminhtml_init_system_config', array('config' => $config));
$this->_sections = $config->getNode('sections');
$this->_tabs = $config->getNode('tabs');
}
You'll see this wrapper method eventually calls the loadModulesConfiguration method itself. The additional applyExtends if an old bit of meta-programming in the configuration you can read about here, which is part of a longer series on configuration loading. (self-links, too long for a StackOverflow answer).
The reason I personally wouldn't use this to grab values out of the configuration is the event that's dispatched when you make this call
Mage::dispatchEvent('adminhtml_init_system_config', array('config' => $config));
This event may trigger code in your system that assumes you're loading the system configuration system in the backend admin console area. If you just want to read the XML tree. simply loading it yourself and reading the values seems the way to go. Your use case, of course, may vary.
As so often seems to be the case, I find the answer moments after posting the question...
This is how to get sections/dev/my_module/label:
Mage::getSingleton('adminhtml/config')->getSection('dev')->groups->my_module->label
As you can see, you need to use Mage::getSingleton('adminhtml/config')->getSection('dev') to get the backend config (you can also use ->getSections() to get all the sections to iterate over). This returns a Mage_Core_Model_Config_Element Object, which is the root of a tree of objects, accessible as shown. Just do a print_r at any stage and you'll see the rest of the tree, which print_r formats like an array, although it's not.
I want to override the guestbook functionality. To be exact, I want to override the action_form_save_entry() function on [mysite]/concrete5/core/controllers/blocks/guestbook.php
I've tried to override it these ways:
[mysite]/controllers/blocks/guestbook.php
[mysite]/core/controllers/blocks/guestbook.php
noe of them works. I can't find any way how to override that file. The documentation here and here doesn't show how to override that /core/ directory. Their forum never helps. Google result also just get misled with the 'core' keyword. All the result just take the 'core' meaning as just what's exist on the /concrete5/ directory, not the exact true /concrete5/core
Looks like that /concrete5/core/ directory appear only on the newer version. CMIIW.
Btw, maybe I should also tell you what I want to do with that function. Probably you have another workaround for this instead of simply overriding it. I want to add SMS notification functionality to it. So whenever someone submit a new comment, an SMS would be sent to the admin of a particular page.
Yes, the /concrete/core directory structure is new to 5.6. Tutorials and documentation on c5 can be ... lacking ... but in this case it's just a matter of them being behind a bit.
The "real" guestbook controller is at /concrete/blocks/guestbook/controller.php. You'll notice that it's just a shell of a class:
class GuestbookBlockController extends Concrete5_Controller_Block_Guestbook {}
The file that you referenced defines Concrete5_Controller_Block_Guestbook.
So, the solution is to override the real controller, not whatever it extends (ie, the file that you were looking at). Thinking in this way, it should be clearer that you need to create a file at /blocks/guestbook/controller.php. In fact, just copy the controller.php that I referenced above because you need to keep the (sometimes multiple) classes. Then, you can override the particular function. (Don't forget to call parent::action_save_form_entry()).
I've used codeigniter in the past but on my current project I'm making the switch to Kohana. What is the best practice on constants?
In codeigniter there is the actual constants.php, but going through Kohana's source I'm not seeing something similar.
Never used kohana, but after a quick googling I find that you can use the config API to create your own config that will house the constants you need.
This thread suggests that if you are storing database sensitive items, to place them in the database.php config, etc.. making them relative to the type of data they are storing.
I'm familiar with Kohana, but not CI so much, so I'm guessing a bit to what you mean by 'constants.' I believe the closest thing to this is indeed Kohana's config API. So, if you wanted to make templates aware of some site-wide constant like your site name, that's a great thing to use the config API for.
To accomplish this, you'll need to create a config file under your /config folder, probably in the /application directory. What you call it isn't very important, but since it contains site information, let's call it site.php.
To quickly get going, here is what you'll want to have in that file:
<?php defined('SYSPATH') or die('No direct script access.');
return array(
// Your site name!
'name' => 'Oh me, Oh my',
);
Now, you can bring this in to a template by doing something like:
A better way to do this (using dumb templating) would be to assign this as a template variable in your Controller. So, assuming you have some default controller set up, the code would be:
public function action_index() {
$this->template->site_name = Kohana::config('site.name');
}
And then your template would have something like this:
<title><?php echo $site_name; ?></title>
Kohana's config API is interesting because it is hierarchical, meaning you can override and merge new configuration values on top of existing config structures. When you call Kohana::config('site.name'), the engine looks through all the config files named site.php, runs all of those config files and merges the results in to an array. The application-level config files will overwrite modules, which will overwrite system, etc... Then, based on that result array, Kohana will attempt to find the 'name' key and return it.
Assuming you want global constants...the following worked well for me.
application/config/constants.php:
define('MY_COOL_CONSTANT', 'foo');
return array();
index.php:
Kohana::$config->load('constants');
MY_COOL_CONSTANT should then be available globally.