I have a simple importer class that logs success and failure statuses to a log file.
I have made the log file name a constant in the class like so:
class MyClass
{
const STATUS_LOG = "my_log.log";
public function doImport()
{
// do import here and log result
}
}
Currently i know of no reason that different logs would be used, but would it be better to allow that flexibility and do the following instead:
class MyClass
{
private $statusLog;
public function __construct($statusLog)
{
$this->statusLog = $statusLog;
}
public function getStatus()
{
return $this->statusLog;
}
public function setStatusLog($statusLog)
{
$this->statusLog = $statusLog;
}
public function doImport()
{
// do import here and log result
}
}
Given i currently have no use for different log files, is there any benefit in the second approach?
I think that in terms of logging you should not allow to change log path. Not in runtime - since there's a question - what will happen with data integrity if log path will changed 'on hot'? Flexibility sounds good, but I think this is not a case when you should allow change your property in runtime.
If you're hesitating about log path then it should be adjustable via configuration file - i.e. read once at application's start. So you will not store path in your class, reading it from config instead (in __construct() for your class).
If you have a static logfile path that doesn't change you can stay at the first one.
If it should be able to change the log path (weather now or in planned future) I would use the second one. (The benefit is just to be able to set and get the logpath, a getter can also be used for the firse solution).
This is a no-brainer. The second approach is more flexible and more explicit. Also, the log file name is not a "real" constant in the sense that, say, pi or equator length are. When defined as constant, it's just a "magic value" you'll sooner or later forget that it's there.
I'd get rid of the setter method though and only allow setting the value in constructor.
Your class might violate the single responsibility principle: from the doImport method I conclude that the first responsibility of your class is importing and it should not concern itself with the details of logging (formatting, what file to use, what to log in development/production environment). Why not pass in a logger class (preferably a class that implements the PSR-3 interface) in the constructor? Monolog is a fantastic and flexible logging library.
If you keep the filename (or better the logger) flexible, you can write unit tests for your class without having to worry about overwriting important files. If you use a memory/dummy logger class, you don't even need a file system!
If you just write a small one-off script that will not be tested however, then using a constant is a good way for configuration IHMO.
I am trying to write unit tests for a file import export module.
One of my method checks that a filename passed exists.
How would this be mocked and a test written to check if the file exists or not?
Unit tests are supposed to prove that a unit of code functions correctly in complete isolation. If your test depends on the file system to function correctly in order to pass, your test is suboptimal and could be lying to you on any given test run.
Like any experiment, when you have more than one variable in play, you can't be sure of your results. For PHP code that interacts with the file system, it's best to mock the file system using a custom stream wrapper (usually vfsStream, but you can easily write your own stream wrapper if you really want to).
$noTest < $testWithFileSystemDependency < $testThatMocksFileSystem
Usually, this is accomplished by passing file paths into the methods that use them directly:
<?php
function myFunction($someFilePath) {
// do stuff
}
In this way, you can mock the file system and pass in a testable dummy path that will behave how you have mocked it to behave.
You write two tests. One creates the file and expects your method to succeed, the other ensures the file doesn't exist and expects your method to fail.
Personally I just create _files directory in the tests directory and create files there. Or in /tmp.
There is a way to mock the fs: http://www.phpunit.de/manual/current/en/test-doubles.html#test-doubles.mocking-the-filesystem but from my experience - I prefer real FS operations (in this case).
Is it possible to save the config for HTMLPurifier in a separate file so that I don't have to enter the config lines every time I create an instance?
I am using the standalone version as follows:
library/HTMLPurifier.php
library/standalone
You can store the config into codeigniter config file,
create a wrapper class that wrap around HTMLPurifier,
and putting all your config setting into constructor, like this :-
class Purifier extends HTMLPurifier
{
public function __construct()
{
// call parent::__construct
// load and set your configuration
// seq might affect the logic, you might want to reverse it
}
I've got some files using raw php (including config files) that's used for the automatic mailing stuff in my server.
Suppose inside this file I define a couple of constants (define(...)) and an array filled with database connection info, like user, host and so).
The website is done using Yii, so it also uses a config file.
These raw files can be placed anywhere (inside protected, outside, at the same level of index.php, whatever).
Now the problem comes that I've got a different configuration file (and different users/password for databases, and so) outside Yii, but I need to use it in some places inside Yii, too.
Is there a clear way to import these files to a controller? I've done it placing them inside extensions, but the raw functions didn't work from there.
The best approach would be to see if you can put your custom code into class files and put those in the components directory or similar and convert your files to classes (if they aren't already). That way you can get at your data without a lot of mixing of code which is going to be hard to maintain.
Simple approach will be to place the files in extensions and add the path of the extensions to your yii configuration. Then make a controller and call methods from its actions. Lets consider an example of swiftmailer. Here is a custom controller you can use.
class mailerController extends Controller{
public function actions()
{
return array(
//can add other actions here
);
}
public function actionIndex(){
echo "use mailer?e=<email>&m=<message>&sub=<subject> to send mail from the site to the email address";
}
public static function actionSendMail($e,$m,$sub){
$content = $m ; // can use a template and then assign to content
$SM = new SwiftMailer(); //the external method, should be present in include path
// Get config
$mailHost = Yii::app()->params['mailhost'];
$mailPort = 25; // Optional
$Transport = $SM->smtpTransport($mailHost, $mailPort);
$Mailer = $SM->mailer($Transport);
$Message = $SM
->newMessage($sub)
->setFrom(Yii::app()->params['sitemail'])
->setTo($e)
->addPart($content, 'text/html');
return ( $Mailer->send($Message));
} }
once your controller is ready, it can be called like
yoursite.com/mailer?e=<email>&m=<message>&sub=<subject>
I often switch between .NET and PHP development. With ASP.NET sites I save configuration information (e.g. connection strings, directories, application setting) in the web.config file which is appropriately protected and easy to access the values, etc.
In PHP, I solve this with a class that has static methods for each variable:
class webconfig {
public static function defaultPageIdCode() {
return 'welcome';
}
}
The file is included by the app variables are accessed with a one-line:
$dp = webconfig::defaultPageIdCode();
And since PHP isn't compiled, it is easy to telnet in and change a value for a website anyway, so this solution works fairly well and gives me these two advantages:
I can add logic to a config variable without breaking its interface with the application
these config variables appear as intellisense in my e.g. Eclipse, NetBeans, etc.
But I can imagine there are other ways people solve saving web config settings in PHP which may have other advantages.
Especially those who have experience with a number of PHP frameworks, what are other ways of saving config variables and their advantages and disadvantages?
I've decided to list all known methods along with their advantages and disadvantages.
I've marked this answer as a community wiki so collaboration is easier.
Global Constants
Assigning:
define('CONFIG_DIRECTIVE', 'value');
Accessing:
$object = new MyObject(CONFIG_DIRECTIVE);
Advantages:
Has global scope.
Autocompleted by most IDEs.
Has an agreed upon naming convention (UPPERCASE_UNDERSCORE_SEPARATED).
Disadvantages:
Directives cannot contain arrays (prior to v7.0.0).
Special Notes:
Cannot be reassigned.
Alternate Syntax Files
For example: XML, INI, YAML, etc.
Assigning:
Simply edit the file in it's specific language. (For example, for INI files: config_directive = value.)
Accessing:
The config file needs to be parsed. (For example, for INI's: parse_ini_file().)
Advantages:
Most likely has a syntax more suited for a config file.
Disadvantages:
Possible overhead of accessing and parsing the file.
Array
Assigning:
$config['directive'] = 'value';
Accessing:
The cleanest method of accessing configuration values using this method is to pass the required values to the object that needs them on creation, or pass them to your container object and let it handle passing them out internally.
$object = new MyObject($config['directive']);
$container = new MyContainer($config);
Advantages:
Directives can be arrays.
Disadvantages:
No autocompletion.
Special Notes:
Variable collisions can occur. If this is a concern, name your array appropriately to avoid them.
Class
Assigning:
There are many different class based implementations.
Static class.
myCfgObj::setDirective('DIRECTIVE', 'value');
Instanced class.
myCfgObj->setDirective('DIRECTIVE', 'value');
Accessing:
Again there are various class based implementations.
Static class.
$object = new MyObject(myCfgObj::getDirective('DIRECTIVE'));
Instanced class.
$object = new MyObject(myCfgObj->getDirective('DIRECTIVE'));
Advantages:
Can be autoloaded.
Disadvantages:
Tends to be a bit verbose.
Can be hard to maintain if a container class is not being used.
I tend to use a Settings static class in PHP, this is because
It has a global scope.
You can enable/disable changes to protected configs.
You can add any settings during anywhere within runtime.
You can make the class automated to fetch public configs from a file/database.
Example:
abstract class Settings
{
static private $protected = array(); // For DB / passwords etc
static private $public = array(); // For all public strings such as meta stuff for site
public static function getProtected($key)
{
return isset(self::$protected[$key]) ? self::$protected[$key] : false;
}
public static function getPublic($key)
{
return isset(self::$public[$key]) ? self::$public[$key] : false;
}
public static function setProtected($key,$value)
{
self::$protected[$key] = $value;
}
public static function setPublic($key,$value)
{
self::$public[$key] = $value;
}
public function __get($key)
{//$this->key // returns public->key
return isset(self::$public[$key]) ? self::$public[$key] : false;
}
public function __isset($key)
{
return isset(self::$public[$key]);
}
}
Then within your runtime, if you loaded this file first, followed by your database config file, your database config file would look like so:
<?php
Settings::setProtected('db_hostname', 'localhost');
Settings::setProtected('db_username', 'root');
Settings::setProtected('db_password', '');
Settings::setProtected('db_database', 'root');
Settings::setProtected('db_charset', 'UTF-8');
//...
echo Settings::getProtected('db_hostname'); // localhost
//...
Settings::setPublic('config_site_title', 'MySiteTitle');
Settings::setPublic('config_site_charset', 'UTF-8');
Settings::setPublic('config_site_root', 'http://localhost/dev/');
As you can see the we have a method __get that should only be allowed to grab public variables, An example of why we have this is as follows:
$template = new Template();
$template->assign('settings', new Settings());
Regardless the fact that we have used this object as a static object, the values should still stand so within the template you can now do, lets say.
<html>
<head>
<?php echo isset($settings->config_site_title) ? $settings->config_site_title : 'Fallback Title'; ?>
</head>
</html>
And this will only allow you to have access to the public data during the initialized period.
This can get a lot more complex but more system friendly, some examples:
A loadConfig method to automatically parse a config file, xml, php, yaml.
If you register an shutdown_function you can auto update the database with new settings.
You can auto-populate the class with config from that database.
You can implement iterators to make it compatible with looping.
Lots more.
This too me is by far the best methods to complete this job.
The way I do it is directly store them in an array and save the file as config.php
<?php
$config['dbname'] = "mydatabase";
$config['WebsiteName'] = "Fundoo Site";
$config['credits'] = true;
$config['version'] = "4.0.4";
?>
Thi is the way most PHP frameworks like Wordpress, etc do it.
In PHP I always use a ".htaccess" to protect my config file (Double protection)
Note: "Best way" never exists. Every application and framework doing it their own style. While your example is doing the trick i think it's a little bit resource-heavy for a simple config file.
You can do it with single variables like Amber pointed out
You can do it with arrays this is the most common approach and you can always easily edit your config file.
You can do it with .ini files that PHP easily parsing
Edit:
Edward please take a look at parse_ini_file's examples. You can load the .ini file with a simple command then you can use the variables in a class like in your example.
There are pretty much possibilities I think, but the most common methods are storing as plain text in files like .csv, .ini, .xml. With little tricks you can protect these files, so that no one can load the files directly.
an INI-File example:
;<?php die(); ?>
[config1]
var1 = 'value1';
var2 = 'value2';
...
[config2]
...
The ; is considered a comment in ini files. So when you read in the file with an ini-parser, this line will be ignored. If someone accesses the file directly via url the die()-function will be executed. This works only, if the INI-file wears a file-extension like .php so that the server knows that this should be executed and not diplayed as plain text.
Possible disadvantage of most file-base-config-storages are problems with some utf8-characters.
Zend_Config is a component of the Zend-Framework, which provides possibilities for several storage-adapters with an easy to use api.
Since PHP is able to use OO, I like to go with a "Config class":
class Config
{
/**
* ---------------------------------
* Database - Access
* ---------------------------------
*/
/**
* #var String
*/
const DB_DRIVER = 'pgsql';
const DB_USER = 'postgres';
const DB_PASSWORD = 'postgres';
const DB_NAME = 'postgres';
}
It is easy accessable with Config::DB_DRIVER. No need to include the file since the application autoloader will do that for you. Of course, protecting the file still needs to be done.
Telnet? OMG I've fallen into a timewarp and arrived in 1992!
But seriously, IIRC there are tools which allow asp.net (and other languages) to parse session data - which is just a serialized php array. I'd have a go at implementing the global settings as a sort of shadow session within PHP. Even if you don't store your config settings as a serialized PHP array, you could map them into the session at runtime using your own session handler.
In terms of where you store the data - that's a more tricky one when you're presumably running on a Microsoft platform. Obviously you don't want the expense of disk access for every request. Although NT does some disk caching it's not (IME) quite as effective as other OS. Memcached appears to be one solution to this. It does appear to be usable from asp.net.
HTH
The usual route is to use define:
define('MYSQL_USER', 'ROOT');
and access it all over the application via MYSQL_USER :
$user = MYSQL_USER;
However arrays are not supported in this way.
There are few possibilities:
You can use config file (ini, json, xml or yaml). For ini you have parse_ini_file, for JSON there is json_decode (+file_get_contents), for YAML you have to use external library (search for sfYaml)
You can have a config file with variables or constants (better for immutable config and accessible in all scopes), which you includes in your script:
define('ROOT_DIR', '\home\www');
$sRootDir = '\home\www';
If you are OO-oriented, you can wrap it in class, as properties - you do not have getter method for every property, you can just have:
class Config
{
public $var1 = 'xxx';
public $var2 = 'yyy';
}
($c = new Config(); print $c->var1)
or
static class Config
{
public static $var1 = 'xxx';
public static $var2 = 'yyy';
}
(print c::$var1)
The best is to have registry-type class, implementing singleton pattern and capable to read config from given file.
In order to be testable, I use a Config class that holds the actual configuration data and an AppConfig static class that holds a reference to a Config object loaded at bootstrap from application wide configuration files (dependency injected at bootstrap). In the tests environment, I only change the Config object. See https://github.com/xprt64/Config