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
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.
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];
}
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
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);
}
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";
}
}