I've been looking around the official php documentation but I'm unable to find what I'm looking for.
http://php.net/manual/en/function.parse-ini-file.php
I just want a function to edit and read the value from the php ini file, for instance,
[default_colors]
sitebg = #F8F8F8
footerbg = #F8F8F8
link = #F8F8F8
url = #F8F8F8
bg = #F8F8F8
text = #F8F8F8
border = #F8F8F8
lu_link = #F8F8F8
lu_url = #F8F8F8
lu_bg = #F8F8F8
lu_text = #f505f5
lu_border = #F8F8F8
How do I read the value belonging to "lu_link" or "footerbg"?
How to I write a new value for these places?
You can simply use parse_ini_file with PHP4/5.
$ini_array = parse_ini_file("sample.ini");
print_r($ini_array);
Here is the doc: http://php.net/manual/en/function.parse-ini-file.php
To write back an array of objects back to the ini file, use below as a very fast & easy solution:
function write_php_ini($array, $file)
{
$res = array();
foreach($array as $key => $val)
{
if(is_array($val))
{
$res[] = "[$key]";
foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"');
}
else $res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"');
}
safefilerewrite($file, implode("\r\n", $res));
}
function safefilerewrite($fileName, $dataToSave)
{ if ($fp = fopen($fileName, 'w'))
{
$startTime = microtime(TRUE);
do
{ $canWrite = flock($fp, LOCK_EX);
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
if(!$canWrite) usleep(round(rand(0, 100)*1000));
} while ((!$canWrite)and((microtime(TRUE)-$startTime) < 5));
//file was locked so now we can store information
if ($canWrite)
{ fwrite($fp, $dataToSave);
flock($fp, LOCK_UN);
}
fclose($fp);
}
}
The PEAR Config_Lite package can do almost all the work (both reading and writing) for you super-easily. Check it out here: http://pear.php.net/package/Config_Lite
How about this:
$key='option';
$val='1.2.3.4.5';
system("sed -ie 's/\({$key}=\)\(.*\)/\1{$val}/' file.in");
Below is an implementation of write_ini_file() which PHP is currently lacking, it will create an almost identical (except comments) of the input:
Supports cross platform (PHP_EOL) new lines added between sections.
Handles both index and key value arrays.
Handles CONSTANT style values.
And file locking to stay consistent.
Source
<?php
if (!function_exists('write_ini_file')) {
/**
* Write an ini configuration file
*
* #param string $file
* #param array $array
* #return bool
*/
function write_ini_file($file, $array = []) {
// check first argument is string
if (!is_string($file)) {
throw new \InvalidArgumentException('Function argument 1 must be a string.');
}
// check second argument is array
if (!is_array($array)) {
throw new \InvalidArgumentException('Function argument 2 must be an array.');
}
// process array
$data = array();
foreach ($array as $key => $val) {
if (is_array($val)) {
$data[] = "[$key]";
foreach ($val as $skey => $sval) {
if (is_array($sval)) {
foreach ($sval as $_skey => $_sval) {
if (is_numeric($_skey)) {
$data[] = $skey.'[] = '.(is_numeric($_sval) ? $_sval : (ctype_upper($_sval) ? $_sval : '"'.$_sval.'"'));
} else {
$data[] = $skey.'['.$_skey.'] = '.(is_numeric($_sval) ? $_sval : (ctype_upper($_sval) ? $_sval : '"'.$_sval.'"'));
}
}
} else {
$data[] = $skey.' = '.(is_numeric($sval) ? $sval : (ctype_upper($sval) ? $sval : '"'.$sval.'"'));
}
}
} else {
$data[] = $key.' = '.(is_numeric($val) ? $val : (ctype_upper($val) ? $val : '"'.$val.'"'));
}
// empty line
$data[] = null;
}
// open file pointer, init flock options
$fp = fopen($file, 'w');
$retries = 0;
$max_retries = 100;
if (!$fp) {
return false;
}
// loop until get lock, or reach max retries
do {
if ($retries > 0) {
usleep(rand(1, 5000));
}
$retries += 1;
} while (!flock($fp, LOCK_EX) && $retries <= $max_retries);
// couldn't get the lock
if ($retries == $max_retries) {
return false;
}
// got lock, write data
fwrite($fp, implode(PHP_EOL, $data).PHP_EOL);
// release lock
flock($fp, LOCK_UN);
fclose($fp);
return true;
}
}
Example input .ini file (taken from http://php.net/manual/en/function.parse-ini-file.php)
; This is a sample configuration file
; Comments start with ';', as in php.ini
[first_section]
one = 1
five = 5
animal = BIRD
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
[third_section]
phpversion[] = "5.0"
phpversion[] = "5.1"
phpversion[] = "5.2"
phpversion[] = "5.3"
urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"
Example usage:
// load ini file values into array
$config = parse_ini_file('config.ini', true);
// add some additional values
$config['main']['foobar'] = 'baz';
$config['main']['const']['a'] = 'UPPERCASE';
$config['main']['const']['b'] = 'UPPER_CASE WITH SPACE';
$config['main']['array'][] = 'Some Value';
$config['main']['array'][] = 'ADD';
$config['third_section']['urls']['docs'] = 'http://php.net';
// write ini file
write_ini_file('config.ini', $config);
Resulting .ini file:
[first_section]
one = 1
five = 5
animal = BIRD
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
[third_section]
phpversion[] = 5.0
phpversion[] = 5.1
phpversion[] = 5.2
phpversion[] = 5.3
urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"
urls[docs] = "http://php.net"
[main]
foobar = "baz"
const[a] = UPPERCASE
const[b] = "UPPER_CASE WITH SPACE"
array[] = "Some Value"
array[] = ADD
Here's a functional version that creates a string that can be written to a file.
function IniAsStr(array $a) : string
{
return array_reduce(array_keys($a), function($str, $sectionName) use ($a) {
$sub = $a[$sectionName];
return $str . "[$sectionName]" . PHP_EOL .
array_reduce(array_keys($sub), function($str, $key) use($sub) {
return $str . $key . '=' . $sub[$key] . PHP_EOL;
}) . PHP_EOL;
});
}
Here is your function to read and write INI files with a category option!
If you provide multi dimensional array, you will have category in your INI file.
Or basic array will allow you to read and write data fast.
See the comments and example below for details:
### PHP write_ini_file function to use with parse_ini_file: (choose one of the two example arrays below...)
$array = array('category' => array('color' => 'blue', 'size' => 'large'));
// $array = array('color' => 'red', 'size' => 'small');
function write_ini_file($array, $path) {
unset($content, $arrayMulti);
# See if the array input is multidimensional.
foreach($array AS $arrayTest){
if(is_array($arrayTest)) {
$arrayMulti = true;
}
}
# Use categories in the INI file for multidimensional array OR use basic INI file:
if ($arrayMulti) {
foreach ($array AS $key => $elem) {
$content .= "[" . $key . "]\n";
foreach ($elem AS $key2 => $elem2) {
if (is_array($elem2)) {
for ($i = 0; $i < count($elem2); $i++) {
$content .= $key2 . "[] = \"" . $elem2[$i] . "\"\n";
}
} else if ($elem2 == "") {
$content .= $key2 . " = \n";
} else {
$content .= $key2 . " = \"" . $elem2 . "\"\n";
}
}
}
} else {
foreach ($array AS $key2 => $elem2) {
if (is_array($elem2)) {
for ($i = 0; $i < count($elem2); $i++) {
$content .= $key2 . "[] = \"" . $elem2[$i] . "\"\n";
}
} else if ($elem2 == "") {
$content .= $key2 . " = \n";
} else {
$content .= $key2 . " = \"" . $elem2 . "\"\n";
}
}
}
if (!$handle = fopen($path, 'w')) {
return false;
}
if (!fwrite($handle, $content)) {
return false;
}
fclose($handle);
return true;
}
write_ini_file($array,'./data.ini');
$readData = parse_ini_file('./data.ini',true);
print_r($readData);
Related
I need to refresh modifications after install module.
public function install() {
$this->load->controller('marketplace/modification/refresh');
}
I tried this. Its worked but the page redirected to modification listing. How can i do without redirect. I am using opencart 3.
If you don't want to edit modification.php or clone its refresh function, You can use this:
public function install(){
$data['redirect'] = 'extension/extension/module';
$this->load->controller('marketplace/modification/refresh', $data);
}
You could not controll by this way as you are doing:
You need to do this as
public function install() {
$this->refresh();
}
protected function refresh($data = array()) {
$this->load->language('marketplace/modification');
$this->document->setTitle($this->language->get('heading_title'));
$this->load->model('setting/modification');
if ($this->validate()) {
// Just before files are deleted, if config settings say maintenance mode is off then turn it on
$maintenance = $this->config->get('config_maintenance');
$this->load->model('setting/setting');
$this->model_setting_setting->editSettingValue('config', 'config_maintenance', true);
//Log
$log = array();
// Clear all modification files
$files = array();
// Make path into an array
$path = array(DIR_MODIFICATION . '*');
// While the path array is still populated keep looping through
while (count($path) != 0) {
$next = array_shift($path);
foreach (glob($next) as $file) {
// If directory add to path array
if (is_dir($file)) {
$path[] = $file . '/*';
}
// Add the file to the files to be deleted array
$files[] = $file;
}
}
// Reverse sort the file array
rsort($files);
// Clear all modification files
foreach ($files as $file) {
if ($file != DIR_MODIFICATION . 'index.html') {
// If file just delete
if (is_file($file)) {
unlink($file);
// If directory use the remove directory function
} elseif (is_dir($file)) {
rmdir($file);
}
}
}
// Begin
$xml = array();
// Load the default modification XML
$xml[] = file_get_contents(DIR_SYSTEM . 'modification.xml');
// This is purly for developers so they can run mods directly and have them run without upload after each change.
$files = glob(DIR_SYSTEM . '*.ocmod.xml');
if ($files) {
foreach ($files as $file) {
$xml[] = file_get_contents($file);
}
}
// Get the default modification file
$results = $this->model_setting_modification->getModifications();
foreach ($results as $result) {
if ($result['status']) {
$xml[] = $result['xml'];
}
}
$modification = array();
foreach ($xml as $xml) {
if (empty($xml)){
continue;
}
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->loadXml($xml);
// Log
$log[] = 'MOD: ' . $dom->getElementsByTagName('name')->item(0)->textContent;
// Wipe the past modification store in the backup array
$recovery = array();
// Set the a recovery of the modification code in case we need to use it if an abort attribute is used.
if (isset($modification)) {
$recovery = $modification;
}
$files = $dom->getElementsByTagName('modification')->item(0)->getElementsByTagName('file');
foreach ($files as $file) {
$operations = $file->getElementsByTagName('operation');
$files = explode('|', $file->getAttribute('path'));
foreach ($files as $file) {
$path = '';
// Get the full path of the files that are going to be used for modification
if ((substr($file, 0, 7) == 'catalog')) {
$path = DIR_CATALOG . substr($file, 8);
}
if ((substr($file, 0, 5) == 'admin')) {
$path = DIR_APPLICATION . substr($file, 6);
}
if ((substr($file, 0, 6) == 'system')) {
$path = DIR_SYSTEM . substr($file, 7);
}
if ($path) {
$files = glob($path, GLOB_BRACE);
if ($files) {
foreach ($files as $file) {
// Get the key to be used for the modification cache filename.
if (substr($file, 0, strlen(DIR_CATALOG)) == DIR_CATALOG) {
$key = 'catalog/' . substr($file, strlen(DIR_CATALOG));
}
if (substr($file, 0, strlen(DIR_APPLICATION)) == DIR_APPLICATION) {
$key = 'admin/' . substr($file, strlen(DIR_APPLICATION));
}
if (substr($file, 0, strlen(DIR_SYSTEM)) == DIR_SYSTEM) {
$key = 'system/' . substr($file, strlen(DIR_SYSTEM));
}
// If file contents is not already in the modification array we need to load it.
if (!isset($modification[$key])) {
$content = file_get_contents($file);
$modification[$key] = preg_replace('~\r?\n~', "\n", $content);
$original[$key] = preg_replace('~\r?\n~', "\n", $content);
// Log
$log[] = PHP_EOL . 'FILE: ' . $key;
}
foreach ($operations as $operation) {
$error = $operation->getAttribute('error');
// Ignoreif
$ignoreif = $operation->getElementsByTagName('ignoreif')->item(0);
if ($ignoreif) {
if ($ignoreif->getAttribute('regex') != 'true') {
if (strpos($modification[$key], $ignoreif->textContent) !== false) {
continue;
}
} else {
if (preg_match($ignoreif->textContent, $modification[$key])) {
continue;
}
}
}
$status = false;
// Search and replace
if ($operation->getElementsByTagName('search')->item(0)->getAttribute('regex') != 'true') {
// Search
$search = $operation->getElementsByTagName('search')->item(0)->textContent;
$trim = $operation->getElementsByTagName('search')->item(0)->getAttribute('trim');
$index = $operation->getElementsByTagName('search')->item(0)->getAttribute('index');
// Trim line if no trim attribute is set or is set to true.
if (!$trim || $trim == 'true') {
$search = trim($search);
}
// Add
$add = $operation->getElementsByTagName('add')->item(0)->textContent;
$trim = $operation->getElementsByTagName('add')->item(0)->getAttribute('trim');
$position = $operation->getElementsByTagName('add')->item(0)->getAttribute('position');
$offset = $operation->getElementsByTagName('add')->item(0)->getAttribute('offset');
if ($offset == '') {
$offset = 0;
}
// Trim line if is set to true.
if ($trim == 'true') {
$add = trim($add);
}
// Log
$log[] = 'CODE: ' . $search;
// Check if using indexes
if ($index !== '') {
$indexes = explode(',', $index);
} else {
$indexes = array();
}
// Get all the matches
$i = 0;
$lines = explode("\n", $modification[$key]);
for ($line_id = 0; $line_id < count($lines); $line_id++) {
$line = $lines[$line_id];
// Status
$match = false;
// Check to see if the line matches the search code.
if (stripos($line, $search) !== false) {
// If indexes are not used then just set the found status to true.
if (!$indexes) {
$match = true;
} elseif (in_array($i, $indexes)) {
$match = true;
}
$i++;
}
// Now for replacing or adding to the matched elements
if ($match) {
switch ($position) {
default:
case 'replace':
$new_lines = explode("\n", $add);
if ($offset < 0) {
array_splice($lines, $line_id + $offset, abs($offset) + 1, array(str_replace($search, $add, $line)));
$line_id -= $offset;
} else {
array_splice($lines, $line_id, $offset + 1, array(str_replace($search, $add, $line)));
}
break;
case 'before':
$new_lines = explode("\n", $add);
array_splice($lines, $line_id - $offset, 0, $new_lines);
$line_id += count($new_lines);
break;
case 'after':
$new_lines = explode("\n", $add);
array_splice($lines, ($line_id + 1) + $offset, 0, $new_lines);
$line_id += count($new_lines);
break;
}
// Log
$log[] = 'LINE: ' . $line_id;
$status = true;
}
}
$modification[$key] = implode("\n", $lines);
} else {
$search = trim($operation->getElementsByTagName('search')->item(0)->textContent);
$limit = $operation->getElementsByTagName('search')->item(0)->getAttribute('limit');
$replace = trim($operation->getElementsByTagName('add')->item(0)->textContent);
// Limit
if (!$limit) {
$limit = -1;
}
// Log
$match = array();
preg_match_all($search, $modification[$key], $match, PREG_OFFSET_CAPTURE);
// Remove part of the the result if a limit is set.
if ($limit > 0) {
$match[0] = array_slice($match[0], 0, $limit);
}
if ($match[0]) {
$log[] = 'REGEX: ' . $search;
for ($i = 0; $i < count($match[0]); $i++) {
$log[] = 'LINE: ' . (substr_count(substr($modification[$key], 0, $match[0][$i][1]), "\n") + 1);
}
$status = true;
}
// Make the modification
$modification[$key] = preg_replace($search, $replace, $modification[$key], $limit);
}
if (!$status) {
// Abort applying this modification completely.
if ($error == 'abort') {
$modification = $recovery;
// Log
$log[] = 'NOT FOUND - ABORTING!';
break 5;
}
// Skip current operation or break
elseif ($error == 'skip') {
// Log
$log[] = 'NOT FOUND - OPERATION SKIPPED!';
continue;
}
// Break current operations
else {
// Log
$log[] = 'NOT FOUND - OPERATIONS ABORTED!';
break;
}
}
}
}
}
}
}
}
// Log
$log[] = '----------------------------------------------------------------';
}
// Log
$ocmod = new Log('ocmod.log');
$ocmod->write(implode("\n", $log));
// Write all modification files
foreach ($modification as $key => $value) {
// Only create a file if there are changes
if ($original[$key] != $value) {
$path = '';
$directories = explode('/', dirname($key));
foreach ($directories as $directory) {
$path = $path . '/' . $directory;
if (!is_dir(DIR_MODIFICATION . $path)) {
#mkdir(DIR_MODIFICATION . $path, 0777);
}
}
$handle = fopen(DIR_MODIFICATION . $key, 'w');
fwrite($handle, $value);
fclose($handle);
}
}
// Maintance mode back to original settings
$this->model_setting_setting->editSettingValue('config', 'config_maintenance', $maintenance);
// Do not return success message if refresh() was called with $data
$this->session->data['success'] = $this->language->get('text_success');
$url = '';
if (isset($this->request->get['sort'])) {
$url .= '&sort=' . $this->request->get['sort'];
}
if (isset($this->request->get['order'])) {
$url .= '&order=' . $this->request->get['order'];
}
if (isset($this->request->get['page'])) {
$url .= '&page=' . $this->request->get['page'];
}
}
}
I hope it shouwl work for you.
This process is used to refresh the modification when your module installing.
if you need globally this then please tell me I will update you process.
I'm trying to make a recursive function to get all the directories and sub directories from my ftp server in an array.
I tried a lot of functions I've found on the web. The one that works best for me is this one:
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if ((strpos($x, ".")) || (strpos($x, ".") === 0)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
The problem with this function is it wont allow the directory to have a "." in it's name and every file that is located in the root directory will be considered a directory as well. So I adjusted the function and got this:
public function getAllSubDirFiles($ip, $id, $pw) {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b =0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if (!is_dir('ftp://'.$id.':'.$pw.'#'.$ip.'/'.$x)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
This works pretty good but and gives the result I want. but it's so slow it's unusable.
I also tried working with ftp_rawlist but it has the same drawback of being horribly slow.
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = $this->getFtp_rawlist('/' . $ftp_dir);
foreach ($newdir as $key => $x) {
$firstChar = substr($newdir[$key][0], 0, 1);
$a = 8;
while ($a < count($newdir[$key])) {
if ($a == 8) {
$fileName = $ftp_dir . '/' . $newdir[$key][$a];
} else {
$fileName = $fileName . ' ' . $newdir[$key][$a];
}
$a++;
}
if ($firstChar != 'd') {
unset($newdir[$key]);
} elseif (!in_array($fileName, $dir)) {
$dir[] = $fileName;
}
}
}
$b = count($dir);
}
return $dir;
}
public function getFtp_rawlist($dir) {
$newArr = array();
$arr = ftp_rawlist($this->connectionId, $dir);
foreach ($arr as $value) {
$stringArr = explode(" ", $value);
$newArr[] = array_values(array_filter($stringArr));
}
return $newArr;
}
I've been stuck on this problem for the last couple of days and I'am getting desperate. If any one has any suggestion please let me know
If your server supports MLSD command and you have PHP 7.2 or newer, you can use ftp_mlsd function:
function ftp_mlsd_recursive($ftp_stream, $directory)
{
$result = [];
$files = ftp_mlsd($ftp_stream, $directory);
if ($files === false)
{
die("Cannot list $directory");
}
foreach ($files as $file)
{
$name = $file["name"];
$filepath = $directory . "/" . $name;
if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
{
// noop
}
else if ($file["type"] == "dir")
{
$result = array_merge($result, ftp_mlsd_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
If you do not have PHP 7.2, you can try to implement the MLSD command on your own. For a start, see user comment of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071
If you cannot use MLSD, you will particularly have problems telling if an entry is a file or folder. While you can use the ftp_size trick, calling ftp_size for each entry can take ages.
But if you need to work against one specific FTP server only, you can use ftp_rawlist to retrieve a file listing in a platform-specific format and parse that.
The following code assumes a common *nix format.
function ftp_nlst_recursive($ftp_stream, $directory)
{
$result = [];
$lines = ftp_rawlist($ftp_stream, $directory);
if ($lines === false)
{
die("Cannot list $directory");
}
foreach ($lines as $line)
{
$tokens = preg_split("/\s+/", $line, 9);
$name = $tokens[8];
$type = $tokens[0][0];
$filepath = $directory . "/" . $name;
if ($type == 'd')
{
$result = array_merge($result, ftp_nlst_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
For DOS format, see: Get directory structure from FTP using PHP.
I've build an OOP FTP Client library that's can help you on this a lot, using just this code you can retrieve a list of only the directories with addition useful information like (chmod, last modified time, size ...).
The code :
// Connection
$connection = new FtpConnection("localhost", "foo", "12345");
$connection->open();
// FtpConfig
$config = new FtpConfig($connection);
$config->setPassive(true);
$client = new FtpClient($connection);
$allFolders =
// directory, recursive, filter
$client->listDirectoryDetails('/', true, FtpClient::DIR_TYPE);
// Do whatever you want with the folders
This code a variation of Martin Prikryl one. It is slower but do not have any failures with whitespaces. Use this code only if you have any problems with the code above.
function ftp_list_files_recursive($ftp_stream, $path){
$lines = ftp_nlist($ftp_stream, $path);
$result = array();
foreach ($lines as $line) {
if (ftp_size($ftp_stream, $line) == -1) {
$result = array_merge($result, ftp_list_files_recursive($ftp_stream, $line));
}
else{
$result[] = $line;
}
}
return $result;
}
I've been looking around the official php documentation but I'm unable to find what I'm looking for.
http://php.net/manual/en/function.parse-ini-file.php
I just want a function to edit and read the value from the php ini file, for instance,
[default_colors]
sitebg = #F8F8F8
footerbg = #F8F8F8
link = #F8F8F8
url = #F8F8F8
bg = #F8F8F8
text = #F8F8F8
border = #F8F8F8
lu_link = #F8F8F8
lu_url = #F8F8F8
lu_bg = #F8F8F8
lu_text = #f505f5
lu_border = #F8F8F8
How do I read the value belonging to "lu_link" or "footerbg"?
How to I write a new value for these places?
You can simply use parse_ini_file with PHP4/5.
$ini_array = parse_ini_file("sample.ini");
print_r($ini_array);
Here is the doc: http://php.net/manual/en/function.parse-ini-file.php
To write back an array of objects back to the ini file, use below as a very fast & easy solution:
function write_php_ini($array, $file)
{
$res = array();
foreach($array as $key => $val)
{
if(is_array($val))
{
$res[] = "[$key]";
foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"');
}
else $res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"');
}
safefilerewrite($file, implode("\r\n", $res));
}
function safefilerewrite($fileName, $dataToSave)
{ if ($fp = fopen($fileName, 'w'))
{
$startTime = microtime(TRUE);
do
{ $canWrite = flock($fp, LOCK_EX);
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
if(!$canWrite) usleep(round(rand(0, 100)*1000));
} while ((!$canWrite)and((microtime(TRUE)-$startTime) < 5));
//file was locked so now we can store information
if ($canWrite)
{ fwrite($fp, $dataToSave);
flock($fp, LOCK_UN);
}
fclose($fp);
}
}
The PEAR Config_Lite package can do almost all the work (both reading and writing) for you super-easily. Check it out here: http://pear.php.net/package/Config_Lite
How about this:
$key='option';
$val='1.2.3.4.5';
system("sed -ie 's/\({$key}=\)\(.*\)/\1{$val}/' file.in");
Below is an implementation of write_ini_file() which PHP is currently lacking, it will create an almost identical (except comments) of the input:
Supports cross platform (PHP_EOL) new lines added between sections.
Handles both index and key value arrays.
Handles CONSTANT style values.
And file locking to stay consistent.
Source
<?php
if (!function_exists('write_ini_file')) {
/**
* Write an ini configuration file
*
* #param string $file
* #param array $array
* #return bool
*/
function write_ini_file($file, $array = []) {
// check first argument is string
if (!is_string($file)) {
throw new \InvalidArgumentException('Function argument 1 must be a string.');
}
// check second argument is array
if (!is_array($array)) {
throw new \InvalidArgumentException('Function argument 2 must be an array.');
}
// process array
$data = array();
foreach ($array as $key => $val) {
if (is_array($val)) {
$data[] = "[$key]";
foreach ($val as $skey => $sval) {
if (is_array($sval)) {
foreach ($sval as $_skey => $_sval) {
if (is_numeric($_skey)) {
$data[] = $skey.'[] = '.(is_numeric($_sval) ? $_sval : (ctype_upper($_sval) ? $_sval : '"'.$_sval.'"'));
} else {
$data[] = $skey.'['.$_skey.'] = '.(is_numeric($_sval) ? $_sval : (ctype_upper($_sval) ? $_sval : '"'.$_sval.'"'));
}
}
} else {
$data[] = $skey.' = '.(is_numeric($sval) ? $sval : (ctype_upper($sval) ? $sval : '"'.$sval.'"'));
}
}
} else {
$data[] = $key.' = '.(is_numeric($val) ? $val : (ctype_upper($val) ? $val : '"'.$val.'"'));
}
// empty line
$data[] = null;
}
// open file pointer, init flock options
$fp = fopen($file, 'w');
$retries = 0;
$max_retries = 100;
if (!$fp) {
return false;
}
// loop until get lock, or reach max retries
do {
if ($retries > 0) {
usleep(rand(1, 5000));
}
$retries += 1;
} while (!flock($fp, LOCK_EX) && $retries <= $max_retries);
// couldn't get the lock
if ($retries == $max_retries) {
return false;
}
// got lock, write data
fwrite($fp, implode(PHP_EOL, $data).PHP_EOL);
// release lock
flock($fp, LOCK_UN);
fclose($fp);
return true;
}
}
Example input .ini file (taken from http://php.net/manual/en/function.parse-ini-file.php)
; This is a sample configuration file
; Comments start with ';', as in php.ini
[first_section]
one = 1
five = 5
animal = BIRD
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
[third_section]
phpversion[] = "5.0"
phpversion[] = "5.1"
phpversion[] = "5.2"
phpversion[] = "5.3"
urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"
Example usage:
// load ini file values into array
$config = parse_ini_file('config.ini', true);
// add some additional values
$config['main']['foobar'] = 'baz';
$config['main']['const']['a'] = 'UPPERCASE';
$config['main']['const']['b'] = 'UPPER_CASE WITH SPACE';
$config['main']['array'][] = 'Some Value';
$config['main']['array'][] = 'ADD';
$config['third_section']['urls']['docs'] = 'http://php.net';
// write ini file
write_ini_file('config.ini', $config);
Resulting .ini file:
[first_section]
one = 1
five = 5
animal = BIRD
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
[third_section]
phpversion[] = 5.0
phpversion[] = 5.1
phpversion[] = 5.2
phpversion[] = 5.3
urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"
urls[docs] = "http://php.net"
[main]
foobar = "baz"
const[a] = UPPERCASE
const[b] = "UPPER_CASE WITH SPACE"
array[] = "Some Value"
array[] = ADD
Here's a functional version that creates a string that can be written to a file.
function IniAsStr(array $a) : string
{
return array_reduce(array_keys($a), function($str, $sectionName) use ($a) {
$sub = $a[$sectionName];
return $str . "[$sectionName]" . PHP_EOL .
array_reduce(array_keys($sub), function($str, $key) use($sub) {
return $str . $key . '=' . $sub[$key] . PHP_EOL;
}) . PHP_EOL;
});
}
Here is your function to read and write INI files with a category option!
If you provide multi dimensional array, you will have category in your INI file.
Or basic array will allow you to read and write data fast.
See the comments and example below for details:
### PHP write_ini_file function to use with parse_ini_file: (choose one of the two example arrays below...)
$array = array('category' => array('color' => 'blue', 'size' => 'large'));
// $array = array('color' => 'red', 'size' => 'small');
function write_ini_file($array, $path) {
unset($content, $arrayMulti);
# See if the array input is multidimensional.
foreach($array AS $arrayTest){
if(is_array($arrayTest)) {
$arrayMulti = true;
}
}
# Use categories in the INI file for multidimensional array OR use basic INI file:
if ($arrayMulti) {
foreach ($array AS $key => $elem) {
$content .= "[" . $key . "]\n";
foreach ($elem AS $key2 => $elem2) {
if (is_array($elem2)) {
for ($i = 0; $i < count($elem2); $i++) {
$content .= $key2 . "[] = \"" . $elem2[$i] . "\"\n";
}
} else if ($elem2 == "") {
$content .= $key2 . " = \n";
} else {
$content .= $key2 . " = \"" . $elem2 . "\"\n";
}
}
}
} else {
foreach ($array AS $key2 => $elem2) {
if (is_array($elem2)) {
for ($i = 0; $i < count($elem2); $i++) {
$content .= $key2 . "[] = \"" . $elem2[$i] . "\"\n";
}
} else if ($elem2 == "") {
$content .= $key2 . " = \n";
} else {
$content .= $key2 . " = \"" . $elem2 . "\"\n";
}
}
}
if (!$handle = fopen($path, 'w')) {
return false;
}
if (!fwrite($handle, $content)) {
return false;
}
fclose($handle);
return true;
}
write_ini_file($array,'./data.ini');
$readData = parse_ini_file('./data.ini',true);
print_r($readData);
I'm using parse_ini_file() to read an ini file that has this line:
[admin]
hide_fields[] = ctr_ad_headerImg
the problem is that it outputs it like,
[admin]
hide_fields = Array
can someone help me out? how do I read "hide_fields[]" like a string?
Best Regards
Joricam
My code is:
$ini_array = parse_ini_file($config_path, true);
//print_r($ini_array);
//echo $ini_array["default_colors"]["sitebg"];
$ini_array["default_colors"]["sitebg"]="#000000";
write_php_ini($ini_array,$config_path);
Functions that Im using:
function write_php_ini($array, $file)
{
$res = array();
foreach($array as $key => $val)
{
if(is_array($val))
{
$res[] = "[$key]";
foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : ''.$sval.'');
}
else $res[] = "$key = ".(is_numeric($val) ? $val : ''.$val.'');
}
safefilerewrite($file, implode("\r\n", $res));
}
//////
function safefilerewrite($fileName, $dataToSave)
{ if ($fp = fopen($fileName, 'w'))
{
$startTime = microtime();
do
{ $canWrite = flock($fp, LOCK_EX);
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
if(!$canWrite) usleep(round(rand(0, 100)*1000));
} while ((!$canWrite)and((microtime()-$startTime) < 1000));
//file was locked so now we can store information
if ($canWrite)
{ fwrite($fp, $dataToSave);
flock($fp, LOCK_UN);
}
fclose($fp);
}
}
Parse_ini_file() does handle such identifiers. It correctly converts them into arrays on reading the ini file:
print_r(parse_ini_string("hide_fields[] = ctr_ad_headerImg"));
Will generate:
Array
(
[hide_fields] => Array
(
[0] => ctr_ad_headerImg
)
The entry can be accessed as $cfg["hide_fields"][0] in PHP. The problem is that the ini file output function you have chosen this time does not understand array attributes.
Since you are probably interested in workarounds instead of using an appropriate tool, apply this conversion loop on your ini data:
// foreach ($sections ...) maybe
foreach ($cfg as $key=>$value) {
if (is_array($value)) {
foreach ($value as $i=>$v) {
$cfg["$key"."[$i]"] = $v;
}
unset($cfg[$key]);
}
}
And save it afterwards.
Edited code
function write_php_ini($array, $file)
{
$res = array();
foreach($array as $key => $val)
{
if(is_array($val))
{
$res[] = "[$key]";
foreach($val as $skey => $sval) {
if (is_array($sval)) {
foreach ($sval as $i=>$v) {
$res[] = "{$skey}[$i] = $v";
}
}
else {
$res[] = "$skey = $sval";
}
}
}
else $res[] = "$key = $val";
}
safefilerewrite($file, implode("\r\n", $res));
}
//////
function safefilerewrite($fileName, $dataToSave)
{
file_put_contents($fileName, $dataToSave, LOCK_EX);
}
I cannot find a way that easily lets me create a new file, treat it as an ini file (not php.ini or simiilar... a separate ini file for per user), and create/delete values using PHP. PHP seems to offer no easy way to create an ini file and read/write/delete values. So far, it's all just "read" - nothing about creating entries or manipulating keys/values.
Found following code snippet from the comments of the PHP documentation:
function write_ini_file($assoc_arr, $path, $has_sections=FALSE) {
$content = "";
if ($has_sections) {
foreach ($assoc_arr as $key=>$elem) {
$content .= "[".$key."]\n";
foreach ($elem as $key2=>$elem2) {
if(is_array($elem2))
{
for($i=0;$i<count($elem2);$i++)
{
$content .= $key2."[] = \"".$elem2[$i]."\"\n";
}
}
else if($elem2=="") $content .= $key2." = \n";
else $content .= $key2." = \"".$elem2."\"\n";
}
}
}
else {
foreach ($assoc_arr as $key=>$elem) {
if(is_array($elem))
{
for($i=0;$i<count($elem);$i++)
{
$content .= $key."[] = \"".$elem[$i]."\"\n";
}
}
else if($elem=="") $content .= $key." = \n";
else $content .= $key." = \"".$elem."\"\n";
}
}
if (!$handle = fopen($path, 'w')) {
return false;
}
$success = fwrite($handle, $content);
fclose($handle);
return $success;
}
Usage:
$sampleData = array(
'first' => array(
'first-1' => 1,
'first-2' => 2,
'first-3' => 3,
'first-4' => 4,
'first-5' => 5,
),
'second' => array(
'second-1' => 1,
'second-2' => 2,
'second-3' => 3,
'second-4' => 4,
'second-5' => 5,
));
write_ini_file($sampleData, './data.ini', true);
Good luck!
PEAR has two (unit tested) packages which do the task you are longing for:
Config_Lite - ideal if you only want .ini files
Config - reads also .php and .xml files
I'd rather use well tested code than writing my own.
I can't vouch for how well it works, but there's some suggestions for implementing the opposite of parse_ini_file() (i.e. write_ini_file, which isn't a standard PHP function) on the documentation page for parse_ini_file.
You can use write_ini_file to send the values to a file, parse_ini_file to read them back in - modify the associative array that parse_ini_file returns, and then write the modified array back to the file with write_ini_file.
Does that work for you?
in this portion of code:
else {
foreach ($assoc_arr as $key=>$elem) {
if(is_array($elem))
{
for($i=0;$i<count($elem);$i++)
{
$content .= $key2."[] = \"".$elem[$i]."\"\n";
}
}
else if($elem=="") $content .= $key2." = \n";
else $content .= $key2." = \"".$elem."\"\n";
}
}
$key2 must be replaced by $key or you would find empty keys in your .ini
based on the above answers I wrote this class that might be useful. For PHP 5.3 but can be easily adapted for previous versions.
class Utils
{
public static function write_ini_file($assoc_arr, $path, $has_sections)
{
$content = '';
if (!$handle = fopen($path, 'w'))
return FALSE;
self::_write_ini_file_r($content, $assoc_arr, $has_sections);
if (!fwrite($handle, $content))
return FALSE;
fclose($handle);
return TRUE;
}
private static function _write_ini_file_r(&$content, $assoc_arr, $has_sections)
{
foreach ($assoc_arr as $key => $val) {
if (is_array($val)) {
if($has_sections) {
$content .= "[$key]\n";
self::_write_ini_file_r(&$content, $val, false);
} else {
foreach($val as $iKey => $iVal) {
if (is_int($iKey))
$content .= $key ."[] = $iVal\n";
else
$content .= $key ."[$iKey] = $iVal\n";
}
}
} else {
$content .= "$key = $val\n";
}
}
}
}
I use this and it seems to work
function listINIRecursive($array_name, $indent = 0)
{
global $str;
foreach ($array_name as $k => $v)
{
if (is_array($v))
{
for ($i=0; $i < $indent * 5; $i++){ $str.= " "; }
$str.= " [$k] \r\n";
listINIRecursive($v, $indent + 1);
}
else
{
for ($i=0; $i < $indent * 5; $i++){ $str.= " "; }
$str.= "$k = $v \r\n";
}
}
}
it returns the text to write to an .ini file