I made this PHP script that gets data from a JSON file and then it edits or adds a line at the bottom of multiple CSV files. This script runs every 60 seconds with a cronjob, and it takes up a lot of CPU when running. I'm not an expert and I might need some tips to optimize it, or even change the way it works for better performance. Any help is appreciated
//get data from json
$coins = 'BTC,ETH,BNB,XRP,USDT,DOT';
$url = 'https://apilink'.$coins;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$stats = json_decode($result, true);
curl_close($ch);
$timeupd = date('d/m/Y H:i:s');
//FIRST COIN
$date = $stats[0]['price_date'];
$createDate = new DateTime($date);
$date = $createDate->format('Y-m-d');
$cap = $stats[0]['market_cap'];
$high = $stats[0]['high'];
$daypc = number_format((float)$stats[0]['1d']['price_change_pct'] * 100, 2, '.', '');
$weekpc = number_format((float)$stats[0]['7d']['price_change_pct'] * 100, 2, '.', '');
$adjclose = $stats[0]['price'];
$volume = $stats[0]['1d']['volume'];
$monthpc = number_format((float)$stats[0]['30d']['price_change_pct'] * 100, 2, '.', '');
$yearpc = number_format((float)$stats[0]['365d']['price_change_pct'] * 100, 2, '.', '');
$ytdpc = number_format((float)$stats[0]['ytd']['price_change_pct'] * 100, 2, '.', '');
//create array
$array = array($date, $cap, $high, $daypc, $weekpc, $adjclose, $volume, $monthpc, $yearpc, $ytdpc, $timeupd);
//get last row from csv file
$rows = file('/firstcoin.csv');
$last_row = array_pop($rows);
$data = str_getcsv($last_row);
//add new line or modify it
if($date == $data[0] && !empty($stats[0]['price'])){
$f = '/firstcoin.csv';
$rows = file($f);
array_pop($rows); // remove final element/row from $array
file_put_contents($f, implode($rows)); // convert back to string and overwrite file
$handle = fopen("/firstcoin.csv", "a");
fputcsv($handle, $array);
fclose($handle);
} elseif($date != $data[0] && !empty($stats[0]['price'])) {
$handle = fopen("/firstcoin.csv", "a");
fputcsv($handle, $array);
fclose($handle);
} else {
echo 'EMPTY JSON RESPONSE FOR FIRST COIN';
}
//SECOND COIN
//....other csv
//THIRD COIN
//....other csv
//ETC
You're reading the whole file into memory at least once, then writing the whole file back to disk in the first condition. That's very expensive, and as your file grows it will get worse. Since you only need to read and write to the end of the file, you should find a way to just read in the last line, optionally delete it, then write your new line to the end of the file.
This is a proof of concept, you will want to test this sort of technique carefully to make sure it works with the line endings on your system.
<?php
//get last row from csv file
$handle = fopen('/firstcoin.csv', 'a+');
$data = getLastCsvLine($handle, $lastLineOffset);
//add new line or modify it
if($date == $data[0] && !empty($stats[0]['price'])){
/*
* Remove last line from the file
* getLastCsvLine will set the file pointer to the beginning of the line,
* so we can just truncate from there then add our line
*/
ftruncate($handle, $lastLineOffset);
fputcsv($handle, $array);
} elseif($date != $data[0] && !empty($stats[0]['price'])) {
fputcsv($handle, $array);
} else {
echo 'EMPTY JSON RESPONSE FOR FIRST COIN';
}
fclose($handle);
/**
* Get the last CSV line in a file
*
* @param $fileHandle
* @return array|false
*/
function getLastCsvLine($fileHandle, &$lastLineOffset)
{
// Set the initial reverse offset
$cursor = -1;
// Set the file pointer to the end of the file
fseek($fileHandle, $cursor, SEEK_END);
// Rewind past any newlines at the end of the file
do
{
$char = fgetc($fileHandle);
fseek($fileHandle, --$cursor, SEEK_END);
} while( $char === "\n" || $char === "\r" );
// Read backwards until we hit the next newline or the beginning of the file
do
{
// Step backwards one byte
fseek($fileHandle, --$cursor, SEEK_END);
// Get the current char
$char = fgetc($fileHandle);
// Test to see if it is a newline char
$foundLineStart = ( $char === false || $char === "\n" || $char === "\r" );
} while (!$foundLineStart);
// Set the last line offset var and seek to the offset
$lastLineOffset = ftell($fileHandle);
// Return the data for the line
return fgetcsv($fileHandle);
}