How to close excel file in php-excel-reader - php

I am reading two excel file, using php-excel-reader (From this)
After reading two files of 1st row, I am comparing it. If they are same then I am appending contain of on file to other. To write the file I am using this
Now for doing this I want to close one file, but that function is not available in php-excel-reader
here is my code
compare file
{
$data = new Spreadsheet_Excel_Reader($filepath);
$data1 = new Spreadsheet_Excel_Reader($destinationfilepath);
}
unset($data);
unset($data1);
if($flag==0)
{
$excel = new ExcelWriter($destinationfilepath);
// read the source file
$finalarray= array();
for($m=1;$m<$sourcefilerowcount;$m++)
{
$charvalue='A';
$temprow=$m+1;
for($n=0;$n<$destinationcolnum;$n++)
{
$data = new Spreadsheet_Excel_Reader($filepath);
$finalarray[$n]=$data->val($temprow,$charvalue);
$charvalue++;
}
print_r($finalarray)."<br/>";
$excel->writeLine($finalarray);
}

There is no need to explicitly call close() function, because the file is automatically closed in the load() method. If you look at Excel2007.php, where PHPExcel_Reader_Excel2007 is defined, you'll see:
public function load($pFilename)
{
...
$zip = new ZipArchive;
$zip->open($pFilename);
...
$zip->close();
return $excel;
}
Just unset your PHPExcel_Reader object, and the data will be removed from memory:
$objReader = PHPExcel_IOFactory::createReader('Excel2003XML');
$objPHPExcel = $objReader->load("Excel2003XMLTest.xml");
...
unset($objPHPExcel);
unset($objReader);

Related

How to not have tmpfile() be deleted when out of the scope of the method that it was created by?

I'm on php#8.1.3. When I have one method both creating and reading from a tmpfile, everything works as expected:
class TmpFileReadRightAway
{
public function storeToTempFileAndReadRightAway(string $content): string
{
$fh = tmpfile();
$path = stream_get_meta_data($fh)['uri'];
fwrite($fh, $content);
return file_get_contents($path);
}
}
echo (new TmpFileReadRightAway())->storeToTempFileAndReadRightAway('this works as expected');
Yet when I split the method into multiple methods, the tempfile() is deleted after the method in which it was created returns.
This is not at all what I expected as I wanted to keep the file around. I would expect the tmpfile to be deleted at termination of the php code at the very end, not after it exits the method.
class TmpFileStoreButReadLater
{
public function storeButReadLater(string $content): string
{
$path = $this->getPath($content);
return file_get_contents($path); // file at path doesn't exist anymore here, why?
}
private function getPath($content): string
{
$fh = tmpfile();
$path = stream_get_meta_data($fh)['uri'];
fwrite($fh, $content);
return $path;
}
}
This would throw
PHP Warning: file_get_contents(/tmp/phpQsUdA5): Failed to open stream: No such file or directory
Why is the file being deleted in this case and how do I ensure it exists during the runtime of my code?
Use class property.
The tmpfile() document said.
The file is automatically removed when closed (for example, by calling
fclose(), or when there are no remaining references to the file handle
returned by tmpfile()), or when the script ends.
So, I assume that when method exits, the fclose() is called automatically.
The error about failed to open stream is not just occur in PHP 8.1 but all version since PHP 7.0 to 8.1. (I don't have PHP 5.x to test with.)
To prevent that, set the $fh to class property instead.
class TmpFileStoreButReadLater
{
protected $fh;
public function storeButReadLater(string $content): string
{
$path = $this->getPath($content);
return file_get_contents($path); // file at path doesn't exist anymore here, why?
}
private function getPath($content): string
{
$this->fh = tmpfile();
$path = stream_get_meta_data($this->fh)['uri'];
fwrite($this->fh, $content);
return $path;
}
}
echo (new TmpFileStoreButReadLater())->storeButReadLater('this works as expected');
Tested on PHP 7.0 - 8.1.3 but no errors now.

I am trying to download multiple image as a zip file but getting error using laravel 7

I am trying to download multiple images as a zip file but getting errors
Invalid argument supplied for foreach() please help me how i resolve that thanks.
Check the error: https://flareapp.io/share/47qG2A3m
Controller
public function dowloads($id)
{
$url = config('yourstitchart.file_url');
$zip = new ZipArchive;
$inboxFiles = Inbox::where('id', $id)->first()->file;
// $inboxFiles = "["phpCM0Yia.png","phptLC57a.png"]"
foreach ($inboxFiles as $file) {
$zip->add($url . $file); // update it by your path
}
$zip->close();
return response()
->download(
public_path('/temporary_files/' . "deals.zip"),
"deals.zip",
["Content-Type" => "application/zip"]
);
}
You are returning a string, you can't handle it like an array.
It's JSON, you can just use :
$inboxFiles = json_decode(Inbox::where('id', $id)->first()->file);
(the above code is not really robust, but you have the way)
I know this has already been answered, but do not use json_decode any more in Laravel...
Cast the field file as a JSON/array, so it will automatically be an array and when you save it in the database, it will be transformed to JSON, and when you want to read it back, it will be automatically transformed to array...
To do so, you have to edit Inbox model and add this property:
protected $casts = ['file' => 'array'];
And that's it, then you have to use the field as if it is already an array, so leaving your code as it is in your question, without any edit, it will work right away:
public function dowloads($id)
{
$url = config('yourstitchart.file_url');
$zip = new ZipArchive;
$inboxFiles = Inbox::where('id', $id)->first()->file;
// $inboxFiles = "["phpCM0Yia.png","phptLC57a.png"]"
foreach ($inboxFiles as $file) {
$zip->add($url . $file); // update it by your path
}
$zip->close();
return response()
->download(
public_path('/temporary_files/' . "deals.zip"),
"deals.zip",
["Content-Type" => "application/zip"]
);
}

Multithreaded File Processing in PHP with pthreads

I'm trying to create a script that process a number of files simultanously, the rule is, each file can only be processed once, and the input file is deleted after it has been processed. I created this script :
<?php
// Libraries for reading files
require_once "spooler.php";
// Configuration section ///////////////////////////////////////////////////////
$config["data"] = "data";
$config["threads"] = 20;
$config["timer"] = 1;
// Array to store currently processed files
$config["processed_files"] = array();
// Processing section //////////////////////////////////////////////////////////
$timer = 0;
$pool = new Pool($config["threads"], \ProcessingWorker::class);
while (true) {
// Read a number of files from the data folder according to the number of thread
$files = Spooler::read_spool_file($config["data"], $config["threads"]);
foreach ($files as $file) {
// Check if the file is already processed
if (in_array($file, $config["processed_files"])) continue;
// Submit the file to the worker
echo "Submitting $file\n";
$config["processed_files"][$file] = $file;
$pool->submit(new ProcessingJob($config, $file));
}
sleep($config["timer"]);
$timer++;
}
$pool->shutdown();
// Processing thread section ///////////////////////////////////////////////////
class ProcessingJob extends Stackable {
private $config;
private $file;
public function __construct($config, $file)
{
$this->config = $config;
$this->file = $file;
$this->complete = false;
}
public function run()
{
echo "Processing $this->file\n";
// Pretend we're doing something that takes time
sleep(mt_rand(1, 10));
file_put_contents("_LOG", $this->file."\n", FILE_APPEND);
// Delete the file
#unlink($this->file);
// Remove the file from the currently processing list
unset($this->config["processed_files"][$this->file]);
}
}
class ProcessingWorker extends Worker {
public function run() {}
}
However, this code doesn't work well, it doesn't process the same files twice, but instead sometimes it skip processing some files. Here's the file list it should be processed, but it only process these files.
Where am I doing it wrong?
Output to the log file isn't synchronized, it's highly likely that two threads are concurrently calling file_put_contents on the log file and so corrupting it's output.
You should not write to a log file in this way.
If $config['processed_files'] is intended to be manipulated by multiple contexts then it should be a thread safe structure descended from pthreads, not a plain PHP array.

How can I create files and zip them without writing the files to disc?

I want to create 5 different files that store data from my database. I want to zip up the 5 files and have this function return the zip.
Can I create the 5 files without actually writing them to disk? The data I get from the db are only strings, so each file will be a long string.
I simply want to do this:
function getZippedFiles()
// Create 1..5 files
// Zip them up
// Return zip
end
main()
// $zip_file = getZippedFiles();
end
Any information on how to do this is much appreciated, thanks!
Sure you can, it's pretty simple with ZipArchive
// What the array structure should look like [filename => file contents].
$files = array('one.txt' => 'contents of one.txt', ...);
// Instantiate a new zip archive.
$zip_file = new ZipArchive;
// Create a new zip. This method returns false if the creation fails.
if(!$zip_file->open('directory/to/save.zip', ZipArchive::CREATE)) {
die('Error creating zip!');
}
// Iterate through all of our files and add them to our zip stream.
foreach($files as $file => $contents) {
$zip_file->addFromString($file, $contents);
}
// Close our stream.
$zip_file->close();

PHP Counter Using OOP

I'm new to OOP terminology, I am trying to create a class that make a hit counter.
I try the code below but it create just a counter.txt page with inside value 1. I dont know why its not incrementing.
class LOGFILE {
public function READ($FileName) {
$handle = fopen($FileName, 'r');
$fread = file_get_contents($FileName);
return $fread;
fclose($handle);
}
public function WRITE($FileName, $FileData) {
$handle = fopen($FileName, 'w');
$FileData = $fread +1;
fwrite($handle, $FileData);
fclose($handle);
}
}
$logfile = new LOGFILE();
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt");
The reason is that $fread is local variable for both READ and WRITE methods. You need to make it private global variable for your class:
class LOGFILE {
private $fread;
public function READ($FileName) {
$this->fread = file_get_contents($FileName);
return $this->fread;
}
public function WRITE($FileName) {
$this->READ($FileName);
$handle = fopen($FileName, 'w');
$FileData = $this->fread +1;
fwrite($handle, $FileData);
fclose($handle);
}
}
$logfile = new LOGFILE();
$logfile -> WRITE("counter.txt");
echo $logfile -> READ("counter.txt");
Note: I have removed fopen and fclose because file_get_contents does not need it. In write you can use file_put_contents. Removed not used variable $FileData too. It's always a good practice to create variables methods and classes when they are needed.
Also take a look at best practices how to name your classes, variables, methods and so on. Here's best guide, IMO.
Let's start going over the corrected code and see what was missing:
<?php
class LOGFILE {
public function READ($FileName) {
$handle = fopen($FileName, 'r');
$fread = fgets($handle, 8192);
fclose($handle);
return $fread;
}
public function WRITE($FileName, $FileData) {
$counter = $this->READ($FileName);
$handle = fopen($FileName, 'w');
fwrite($handle, $FileData + $counter);
fclose($handle);
}
}
$logfile = new LOGFILE();
$FileData = 1;
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt")."\n";
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt")."\n";
?>
use of fgets instead of file_get_contents in READ (you can choose to use file_get_contents but I rather stay consistent with the other function that uses fopen)
use of READ inside function WRITE (the principal of code-reuse)
open of file with write permissions in WRITE: 'w'
init $FileData = 1;
no need to hold a private member: $fread
most important: do not write statements after return (like you did in READ) - statements that are written after return will not be executed!
This solution was tested successfully.
OOP must be used where it's needed. You need a simple thing so, no need of OOP.
<?php
function addValue($file='counter.txt', $amount=1) {
if( false == is_file($file) ) {
return false;
}
$initial = file_get_contents($file);
return #file_put_contents($initial+$amount);
}
addValue();
?>
Test your OOP knowledge on something complex, like a shopping cart or some other concept.
EDIT // so, if you need a simple example that looks complex, here you go :)
<?php
class log {
public $file = '';
private $amount = 0;
public function __construct( $file ) {
$this->file = $file;
$this->amount = 1;
}
public function makeAdd() {
$initial = file_get_contents($this->file);
return #file_put_contents($this->file, $initial + $this->amount);
}
function __call($f, $args) {
switch( $f ) {
case 'add':
if(isset($args[0]) && !empty($args[0])) {
$this->amount = (int)$args[0];
}
if( $this->amount == 0 ) {
throw new Exception('Not a valid amount.');
}
return $this->makeAdd();
break;
}
}
}
try {
// create log
$L = new log('count.txt');
// this will add 2
var_dump($L->add(2));
// this will also add 2
var_dump($L->add());
// until you rewrite the amount
var_dump($L->add(1));
// final result -> 5
} catch(Exception $e) {
die($e->getMessage());
}
?>
Good luck!
Use UpperCamelCase for class names. LogFile, not LOGFILE. When you have a variable and the most interesting thing about it is that it's expected to hold a reference to something that is_a LogFile you should name it logFile.
Use lowerCamelCase for functions. read and write, not READ and WRITE
No spaces around the arrow operator
Code after a return statement in a method can never be reached, so delete it.
read() does not use the handle returned by fopen, so don't call fopen
the temp variable $freed doesn't help us understand the code, so we can lose it
read is a slightly unconventional name. If we rename the function to getCount it will be more obvious what it does.
You said you wanted to make a hit counter. So rename the class from LogFile to HitCounter, and the variable to hitCounter
the $FileData parameter to write doesn't get used because the variable is re-assigned inside the function. We can lose it.
The write method is supposed to add one to the number in the file. Write doesn't really express that. Rename it to increment.
Use a blank line between functions. The procedural code at the end should generally be in a separate file, but here we can just add a couple of extra lines. Delete the blanks between the last three lines of code.
Don't repeat yourself - we shouldn't have to mention 'counter.txt' more than once. OOP is all about combining data structures and behaviour into classes, so make a class private variable to hold the filename, and pass it via a constructor
$fread doesn't exist in the scope of increment, so we can't use it. This won't work. Replace it with a call to to getCount()
Swap the first two lines of increment, so we're not doing two concurent accesses to the same file, although we might be running inside a server that's running our script twice and still doing two concurrent accesses.
Rename the variable $FileData to $count, since that's what it is.
Replace the fopen,fwrite,fclose sequence with file_put_contents, since that does the same thing and is more succinct.
We need tag, since our php code continues to the end of the file.
That leaves us with:
<?php
class HitCounter {
private $fileName;
public function __construct($fileName){
$this->fileName = $fileName;
}
public function getCount() {
return file_get_contents($this->fileName);
}
public function increment() {
$count = $this->getCount() + 1;
file_put_contents($this->fileName, $count);
}
}
$hitCounter = new HitCounter("counter.txt");
$hitCounter->increment();
echo $hitCounter->getCount();
You can create a static counter and increment it each time (instead of create file)
<?php
class CountClass {
public static $counter = 0;
function __construct() {
self::$counter++;
}
}
new CountClass();
new CountClass();
echo CountClass::$counter;
?>

Categories