How write arrays name while parsing an ini file in PHP? - php

I am using parse_ini_file to parse an ini file using PHP.
Now I first upload an INI file to my server then open it and allow user to mak custom changes to the file.
Now once users has edited the file i get the data from post and save the file back to server. But Now i dont get my sections. INIdetails,Dialplan in the updated file so when i also want to write that to file how to do that?
This is the code :
$this->data['parameters'] = parse_ini_file($path.$filename,true);
/*Getting Our POST DATA from View*/
$data = array(
'SipUserName' => $this->input->post('SipUserName') ,
'SipAuthName' => $this->input->post('SipAuthName'),
'DisplayName' => $this->input->post('DisplayName'),
'Password' => $this->input->post('Password'),
'Domain' => $this->input->post('Domain'),
'Proxy' => $this->input->post('Proxy'),
'Port' => $this->input->post('Port'),
'ServerMode' => $this->input->post('ServerMode'),
'Param_1' => $this->input->post('Param_1'),
'Param_2' => $this->input->post('Param_2')
);
/*Creating New file with the name of customer loggedin*/
$name = $this->session->userdata('username');
$ext = $this->session->userdata('extension');
$custom_file = fopen('uploads/'.$name.'_'.$ext.'.ini', 'w');
fwrite($custom_file, "[INIDetails]\n");
foreach ($data as $key => $value)
{
fwrite($custom_file, " $key = $value\n");
}
fclose($custom_file);
/*Setting path to New CUSTOM file with customer name as prefix*/
$file = $path.$custom_file;
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);
}
}
/*Creates ini file, dumps array to string and creates .INI file*/
write_php_ini($data,$file);

The culprit from your previous code is that your array is not formatted correctly, it should be array of arrays to achieve what you want.
Try below code:
// First read the ini file that the user was editing
// Your idea to read the existing ini file is good, since it will generate you the structured array
$previous_data = parse_ini_file($path . $filename, true);
// Overwrite/edit the previous_data using user's post data
$previous_data['INIDetails']['SipUserName'] = $this->input->post('SipUserName');
$previous_data['INIDetails']['Password'] = $this->input->post('Password');
$previous_data['INIDetails']['Domain'] = $this->input->post('Domain');
$previous_data['INIDetails']['Proxy'] = $this->input->post('Proxy');
$previous_data['INIDetails']['Port'] = $this->input->post('Port');
$previous_data['INIDetails']['SipAuthName'] = $this->input->post('SipAuthName');
$previous_data['INIDetails']['DisplayName'] = $this->input->post('DisplayName');
$previous_data['INIDetails']['ServerMode'] = $this->input->post('ServerMode');
$previous_data['INIDetails']['UCServer'] = $this->input->post('UCServer');
$previous_data['INIDetails']['UCUserName'] = $this->input->post('UCUserName');
$previous_data['INIDetails']['UCPassword'] = $this->input->post('UCPassword');
$previous_data['DialPlan']['DP_Exception'] = $this->input->post('DP_Exception');
$previous_data['DialPlan']['DP_Rule1'] = $this->input->post('DP_Rule1');
$previous_data['DialPlan']['DP_Rule2'] = $this->input->post('DP_Rule2');
$previous_data['DialPlan']['OperationMode'] = $this->input->post('OperationMode');
$previous_data['DialPlan']['MutePkey'] = $this->input->post('MutePkey');
$previous_data['DialPlan']['Codec'] = $this->input->post('Codec');
$previous_data['DialPlan']['PTime'] = $this->input->post('PTime');
$previous_data['DialPlan']['AudioMode'] = $this->input->post('AudioMode');
$previous_data['DialPlan']['SoftwareAEC'] = $this->input->post('SoftwareAEC');
$previous_data['DialPlan']['EchoTailLength'] = $this->input->post('EchoTailLength');
$previous_data['DialPlan']['PlaybackBuffer'] = $this->input->post('PlaybackBuffer');
$previous_data['DialPlan']['CaptureBuffer'] = $this->input->post('CaptureBuffer');
$previous_data['DialPlan']['JBPrefetchDelay'] = $this->input->post('JBPrefetchDelay');
$previous_data['DialPlan']['JBMaxDelay'] = $this->input->post('JBMaxDelay');
$previous_data['DialPlan']['SipToS'] = $this->input->post('SipToS');
$previous_data['DialPlan']['RTPToS'] = $this->input->post('RTPToS');
$previous_data['DialPlan']['LogLevel'] = $this->input->post('LogLevel');
// Set Name of New file with the name of customer logged in
$name = $this->session->userdata('username');
$ext = $this->session->userdata('extension');
$custom_file = "$name_$ext.ini";
$new_filename = $path . $custom_file;
// Write the INI file
write_php_ini($data, $new_filename);
function write_php_ini($array, $new_filename)
{
$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($new_filename, implode("\r\n", $res));
}
function safefilerewrite($new_filename, $dataToSave)
{
if ($fp = fopen($new_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);
}
}
From your previous code, there are bunch of inappropriate codes which I remove. Also, too many inconsistencies like Method naming, variable naming etc.
If your function was named Camel cased then through out your code it must be named as camel-cased. If your variables are with underscore, then through out your code, they must have underscore for two or more worded variable.
I didn't edit the naming convention of your code so you won't be confused but i suggest to have a consistent naming convention through out your project.
UPDATED:
based on your answer, it seems like you changed your whole code. I would like to provide another way using nested foreach and passing by reference that save couple of lines:
$this->data['params'] = $this->parameter_m->get();
$this->data['parameters'] = parse_ini_file($path . $filename, true);
foreach ($this->data['parameters'] as $key_header => &$value_header) {
foreach ($value_header as $key_item => &$value_item) {
$value_item = $this->input->post($key_item);
}
}
$this->load->helper('file');
$this->load->library('ini');
$file = $path . $filename;
$ini = new INI($file);
$ini->read($file);
$ini->write($file, $this->data['parameters']);

Finally i got an answer:
I will loop through what i get from POST and will get each array's key. And then i will give the resultant to my Write Method
$this->data['params'] = $this->parameter_m->get();
/*Getting the parameters to display on view*/
$this->data['parameters'] = parse_ini_file($path.$filename,true);
while (current($this->data['parameters']) )
{
$param_set = current($this->data['parameters']);
$param_type = key($this->data['parameters']);
foreach ($param_set as $key =>$value)
{
$this->data['parameters'][$param_type][$key] = $this->input->post($key);
}
next($this->data['parameters']);
}
$this->load->helper('file');
$this->load->library('ini');
$file = $path.$filename;
$ini = new INI($file);
$ini->read($file);
$ini->write($file, $this->data['parameters']);

Related

Optimize runtime of file processed search in large array for every entry

I have developed a file upload and processing with Laravel. But the runtime is very long.
File looks like this: (very large, about 50k hands per file)
QhQs3s2s#86,QdQs3s2s#86,QcQs3s2s#86,KhKs3s2s#100,KdKs3s2s#100,KcKs3s2s#100,AhAs3s2s#86,AdAs3s2s#86,AcAs3s2s#86
It is uploaded via txt upload and then chunked into sets of 1000 "Hands"
/**
* Upload the create Files
*/
public function uploadFile(Request $request)
{
// process SituationName
$name = $request->input('name');
$situation = Situation::firstOrCreate(['name' => $name, 'active' => 1]);
//process RaiseRange
$action = Action::where('name', 'Raise')->first();
$path = $request->file('rangeRaise')->store('ranges');
//Split Files
$content = Storage::disk('local')->get($path);
$array = explode(",", $content);
$arrayFinal = array_chunk($array, 1000);
foreach($arrayFinal as $arrayJob){
$filename = 'ranges/RaiseFile'.uniqid().'.txt';
Storage::disk('local')->put($filename, json_encode($arrayJob));
ProcessRangeFiles::dispatch($action, $situation, $filename);
}
}
Then it gets dispatched as a job with following handle
public function handle()
{
Log::info('File Processing started');
$array = null;
$content = null;
$found = null;
$path = $this->path;
$action = $this->action;
$situation = $this->situation;
$hands = Hand::all();
$content = json_decode(Storage::disk('local')->get($path));
foreach ($content as $key=>$line){
$array[$key] = explode('#', $line);
foreach($hands as $hand){
if($hand->hand == $array[$key][0]){
$found = $hand;
break;
}
}
DB::table('hands_to_situations_to_actions')->insert(
['hand_id' => $found->id, 'action_id' => $action->id, 'situation_id' => $situation->id, 'percentage' => $array[$key][1], 'created_at' => Carbon::now()->toDateTimeString(), 'updated_at' => Carbon::now()->toDateTimeString()]
);
}
Log::info('File Processing finished');
}
$hands is filled with every possible Poker Omaha Hand.
Has anyone an idea how to optimize this code? Then runtime for every 1000 chunks is about 12 Minutes.

Get googlesheet data by passing spreadsheet id and sheet id

I need to get the google sheet data and then to download it as csv. but the issue is I need to get data by sheet id in the url.
I'm using this package "google/apiclient": "^2.0"
Code
$sheets = new \Google_Service_Sheets($client);
//get spreadsheet id from db
$google_sheet_link = TeamGoogleSheet::where('team_id',$team_id)->first();
$url_array = parse_url($google_sheet_link->url);
$path_array = explode("/",$url_array["path"]);
$spreadsheetId = $path_array[3];
\Log::info('Spreadsheet id');
\Log::info($spreadsheetId);
$range = 'Sheet1';
$rows = $sheets->spreadsheets_values->get($spreadsheetId, $range, ['majorDimension' => 'ROWS']);
if (isset($rows['values'])) {
$filename = storage_path("chat_bots.csv");
if (file_exists($filename))
unlink($filename);
$handle = fopen($filename, 'a');
foreach ($rows['values'] as $key => $data) {
fputcsv($handle, $data);
}
fclose($handle);
$headers = array(
'Content-Type' => 'text/csv',
);
return \Response::download($filename, 'chat_bots.csv', $headers);
}
But this requires the sheet name(range attribute) and also only 1 sheet.I want to make it dynamic. we can get sheetid from url, but didn't find a method to retrieve data by passing this sheet id.
$spreadsheet_data = $sheets->spreadsheets->get($spreadsheetId);
//get sheet titles
$work_sheets = [];
$gid_sheet = '';
foreach($spreadsheet_data->getSheets() as $s) {
$work_sheets[] = $s['properties']['title'];
}
if(preg_match("/[#&]gid=([0-9]+)/", $google_sheet_link->url)){
$explode_array = explode("#gid=",$google_sheet_link->url);
$gid = $explode_array[1];
foreach($spreadsheet_data->getSheets() as $s) {
if($s['properties']['sheetId'] == $gid)
$gid_sheet = $s['properties']['title'];
}
}
if($gid_sheet != ''){
$range = $gid_sheet;
}
else{
$range = $work_sheets[0];
}

Phalcon Query super slow inside large loop

I'm using Phalcon 3.0.4. I made a foreach on each file inside my folder. Currently I have just 4000 files. I did a findFirst to check if the filename already exist in MySQL (I have 100 000 rows in my table). But when I use findFirst, the response is super slow (I have to wait 20 minutes to get a response). Here is my code :
$dir = new FilesystemIterator("files/path/to/my/files/");
foreach ($dir as $file) {
if ($file->getExtension() == 'json') {
$filename = $file->getFilename();
$explode_filename = explode("_", $filename);
$date = $explode_filename[0];
$unformatted_date = DateTime::createFromFormat("Ymd-His", $date);
$date_server = $unformatted_date->format("Y-m-d H:i:s");
$timestamp_app = $explode_filename[2];
$date_app = date("Y-m-d H:i:s", $timestamp_app/1000);
echo $date_server;
$json_data = json_decode(file_get_contents($file), true);
$scan = Scans::findFirst(array(
"name = :name:",
"bind" => array("name" => $filename)
));
if (!$scan) {
...
}
}
}
I tried to make my query with the QueryBuilder PHQL but I have the same result:
$scan = $this->modelsManager->createBuilder()
->from("Scans")
->where("name = :name:", ["name" => $filename])
->limit(1)
->getQuery()
->execute();
If I remove the findFirst or queryBuilder the response is ~30ms but with the findFirst it will takes ~20 minutes... How can I do to increase the performance of the search in my table ?
By changing your code to better performing one:
$dir = new FilesystemIterator("files/path/to/my/files/");
$fileNames = [];
foreach ($dir as $file) {
if ($file->getExtension() == 'json') {
$filename = $file->getFilename();
$explode_filename = explode("_", $filename);
$date = $explode_filename[0];
$unformatted_date = DateTime::createFromFormat("Ymd-His", $date);
$date_server = $unformatted_date->format("Y-m-d H:i:s");
$timestamp_app = $explode_filename[2];
$date_app = date("Y-m-d H:i:s", $timestamp_app/1000);
echo $date_server;
$json_data = json_decode(file_get_contents($file), true);
// save the above data to some arrays
$fileNames[] = $fileName;
}
}
$scans = Scans::find([
'columns' => 'check only columns you need, otherwise you will have full models with hydration',
'conditions' => 'name IN ({fileNames:array})',
'group' => 'name',
'bind' => [
'fileNames' => $fileNames
]
]);
foreach($fileNames as $fileName) {
$filteredScans = $scans->filter(function($scan) use ($fileName) {
return $scan->name == $fileName;
}
if(!$filteredScans) {
// do here whatever
}
}
This solution can be memory heavy though, then you could include here some paginations like do some limit like proper for and do 100-10000 rows at once depending how much RAM you have.
create index on Scans.name
use group by Scans.name (if not uniq)
set some columns then be use

fopen creates file like Resource id #3?

i am trying yo create a new file using a existing file.
but When i create a new file in my uploads folder a file is automatically created with resource id #3 WHY??
public function edit_ini_custom($id)
{
/*Getting parameters name to display in view*/
$this->data['params'] = $this->parameter_m->get();
/*Path of our BASE and CUSTOM INI files*/
$path = "./uploads/";
$this->db->select('*');
$this->db->where('id',$id);
/*Here the id is the ID we got from URI View*/
$this->db->from('base_ini');
$query = $this->db->get();
$result = $query->row();
$filename= $result->base_ini_filename;
$path= $result->file_path;
/*Reading Contents from our path and the name of file we got from database*/
file_get_contents($path.$filename);
$this->data['parameters'] = parse_ini_file($path.$filename);
/*Getting Our POST DATA from View*/
$data = array(
'SipUserName' => $this->input->post('SipUserName') ,
'SipAuthName' => $this->input->post('SipAuthName'),
'DisplayName' => $this->input->post('DisplayName'),
'Password' => $this->input->post('Password'),
'Domain' => $this->input->post('Domain'),
'Proxy' => $this->input->post('Proxy'),
'Port' => $this->input->post('Port'),
'ServerMode' => $this->input->post('ServerMode'),
'Param_1' => $this->input->post('Param_1'),
'Param_2' => $this->input->post('Param_2')
);
$this->load->helper('file');
$suffix =$this->input->post('SipUserName');
/*Setting the Name of File*/
$name =$this->session->userdata('username');
/*Creating New file with the name of customer loggedin*/
$file_new = fopen('uploads/'.$name.$suffix.'.ini', 'w');
fwrite($file_new, "[INIDetails]\n");
foreach ($data as $key => $value)
{
fwrite($file_new, " $key = $value\n");
}
fclose($file_new);
/*Setting path to New CUSTOM file with customer name as prefix*/
$file = $path.$file_new;
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);
}
}
/*Creates ini file, dumps array to string and creates .INI file*/
write_php_ini($data,$file);
/*Back to you index page id data is submmited*/
if(isset($_POST['submit'] ))
{
redirect('customer/upload_ini/index');
}
$this->data['subview'] = 'customer/upload/edit_ini_custom';
$this->load->view('customer/_layout_main', $this->data);
}

Using PHP to display items on page

I have a document called subjects.txt in the following format:
DateCreated,Subject,Link
18.10.2015,"Math",http: //address.html
17.10.2015,"English",http: //address.html
18.10.2015,"English",http: //address.html
19.10.2015,"Science",http: //address.html
17.10.2015,"Math",http: //address.html
The file contains URLs of sites created based on a school subject. There can be more than one site for a subject.
The goal is to use PHP to open, read, and display the contents of the file in the following format:
Math
Link 1
Link 2
English
Link 1
Link 2
Science (because there's only one link, the name of the subject is the
link)
So far I've been able to open and read the file:
$file = "./subjects.txt";
$subjects = file_get_contents($file);
I'm having trouble trying to determine how to go about writing the file in specified format.
I've tried using explode to separate the elements with "," - however I don't know where to go from there.
Your input file looks to be in Comma-separated values (CSV) format. PHP has a built-in fgetcsv function designed to make reading CSV data from a file easy.
<?php
$file = './subjects.txt';
$fh = fopen($file, 'r');
if ($fh === false) {
die("Can not read {$file}");
}
$data = array();
while (($row = fgetcsv($fh, 1000, ',')) !== false) {
if ($row[0] === 'DateCreated') {
// Ignore the column header row
continue;
}
list($date, $subject, $link) = $row;
if (!isset($data[$subject])) {
$data[$subject] = array();
}
$data[$subject][] = $link;
}
fclose($fh);
foreach ($data as $subject => $links) {
// TODO: output each subject here
}
Here is another version
<?php
$file = "./subjects.txt";
$h = fopen($file, "r");
if($h !== false) {
$subjects = [];
$data = [];
while(!feof($h)) {
if($line = trim(fgets($h))) {
$line = explode(",", $line);
if(!in_array("DateCreated",$line)) {
array_push($subjects, $line);
}
}
}
fclose($h);
foreach ($subjects as $subject) {
if(!isset($data[$subject[1]])) {
$data[$subject[1]] = [];
}
$data[$subject[1]][] = $subject[2];
}
foreach ($data as $subject => $links) {
if(count($links) == 1) {
echo "<p>$subject</p>\n";
} else {
$i = 1;
echo "<p>$subject</p>\n";
echo "<ul>\n";
foreach ($links as $link) {
echo "<li>link$i</li>\n";
$i++;
}
echo "</ul>\n";
}
}
}
?>
The problem using file_get_contents() is that retrieves all the file contents into $subjects.
You have to use a different approach. For example fgets():
$fp = fopen("./subjects.txt", "r");
if ($fp){
while (($line = fgets($fp)) !== false){
// So here you can treat each line individually.
// You can use explode (";", $line) for example if the line is not empty
}
}
fclose($fp);
Using fgets() will allow you to parse each of the file's lines individually.
As stated doing this with a database would be much easier probably 3 lines of code. Here's one approach you could use though.
$data = '18.10.2015,"Math",http: //address.html
17.10.2015,"English",http: //address1.html
18.10.2015,"English",http: //address2.html
19.10.2015,"Science",http: //address3.html
17.10.2015,"Math",http: //address4.html';
preg_match_all('~^(.*?),"(.*?)",(.*?)$~m', $data, $fields);
array_multisort($fields[2], SORT_STRING, $fields[1], $fields[3]);
$lastcat = '';
foreach($fields[2] as $key => $cat) {
if($cat != $lastcat) {
echo $cat . "\n";
}
$lastcat = $cat;
echo $fields[3][$key] . "\n";
}
Output:
English
http: //address1.html
http: //address2.html
Math
http: //address4.html
http: //address.html
Science
http: //address3.html
The array_multisort is how the categories are grouped.
Here's a regex101 demo of what that regex is doing. https://regex101.com/r/wN3nB2/1
Update for single record check (only ran 1 test on it):
$data = '18.10.2015,"Math",http: //address.html
17.10.2015,"English",http: //address1.html
18.10.2015,"English",http: //address2.html
19.10.2015,"Science",http: //address3.html
17.10.2015,"Math",http: //address4.html';
preg_match_all('~^(.*?),"(.*?)",(.*?)$~m', $data, $fields);
array_multisort($fields[2], SORT_STRING, $fields[1], $fields[3]);
$lastcat = '';
foreach($fields[2] as $key => $cat) {
if((empty($fields[2][($key +1)]) && $cat != $lastcat)|| ($cat != $lastcat && !empty($fields[2][($key +1)]) && $fields[2][($key +1)] != $cat)) {
//single record
echo $cat . $fields[3][$key] . "\n";
} else {
if($cat != $lastcat) {
echo $cat . "\n";
}
$lastcat = $cat;
echo $fields[3][$key] . "\n";
}
}

Categories