Error Handling with files in PHP
$path = '/home/test/files/test.csv';
fopen($path, 'w')
Here I want add an error handling by throwing exceptions, on 'No file or directory is found' and 'No permission to create a file'.
I am using Zend Framework.
By using fopen with write mode, I can create a file. But how to handle it when corresponding folder is not there?
i.e if files folder is not present in root structure.
How to throw an exception when no permission is permitted for creating a file?
Something like this should get you started.
function createFile($filePath)
{
$basePath = dirname($filePath);
if (!is_dir($basePath)) {
throw new Exception($basePath.' is an existing directory');
}
if (!is_writeable($filePath) {
throw new Exception('can not write file to '.$filePath);
}
touch($filePath);
}
Then to call
try {
createFile('path/to/file.csv');
} catch(Exception $e) {
echo $e->getMessage();
}
I suggest, you take a look at this link: http://www.w3schools.com/php/php_ref_filesystem.asp
especially the methods file_exists and is_writable
Like this:
try
{
$path = '/home/test/files/test.csv';
fopen($path, 'w')
}
catch (Exception $e)
{
echo $e;
}
PHP will echo whatever error would arise there.
Though you can also use is_dir or is_writable functions to see if folder exists and has permission respectively:
is_dir(dirname($path)) or die('folder doesnt exist');
is_writable(dirname($path)) or die('folder doesnt have write permission set');
// your rest of the code here now...
But how to handle it when corresponding folder is not there?
When a folder does not exist .. try to create it!
$dir = dirname($file);
if (!is_dir($dir)) {
if (false === #mkdir($dir, 0777, true)) {
throw new \RuntimeException(sprintf('Unable to create the %s directory', $dir));
}
} elseif (!is_writable($dir)) {
throw new \RuntimeException(sprintf('Unable to write in the %s directory', $dir));
}
// ... using file_put_contents!
Related
Hi have the following code in slim to save file
I want to make sure the files have been uploaded to the server and only then to return true. or false
how can I do that with Slim or PHP?
Result of file log always null for nonreason and the file are being uploaded
public function saveFiles(Array $files, $location) {
try
{
/** #var UploadedFileInterface $file */
foreach ($files as $file) {
$fileLog = $file->moveTo($location . DIRECTORY_SEPARATOR . $file->getFilename());
}
return true;
}
catch(\Exception $e) {
throw new Exception($e->getMessage());
According to slim implementaion of this PSR, it always throws exception if something went wrong while file is being uploaded.
I don't know whether you throw another Exception for a reason, but you can process it something like:
public function saveFiles(Array $files, $location) {
$result = true;
foreach ($files as $file) {
try {
$fileLog = $file->moveTo($location . DIRECTORY_SEPARATOR . $file->getFilename());
} catch(\Exception $e) {
// Exception on file uploading happened, but
// we still continue loading other files
$result = false;
// Or just `return false;` if you don't want
// to upload other files if exception happened
// return false;
}
}
return $result;
}
Of course, this method can be extended to collect exceptions' messages and return them.
In ZF 2.2.5 Zend\View\Renderer\PhpRenderer::render method there is an if statement that checks if template exists in template stack (line 497)
$this->__file = $this->resolver($this->__template);
if (!$this->__file) {
throw new Exception\RuntimeException(sprintf(
'%s: Unable to render template "%s"; resolver could not resolve to a file',
__METHOD__,
$this->__template
));
}
try {
ob_start();
include $this->__file;
$this->__content = ob_get_clean();
} catch (\Exception $ex) {
ob_end_clean();
throw $ex;
}
But it doesn't check if file really exists in file system. Which means that subsequent try { ... } catch(\Exception $ex) {...} block is useless, because include $this->__file; is uncatchable. So when I test my controllers I always get 200 response even if template file is missing and there is nothing but exception call stack on the screen. Shouldn't that if (!$this->__file) { ... } be rewritten to if (!is_file($this->__file)) { ... }?
Well, it's up to the resolver to perform these checks. See the following code taken from Zend\View\Resolver\TemplatePathStack:
foreach ($this->paths as $path) {
$file = new SplFileInfo($path . $name);
if ($file->isReadable()) {
// Found! Return it.
if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === 'phar://') {
// Do not try to expand phar paths (realpath + phars == fail)
$filePath = $path . $name;
if (!file_exists($filePath)) {
break;
}
}
if ($this->useStreamWrapper()) {
// If using a stream wrapper, prepend the spec to the path
$filePath = 'zend.view://' . $filePath;
}
return $filePath;
}
}
Also note that include accepts more than is_file checks for. For instance, include also accepts stream wrappers.
My function cleanup looks like that.
function cleanUp($exdirs, $exfiles){
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('.'),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach($it as $entry) {
if ($entry->isDir() && !in_array($entry->getBasename(), $exdirs)) {
try {
rmdir($entry->getPathname());
}
catch (Exception $ex) {
// dir not empty
}
}
elseif (!in_array($entry->getFileName(), $exfiles)) {
unlink($entry->getPathname());
}
}
}
And calling this function like that
$excludeDirsNames = array('cgi-bin');
$excludeFileNames = array('ws.zip');
cleanUp($excludeDirsNames , $excludeFileNames);
Now the problem is i'm getting warning message. can not unlink cgi-bin on line unlink($entry->getPathname());
What's wrong with my function? How to fix that problem?
I am guessing that cgi-bin is a symlink and not a regular directory. That's why it's getting into the "unlink" section. The error message is probably due to permissions.
The fix, move 'cgi-bin' to the $excludeFileNames array.
My following method uses an array of data $FDFData to create an FDF file:
public function writeFDFFile($FDFData) {
$fdf_file = time().'.fdf';
$fdf = $this->createFDF(PDF_FORM, $FDFData);
// write the file out
if($fp=fopen($fdf_file,'w')) {
fwrite($fp,$fdf,strlen($fdf));
} else {
throw new Exception('Unable to create file: '.$fdf_file);
}
fclose($fp);
return $fdf_file;
}
One of my scripts runs absolutely fine:
require_once('PDFPrinter.php');
try {
$printer = new PDFPrinter();
$FDFData = $printer->assembleFDFData(9);
$fdf_file = $printer->writeFDFFile($FDFData);
$printer->downloadFile($fdf_file);
}catch(Exception $e) {
echo $e->getMessage();
}
but I can't get my test code to run because I get a:
Unexpected PHP error [fopen(1315558352.fdf) [<a href='function.fopen'>function.fopen</a>]: failed to open stream: Permission denied]
error thrown:
require_once(dirname(__FILE__) . '/simpletest/autorun.php');
require_once(dirname(__FILE__) . '/../PDFPrinter.php');
class PDFPrinterTest extends UnitTestCase {
private $printer;
private $FDFData;
function setUp() {
$this->printer = new PDFPrinter();
$this->FDFData = $this->printer->assembleFDFData(5);
}
function testException() {
try {
$this->printer->writeFDFFile($this->FDFData);
$this-assertTrue(true);
} catch(Exception $e) {
$this->assertTrue(false);
}
}
}
Both scripts are run in directories with the correct permissions. I am running my test scripts through the browser as well, so it's not that I have a different environment.
I'm stuck as to how to proceed to find the issue really.
My directory structure:
HOME - PDFPrinter.php
|
-----tests - PDFPrinterTest.php
|
------simpletest - autorun.php
Any suggestions as to how I could find the issue?
Many thanks
Update
I have tried changing my test class so that the only test function in there is:
function testWrite() {
try {
$name = "testing.txt";
if($fp=fopen($name, 'w')) {
fwrite($fp, "Blah");
} else {
throw new Exception("Nope.");
}
$this->pass("All good");
} catch(Exception $e) {
$this->fail("Not good");
}
}
but the exception is still thrown with the warning.
Yet a very simple script run from the same directory works fine:
$name = "Test.txt";
if($fp=fopen($name, 'w')) {
fwrite($fp, "Working");
} else {
throw new Exception("Nope.");
}
fclose($fp);
that will actually create and write to the file.
Finally found the solution which was that the file name needed to be the full absolute address in order for it to work in both scripts for some reason. This was suggested in one of the answers for this SO question which I quote below:
Use fopen($_SERVER['DOCUMENT_ROOT'].'test.txt','a+');
so for my code, I have used:
if($fp=fopen($_SERVER['DOCUMENT_ROOT'].$name, 'w')) {
fwrite($fp, "Working");
}
In your test
fwrite("Blah");
should be
fwrite($fp, "Blah");
I'm not sure what the problem in the original code is though.
failed to open stream: Permission denied
There is one important programmer's skill every developer ought to master.
Here it is:
To trust your eyes
If your PHP telling you that permission is denied - so it is. Just doble-check it. It is not a big deal yet noone can do it for you.
I understand what try-catch statements do, but from reading the documentation on php.net, I would not be able to implement one into my own code. I need a real example to help me understand.
How can I turn this example into a try-catch statement, if the upload was not successful?
$move = move_uploaded_file($_FILES['file']['tmp_name'], $_SERVER['DOCUMENT_ROOT'].'/uploads/'.$_FILES['file']['name']);
if (!$move) {
die ('File didn\'t upload');
} else {
//opens the uploaded file for extraction
echo 'Upload Complete!';
}
This may not be a good example to work with, but any help would be appreciated.
You could do it like this.
try {
//throw exception if can't move the file
if (!move_uploaded_file( ... )) {
throw new Exception('Could not move file');
}
//do some more things with the file which may also throw an exception
//...
//ok if got here
echo "Upload Complete!";
} catch (Exception $e) {
die ('File did not upload: ' . $e->getMessage());
}
It is a bit pointless for the above example, but you should get the idea. Note that you can throw the exception(s) from anywhere (e.g. within a function/method that you call from withing the try{}) and they will propagate upwards.
Well, if you want to use exceptions, you could do something like:
function handleUpload() {
$move = move_uploaded_file($_FILES['file']['tmp_name'], $_SERVER['DOCUMENT_ROOT']."/uploads/".$_FILES['file']['name']);
if (!$move) {
throw new Exception('File Didnt Upload');
}
}
try {
handleUpload();
echo "File Uploaded Successfully";
} catch(Exception $ex) {
die($ex->getMessage);
}
I know this may seem like bloat - but you can call the method from anywhere in the call stack, and catch the exception at any point.
try-catch statements are used to handle exceptions. I don't believe that the function move_uploaded_files can throw and exception, as such I think that you code is written is correct. After the call, you look at the return code. If it's false, you end processing, otherwise you are report success.
According to a similar post in PHPbug, only OO code (Object-Oriented code) throws exceptions. That would mean that functions such as move_uploaded_file won't throw their own exceptions, but some other code will.