Caching JSON request in PHP - Cached file error - php

I am using weatherundeground.com to get weather data but I always reach the API calls limit so I was thinking about caching the json response every 60 minutes.
This is my simple php script
<?php
$json_string = file_get_contents("http://api.wunderground.com/api/apikey/conditions/forecast/lang:IT/q/CITY1.json");
$parsed_json = json_decode($json_string);
$city1 = $parsed_json->{'current_observation'}->{'display_location'}->{'city'};
I searched and found this answer: Caching JSON output in PHP
I tried to merge them like this:
$url = "http://api.wunderground.com/api/apikey/conditions/forecast/lang:IT/q/SW/Acquarossa.json";
function getJson($url) {
// cache files are created like cache/abcdef123456...
$cacheFile = 'cache' . DIRECTORY_SEPARATOR . md5($url) . '.json';
if (file_exists($cacheFile)) {
$fh = fopen($cacheFile, 'r');
$cacheTime = trim(fgets($fh));
// if data was cached recently, return cached data
if ($cacheTime > strtotime('-60 minutes')) {
return fread($fh);
}
// else delete cache file
fclose($fh);
unlink($cacheFile);
}
$json = file_get_contents($url);
$fh = fopen($cacheFile, 'w');
fwrite($fh, time() . "\n");
fwrite($fh, $json);
fclose($fh);
return $json;
}
$json_string = getJson($url);
$parsed_json = json_decode($json_string);
$city1 = $parsed_json->{'current_observation'}->{'display_location'}->{'city'};
I have been able to set it up and now it gets the first "round" of data, but the 2nd one and all the following, give me an error:
Warning: fread() expects exactly 2 parameters, 1 given in /home/*****/public_html/*****/acquarossa.php on line 27.
And if I put the cached json on any json validator, it says that it isn't a valid json.
( this is the cached file: http://spinnaker.url.ph/meteo/cache/1f58bbab7bf88f3f8561b769475cb7c1.json )
What can I do?
P.S.:I already CHMOD 777 the directory

Actually, the warning is pretty clear about whats going wrong. fread() expects 2 parameters but you're only passing on one in line 27: return fread($fh);
Reading the manual for fread I guess you can fix this by changing
return fread($fh);
to
return fread($fh, filesize($chacheFile));

Related

PHP file stream data coming back in Binary

So I have a function that I CANNOT get to work. What's happening is it's coming back with data that's in binary and not the actual file. However, in the binary that I get back, the name of the particular file is included in the string. The data looks like this:
PK‹Mpt-BR/finalinport.html³)°S(ÉÈ,V¢‘ŒT…‚ü¢’ÒôÒÔâT…´Ì¼Ä ™” “I,QðT(ÏÌÉQ(-ÈÉOLQH„¨‡*Ê/B×]’X”žZ¢`£_`ÇPK.Ùô LePK‹M.Ùô Lept-BR/finalinport.htmlPKD
pt-BR is the directory and the 'finalinport.html' is the file that I am trying to have downloaded. If I replace the second parameter of fwrite to just a plain string, then everything works and I get the string that I wrote in a file inside of the zip. But not when I'm using Stream->getContents(), which leads me to believe that it is something going on with the stream. I cannot wrap my head around what can be happening. I've been on this for a week and a half now so any suggestions would be great.
public function downloadTranslations(Request $request, $id)
{
$target_locales = $request->input("target_locale");
$has_source = $request->input("source");
$client = new API(Auth::user()->access_token, ZKEnvHelper::env('API_URL', 'https://myaccount.com'));
$document = Document::find($id);
$job_document = JobDocument::where('document_id', $id)->first();
$job = Job::find($job_document->job_id);
$file = tempnam('tmp', 'zip');
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
$name_and_extension = explode('.', $document->name);
if($target_locales == null){
$target_locales = [];
foreach ($job->target_languages as $target_language) {
$target_locales[] = $target_language['locale'];
}
}
foreach($target_locales as $target_locale){
$translation = $client->downloadDocument($document->document_id, $target_locale);
$filename = $name_and_extension[0] . ' (' . $target_locale . ').' . $name_and_extension[1];
if($translation->get('message') == 'true') {
//API brings back file in stream type
$stream = Stream::factory($translation->get('body'));
$newFile = tempnam(sys_get_temp_dir(), 'lingo');
$handle = fopen($newFile, 'w');
fwrite($handle, $stream->getContents());
$zip->addFile($newFile, 'eh.html');
fclose($handle);
}
else if($translation->get('message') == 'false'){
//API brings back file contents
$zip->addFromString($filename, $translation->get('body'));
}
}
$translation = $client->downloadDocument($document->document_id, null, null);
$filename = $name_and_extension[0]. ' (Source).'.$name_and_extension[1];
$zip->addFromString($filename, $translation->get('body'));
sleep(10);
$zip->close();
return response()->download($file, $name_and_extension[0].'.zip')->deleteFileAfterSend(true);
}
I'm unfamiliar with PHP streams and I don't have a debugger set up so I keep thinking it has something to do with how I am handling the stream. Because the other condition (the else if) is coming back as content of the file (string) and the if statement, the data is coming back as a stream resource, which I am unfamiliar with.
Stream::factory
is used in Guzzle 5, use json_encode for Guzzle 6.

PHP Lock file while reading - flock() makes my file blank

As you can see in the code below, I'm trying to use flock to prevent other clients to acess the php (actually multiple users will acess this something like 10 times per second, each one), as I've found searching here... But this is not working. My data.txt is getting blank everytime doing this.
<?php
$fileName = $_GET["room"]."/data.txt";
function replaceLine($data){
if (stristr($data, $_GET["player"])){
return $_GET["player"]." ".$_GET["data"]."\n";
}
return $data;
}
$file = fopen($fileName,"r");
if (flock($file, LOCK_EX)){
//ftruncate($file, 0);
///--------------
$data = file($fileName);
$data = array_map("replaceLine", $data);
file_put_contents($fileName, implode('', $data));
echo fread($file, filesize($fileName)+1);
///--------------
fflush($file);
flock($file, LOCK_UN);
} else {
echo "wait";
}
fclose($file);
?>
This is the original code (that I was trying to modify to prevent making the file empty): (It works as I want, but have this file problem...)
<?php
$fileName = $_GET["room"]."/data.txt";
function replaceLine($data){
if (stristr($data, $_GET["player"])){
return $_GET["player"]." ".$_GET["data"]."\n";
}
return $data;
}
$data = file($fileName);
$data = array_map("replaceLine", $data);
file_put_contents($fileName, implode('', $data));
$file = fopen($fileName,"r");
echo fread($file, filesize($fileName)+1);
fclose($file);
?>
Sorry for asking this newbie question, but I have not idea how to fix this and I'm searching and trying different things for weeks! Thanks!
You are opening the file for read only and then you are attempting to write to that same file. Try setting the fopen parameter to read/write.
$file = fopen($fileName,"r+");
I would also use fwrite() instead of file_put_contents() since you already have the file pointer and opening it again will likely be denied by the lock.

Create CSV File in PHP and Save to SFTP using phpseclib

I need to generate a CSV file from a MySQL query and save the file to an SFTP server. I have tried the code below. The CSV file gets created, but it is empty. I also receive an error message in the browser that says Warning: is_file() expects parameter 1 to be a valid path, resource given in regard to this line $sftp->put($fileName, $fp, NET_SFTP_LOCAL_FILE);. If I move fclose($fp); to the last line, I don't get the error but data still doesn't appear in the file. Could someone please let me know how to get the data to save in the file that was created?
$fileName = 'dataFiles/reports/Report Summary/Report Summary.csv';
$sql = mysqli_query($db, "
SELECT *
FROM reports
WHERE reportID = 1
");
$fp = fopen('php://output', 'w');
$first = true;
while($row = mysqli_fetch_assoc($sql)){
if ($first) {
fputcsv($fp, array_keys($row));
$first = false;
}
fputcsv($fp, $row);
}
fclose($fp);
$sftp->put($fileName, $fp, NET_SFTP_LOCAL_FILE);
Try something like this:
<?php
$fp = fopen('php://temp', 'r+');
// do stuff
rewind($fp);
$sftp->put($filename, $fp);
phpseclib (assuming you're using a new enough version) will detect that the second parameter is a stream resource and will try to read from it accordingly.
The second argument is not a handle but the content directly.
I think you could do: stream_get_contents($fp); in the second argument.
$content = stream_get_contents($fp);
fclose($fp);
$sftp->put($fileName, $content, NET_SFTP_LOCAL_FILE);

PHP - Not able to get content with fread

I am trying to get the API data from a cache file if there already was the same request recently. Everything works fine but I am just not able to get the content from the cache file even tho it is there. I can't find an error. I hope u can help me.
$url = /* API URL */;
function getJson($url) {
$cacheFile = 'cache' . DIRECTORY_SEPARATOR . md5($url) . '.json';
if (file_exists($cacheFile)) {
$fh = fopen($cacheFile, 'r');
$cacheTime = filemtime($cacheFile);
if ($cacheTime > strtotime('-60 minutes')) {
$json = fread($fh);
return $json;
}
fclose($fh);
unlink($cacheFile);
}
$json = file_get_contents($url);
$fh = fopen($cacheFile, 'w');
fwrite($fh, $json);
fclose($fh);
return $json;
}
$datab = getJson($url);
$data = json_decode($datab, true);
print_r($data);
Il you dont check for your error log or display errors on screen you gonna have a bad time finding its.
Here you have a $cacheFile which should be a $cachePath, so prefix with './' or better DIR
Then you call fread without second parameter, and so on...check your error log, enable all errors.
You must specify the length that needs to be read in fread.
Use:
if (file_exists($cacheFile)) {
$fh = fopen($cacheFile, 'r');
$cacheTime = filemtime($cacheFile);
if ($cacheTime > strtotime('-60 minutes')) {
$json = fread($fh,filesize($cacheFile));
fclose($fh); //Remember to release resources
return $json;
}
fclose($fh);
unlink($cacheFile);
}
Alternatively use file_get_contents($cacheFile) to get the whole file without the need for an fopen first.

PHP cache writes nothing to text file

I try to make my PHP cache with this code:
//Make cache files
$cache = 'tweets-cache.txt';
$date = 'tweets-date.txt';
$currentTime = time(); // Current time
// Get cache time
$datefile = fopen($date, 'r');
$cacheDate = fgets($datefile);
fclose($datefile);
//check if cache has expired
if (floor(abs(($currentTime-$cacheDate) / 10800)) <= $_GET['expiry'] && $cacheDate) {
$cachefile = fopen($cache, 'r');
$data = fgets($cachefile);
fclose($cachefile);
} else {
//Make the REST call
$data = (array) $cb->$api($params);
// update cache file
$cachefile = fopen($cache, 'wb');
fwrite($cachefile, utf8_encode($data));
fclose($cachefile);
// update date file
$datefile = fopen($date, 'wb');
fwrite($datefile, utf8_encode(time()));
fclose($datefile);
}
//Output result in JSON, getting it ready for jQuery to process
echo json_encode($data);
Now he writes nothing in tweets-cache.txt.
i think it is because of he cannot write an array with utf8_encode.
Yes, you are right about utf8_encode() function. Try to write serialized data to the file:
fwrite($cachefile, json_encode($data));
and when you read your data back from the file, do de-serialization:
$data = json_decode(fgets($cachefile));
Also, you can make things simpler if you use functions like file_get/put_contents, so your code:
$cachefile = fopen($cache, 'r');
$data = fgets($cachefile);
fclose($cachefile);
will be:
$data = json_decode(file_get_contents($cache));
and:
// update cache file
$cachefile = fopen($cache, 'wb');
fwrite($cachefile, utf8_encode($data));
fclose($cachefile);
// update date file
$datefile = fopen($date, 'wb');
fwrite($datefile, utf8_encode(time()));
fclose($datefile);
translates to:
// update cache file
file_put_contents($cache, json_encode($data));
// update date file
file_put_contents($date, time());
Be aware of the synchronization problems you can face when you are using filesystem in multithreaded/multiprocess environment.
Two and more php instances can make attempt to write data to the same file in the same time, and you will get corrupted file.
You can use filesystem locking or special cache software like Memcached, which is much more preferable solution for such cases.
Memcached
PHP Reference: file_put_contents()
PHP Reference: file_get_contents()
PHP Reference: json_encode()

Categories