Reading and Writing Configuration Files - php

I'm writing a small script which generates some configuration for devices. I want to have separate file, where I'm storing configuration, and change some strings during printing the content of the configuration to the browser. How can I replace string in a line of the file with a variable from $_POST['somevariable']?
-- Additional info --
I have several types of devices. I want to have separate file with configuration template for each type of device. If someone want to change configuration of some type device they will change that file not php file. But in order to use this template in php I have to replace some string in that file before printing out to web page, e.g.: sys info hostname %host_name% sys info location %location% ip set %ip% the strings inbetween %% (could be any other) characters should be replaced with $_POST["host_name"], $_POST["location"], $_POST["ip"] etc. All these params gotten from the posted form.

It is advisable to use a structured file format of some sort for this purpose.
Consider using CSV, Ini, XML, JSON or YAML and use appropriate APIs to read and write them.
Another alternative would be to store the configuration in an array and then either use serialize/unserialize or use var_export/include to use it.
Very basic example:
class MyConfig
{
public static function read($filename)
{
$config = include $filename;
return $config;
}
public static function write($filename, array $config)
{
$config = var_export($config, true);
file_put_contents($filename, "<?php return $config ;");
}
}
You could use the class like this:
MyConfig::write('conf1.txt', array( 'setting_1' => 'foo' ));
$config = MyConfig::read('conf1.txt');
$config['setting_1'] = 'bar';
$config['setting_2'] = 'baz';
MyConfig::write('conf1.txt', $config);

Use SQLite. You can then query for specific data, and still have a local file. FYI - PDO quote automatically adds single quotes around a value.
$Filename = "MyDB.db";
try {
$SQLHandle = new PDO("sqlite:".$Filename);
}
catch(PDOException $e) {
echo $e->getMessage()." :: ".$Filename;
}
$SQLHandle->exec("CREATE TABLE IF NOT EXISTS MyTable (ID INTEGER PRIMARY KEY, MyColumn TEXT)");
$SQLHandle->beginTransaction();
$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue").")");
$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue 2").")");
$SQLHandle->commit();
$Iterator = $SQLHandle->query("SELECT * FROM MyTable ORDER BY MyColumn ASC");
unset($SQLHandle);
foreach($Iterator as $Row) {
echo $Row["MyColumn"]."\n";
}

I agree with Gordon.
If you don't follow his advice you can do something like this:
$file = file_get_contents('./conf.tpl');
$file = str_replace('%server%', 'localhost', $file);
file_put_contents('./conf.txt', $file);

Related

How to remove a value from an external text file if it is used PHP

I was just wondering if it is possible to remove a value from an external .txt file once it is randomly chosen.
// Opens file with key values
$randomkeys = file('keys.txt');
// Takes keys from file and puts it into an array
$random_keys = array_rand($randomkeys, 2);
// Outputs the random key chosen
echo "Your code is: ";
echo $randomkeys[$random_keys[0]];
This is the code I am currently using and it works by reading the file that includes all the keys and then it is put into an array that would later be shown to the user that is using the site.
What I want to add to this is that when the user views the site that the key shown to them will be removed from the list so no one else can get that same key.
Simply best solution is to use RDBMS.
But if You insist on solution with .txt file:
unset($randomkeys[$random_keys[0]]);
array_values($randomkeys);
file_put_contents('keys.txt', implode("\n", $randomkeys));
but keep in mind that in situation of asynchronous access it can keep the key that was used (because of parallel write) - so it's better to use DB.
Or just have another file: used_keys.txt and append used key to the bottom.
so solution:
$keys = file('keys.txt'); // reads keys file
$used_keys = file('used_keys.txt'); // reads used keys file
$keys = array_values(array_diff($keys, $used_keys)); // returns array of unused keys
$key = $keys[mt_rand(0, sizeof($keys)-1)]; // picks random key
// outputs random key
echo "Your code is: ";
echo $key;
// appends used random key to the end of used keys file
file_put_contents('used_keys.txt', $key, FILE_APPEND);
You could use file_get_contents to pull the data in the file, str_replace to remove the line and file_put_contents to put the data back. As suggested in this answer:
How to delete a line from the file with php?
Although this might not be the most efficient way of doing that, you could end up reading from and writing from the disk a lot depending on your application. Ideally you'd use a database like MySQL or SQLite.
Edit 9/25/2016
Here's a simple implementation of a database that might work for you. As of PHP 5.3 SQLite3 is enabled by default. This method generates a new code when you need instead of pre generating a list. Of course you could pre generate a list using this method as well if you wanted to.
PHP Docs:
http://php.net/manual/en/book.sqlite3.php
You only need a few concepts to get going:
SQLite3::open [Create or Open a database file]
SQLite3::query [For Selects]
SQLite3::querySingle [For Selects]
SQLite3::exec [For Insert, create, delete, update]
Note: My method for generating the "Code" is not cryptographically secure. If security is a big concern See: PHP: How to generate a random, unique, alphanumeric string?
Setup.php
Run this file once to create your database, you can delete it after if you like
<?php
class MyDB extends SQLite3
{
function __construct()
{
$this->open('mysqlitedb.db');
}
}
$db = new MyDB();
$db->exec('CREATE TABLE codes (code_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,code varchar(50) NOT NULL)');
$db->close();
CodeManager.php
Include this file anywhere you want to generate or delete a code
<?php
class CodeManager extends SQLite3
{
function __construct()
{
// You can change this to the file location of the code list.
$this->open('mysqlitedb.db');
}
function getRandomCode()
{
do {
// If users have a unique identifier, you can use it instead of rand()
// This should guarrantee a unique value that doesn't have to be checked first.
// You can also replace md5(...) with any method you prefer to generate the code
$code = md5(uniqid(rand(), true));
$code_check = $this->querySingle('SELECT code FROM codes WHERE code="'.$code.'"');
} while(!is_null($code_check)); // Need this loop as long as we are using rand()
// Try to add the code to the list of used codes
if($this->exec('INSERT INTO codes (code) VALUES ("'.$code.'")'))
return $code;
return false;
}
function deleteCode($to_delete)
{
// Try to delete the record
if($this->exec('DELETE FROM codes WHERE code="'.$to_delete.'"'))
return true;
// If we couldn't delete the record
return false;
}
function getAllCodes()
{
$all_codes = array();
$results = $this->query("SELECT * FROM codes");
while ($row = $results->fetchArray()) {
$all_codes[] = $row['code'];
}
return $all_codes;
}
}
App.php
Example of how to use CodeManager.php
<?php
include('CodeManager.php');
$cm = new CodeManager();
// Get a single new code
$new_code = $cm->getRandomCode();
echo $new_code."\r\n";
// Get a list of all codes generated so far
$all = $cm->getAllCodes();
// Display them one by one
foreach($all as $single){
echo $single . "\r\n";
}
?>

how to include script of variables in a class? undefined variable

got a script which has string variables that represent data fields like they are in the database. because this project is a complete mess this is a stage in cleaning it up and not having to rewrite the field name in numerous locations.
so one script 'DataKeys.php' will have variables set to field names.
//results from query1
$keyField1 = 'field1';
$keyField2 = 'field2';
these two vars above is only a snippet of a much longer list.
I want to access this file and use these vars when I am formatting the data to be more friendly for the front end. this script is being accessed in a class however the fields, $keyField1, defined in the script is not being found in the class. I did have the actual string there but I think single access point would be best so when I make future changes I don't need search the whole project.
class DataFormatter {
//put your code here
public function __construct() {
$documentRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT");
include ($documentRoot . '/database/values/DataKeys.php');
}
public function cleanData($data){
if (is_null($data) || empty($data))
{
return;
}
foreach($data as $row){
$field1Value = $row[$keyField1];
unset($row[$keyField1]);
}
}
}
I also tried moving the include outside the class definition.
$documentRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT");
include ($documentRoot . '/database/values/DataKeys.php');
The error that is being reported is :
Undefined variable: keyField1
SOULTION
Maybe not the optimal way but I took the include statement and placed it inside the function. The code above is just a demo of what I was trying to achieve not the actual code I am using.
the 2 variables are available just after the "include".
you can for example, put the 2 values in properties of the object
include ...;
$this->keyField1 = $keyField1;
$this->keyField2 = $keyField2;
You have to assign DataKeys.php to class member.
class DataFormatter {
private $keyField1;
private $keyField2;
public function __construct($filename) {
include $filename;
$this->keyField1 = $keyField1;
$this->keyField2 = $keyField2;
}
}
$dataFormatter = new DataFormatter(filter_input(INPUT_SERVER, 'DOCUMENT_ROOT') . '/database/values/DataKeys.php');

Parse text file with PHP [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Help me parse this file with PHP
I need to extract some text from a text file.suppose there is a text file in http://site.com/a.txt
And the contents of that file is like this:
var $name= 'name1';
var $age= 'age2';
var $phone= 'phonenumber';
var $a= 'asd';
var $district= 'district23';
How can I get the values of this text file (name1,age2,phonenumber,asd,district) in separate echo.
Use the file function to read the file into an array. Then loop through the array and each line in the file will be another element in the array. So make sure your file has line-breasks between the data.
Of course the best would be to have ready PHP code in a .php file which would then be included with the include function.
Organize the content of your text file like this : name1,age2,phonenumber,asd,district
And do this :
// Get the content of your file
$content = file_get_contents('a.text');
// Set your values with this
list($name, $age, $phone, $a, $district) = explode(',', $content);
// Then feel free to echo wathever you want
echo 'Name ' . $name;
Use an array and encode it: http://uk.php.net/manual/en/function.json-encode.php
I recommend using a class to encapsulate your data...
So imagine having a file called "person.php" that looks like this...
class Person
{
public $Name;
public $Age;
public $Phone;
public $A;
public $District;
}
You can then use the person class as a container.
include_once('person.php');
$person = new Person();
$person->Name = 'John Doe';
$person->Age = 52;
$person->Phone= '+441234 567 890';
$person->A = 'asd';
$person->District = 'District23';
Please note that "Age" is volatile (i.e. if the object lives for too long, the age will wrong!) You could avoid this by storing date of birth and then having a getAge() function on the Person object that gives you the correct age at any point in time.
The Person class is a plain PHP object, but you could add functions that add behaviour that relates to the concept of a Person, so the getAge() function would live on the Person class.
Finally, you could then store the object wherever you like using PHP's serialize and unserialize functions. The stored string that represents your object would look like this:
O:6:"Person":5:{
s:4:"Name";s:8:"John Doe";
s:3:"Age";i:52;
s:5:"Phone";s:15:"+441234 567 890";
s:1:"A";s:3:"asd";
s:8:"District";s:10:"District23";
}
And here is how you serialize the $person to look like this:
$serializedPerson = serialize($person);
echo $serializedPerson;
And converting from a string back to a Person is easy too:
$serializedPerson = 'O:6:"Person":5:{s:4:"Name";s:8:"John Doe";s:3:"Age";i:52;s:5:"Phone";s:15:"+441234 567 890";s:1:"A";s:3:"asd";s:8:"District";s:10:"District23";}';
$newPerson = unserialize($serializedPerson);
echo $newPerson->Name;
Summary
So if you stored you data in this serialized format, it is really easy to convert it directly into a PHP object that you can use without manually parsing the strings. You could store the string in a text file if you wanted - or a data store.
Rather than giving you the solution code I'm going to break this down into steps for you.
0) write the file in a machine readable format, e.g. name,age,phonenumber
1) Read from the file line by line
2) Break each line up according to the separator you used e.g. ","
3) Read in values into variables
4) Echo out
If you're stuck on something more specific, let us know.

include custom language files inside a while loop

Sorry if the title is not enough clear, I didn't know how to write it better.
The situation is that I a have a cron job that sends mails to users. I have language files, and depending in the configuration of the user I want to send the mail in his language.
But I don't know how to do this.
Each language file have constants definitions like:
en.php define('DATE','Date'); define('TIME','Time');
es.php define('DATE','Fecha'); define('TIME','Hora');
And I need to display the correct labels depending the user language, but I'm in a while loop:
while ($row = mysql_fetch_array($res)) {
if ($row['lang'] == en) //load the english labels
}
So I think I can't use something like "include_once" in each iteration.
The problem is that you use PHP Constants for it so once they are set, you can't really change them within the script. Try to rewrite your constants and all references of them into variables.
Let's say that your language files are named like this lang.code.php and the script that will send the email is in sendemail.php. You can do something like this:
In lang.en.php
<?php
$terms['date'] = 'Date';
In lang.es.php
<?php
$terms['date'] = 'Fecha';
In sendemail.php:
<?php
function sendEmail($language = 'en')
{
include "lang.$language.php";
extract($terms);
$to = 'foo#bar.com';
$subject = 'Test';
$body = "The $date today is: " . date('r');
mail(...);
}
foreach($users as $user)
sendEmail($user->preferredLanguage);
You can then reference terms in your language files using their keys in the $terms array. Of course you'd have to create validation checks for the language files, directory traversals, etc. But that's one way to implement it.
If you can't rewrite your code then I suggest putting all of the email contents in one file and use file_get_contents() to fetch the email content via HTTP. Inside that file, you'd have a conditional which loads the language file you need.
Let's say that the file that will generate the email contents is called emailcontent.php, you can do something like this:
In lang.en.php
<?php
define('DATE', 'Date');
In lang.es.php
<?php
define('DATE, 'Fecha');
In emailcontent.php
<?php
require "lang.{$_GET['lang']}.php";
echo 'The ' . DATE . ' today is: ' . date('r');
In sendemail.php
<?php
$to = 'foo#bar.com';
$subject = 'Test';
foreach($users as $user)
{
$contents = file_get_contents('http://yourhost.com/emailcontent.php?lang=en');
mail(...);
}
Of course you need to add some security checks to prevent local file inclusion, access to emailcontent.php, etc. But this is the gist of it. However this has a big overhead in terms of latency and repeated compilation of your PHP code. If there was any other way to clear the global score for each email, then that is the better way to go. But this is the only way I can think of if you can't rewrite your code.
In this can I thing you should store the template to datebase its self, based on language you can fetch from DB
Or
Another way you have to get every variable conditional like
en.php define('DATE_EN','Date'); define('TIME','Time');
es.php define('DATE_FR','Fecha'); define('TIME','Hora');
if ($row['lang'] == en)
DATE_EN
else
DATE_FR
In your case constants will not work, since they cannot be changed during the execution.
Instead, I would recommend including all language information in one file, these can be constants each containing a different array:
langs.php
define('EN', array(
'DATE' => 'Date',
'TIME' => 'Time',
));
define('ES', array(
'DATE' => 'Fecha',
'TIME' => 'Hora',
));
Next, you can add a value to the array $row for each user, or you can use a variable like $lang which will be overwritten every while loop.
require_once 'langs.php' //or include_once
while ($row = mysql_fetch_array($res)) {
if ($row['lang'] == en) { //load the english labels
$lang = EN;
} elseif ($row['lang'] == es {
$lang = ES;
}
echo "Welcome {$row['name']}, today it's {$lang['DATE']}" //will display the corresponding $lang value
}

Is it possible for PHP to write additional PHP functions like a Lisp macro?

I'm writing a unit testing platform and I want to be able to dynamically generate a function based off of each function in the web service I am testing. The dynamic function would be generated with default(correct) values for each argument in the web service and allow them to be easily traded out with incorrect values for error testing.
$arrayOfDefVals = array(123, 'foo');
testFunctionGenerator('function1', $arrayOfDefVals);
//resulting php code:
function1Test($expectedOutput, $arg1=123, $arg2='foo')
{
try
{
$out = function1($arg1, $arg2);
if($expectedOutput === $out)
return true;
else
return $out;
}
catch ($e)
{
return $e;
}
}
This would allow me to quickly and cleanly pass one bad argument, or any number of bad arguments, at a time to test all of the error catching in the web service.
My main question is:
Is this even possible with php?
If it's not possible, is there an alternative?
EDIT: I'm not looking for a unit test, I'm trying to learn by doing. I'm not looking for advice on this code example, it's just a quick example of what I would like to do. I just want to know if it's possible.
I would not try that first as PHP has not build-in macro support. But probably something in that direction:
function function1($param1, $param2)
{
return sprintf("param1: %d, param2: '%s'\n", $param1, $param2);
}
/* Macro: basically a port of your macro as a function */
$testFunctionGenerator = function($callback, array $defVals = array())
{
$defVals = array_values($defVals); // list, not hash
return function() use ($callback, $defVals)
{
$callArgs = func_get_args();
$expectedOutput = array_shift($callArgs);
$callArgs += $defVals;
return $expectedOutput == call_user_func_array($callback, $callArgs);
};
};
/* Use */
$arrayOfDefVals = array(123, 'foo');
$function1Test = $testFunctionGenerator('function1', $arrayOfDefVals);
var_dump($function1Test("param1: 456, param2: 'foo'\n", 456)); # bool(true)
Probably this is helpful, see Anonymous functions­Docs, func_get_args­Docs, the Union array operator­Docs and call_user_func_array­Docs.
Well, for starters, you can set default parameters in functions:
function function1Test($expectedOutput, $testArg1=123, $testArg2='foo') {
...
}
Beyond that, I'm not really sure what you're trying to achieve with this "function generator"...
Read about call_user_func and func_get_args
This example from the manual should get you on the right track:
<?php
call_user_func(function($arg) { print "[$arg]\n"; }, 'test'); /* As of PHP 5.3.0 */
?>
If it's a function you have file access to (i.e., it's not a part of the PHP standard library and you have permissions to read from the file), you could do something like this:
Assume we have a function like this located in some file. The file will have to be included (i.e., the function will have to be in PHP's internal symbol table):
function my_original_function($param1, $param2)
{
echo "$param1 $param2 \n";
}
Use the ReflectionFunction class to get details about that function and where it's defined: http://us2.php.net/manual/en/class.reflectionfunction.php.
$reflection = new ReflectionFunction('my_original_function');
Next, you can use the reflection instance to get the path to that file, the first/last line number of the function, and the parameters to the function:
$file_path = $reflection->getFileName();
$start_line = $reflection->getStartLine();
$end_line = $reflection->getEndLine();
$params = $reflection->getParameters();
Using these, you could:
read the function out of the file into a string
rewrite the first line to change the function name, using the known function name as a reference
rewrite the first line to alter the parameter defaults, using $params as a reference
write the altered function string to a file
include the file
Voila! You now have the new function available.
Depending on what it is you're actually trying to accomplish, you could also potentially just use ReflectionFunction::getClosure() to get an closure copy of the function, assign it to whatever variable you want, and define the parameters there. See: http://us.php.net/manual/en/functions.anonymous.php. Or you could instantiate multiple ReflectionFunctions and call ReflectionFunction::invoke()/invokeArgs() with the parameter set you want. See: http://us2.php.net/manual/en/reflectionfunction.invokeargs.php or http://us2.php.net/manual/en/reflectionfunction.invoke.php

Categories