Files - part 1 - salvorosta.it

advertisement
CLIL-3-PHP-3
Files - part 1
Introduction
Once you master the art of working with files, a wider world of PHP web
development opens up to you. Files aren't as flexible as databases by any means,
but they do offer the chance to easily and permanently store information across
scripts, which makes them popular amongst programmers.
Files, as you can imagine, can store all sorts of information. However, most file
formats (e.g. picture formats such as PNG and JPEG) are binary, and very difficult
and/or impossible to write using normal text techniques - in these situations you
should use the library designed to cope with each format.
One reminder: if you are using an operating system that uses backslash \ as the
path separator (e.g. Windows), you need to escape the backslash with another
backslash, making \\. Alternatively, just use /, because Windows understands that
just fine too.
Topics covered in this chapter are:
Reading and writing files
Temporary files
How to make a counter
Handling file uploads
File permissions
1
CLIL-3-PHP-3
Note: CPUs work in billions of operations per second - a 3GHz CPU is capable of
performing three billion operations every second. RAM access time is measured
in nanoseconds (billionths of a second) - you can usually access some data in
RAM in about 40 nanoseconds. Hard drive access time, however, is measured in
milliseconds (thousandths of a second) - most hard drive have about a 7ms
access time.
What this means is that hard drives are much, much slower than RAM, so
working with files from your hard drive is the slowest part of your computer,
excluding your CD ROM. Databases are able to store their data in RAM for much
faster access time, whereas storing hard drive data in RAM is tricky.
Files are good for storing small bits of information, but it is not recommended
that you use them too much for anything other than your PHP scripts
themselves - counters are fine in files, as are other little things, but anything
larger would almost certainly benefit from using a database. Having said that,
please try to avoid the newbie mistake of putting everything into your database
- if you find yourself trying to figure out what field type is right to store picture
data, please have a rethink!
2
CLIL-3-PHP-3
readfile()
int readfile (string filename [, bool use_include_path [, resource context]])
If you want to output a file to the screen without doing any form of text
processing on it whatsoever, readfile() is the easiest function to use. When passed
a filename as its only parameter, readfile() will attempt to open it, read it all into
memory, then output it without further question. If successful, readfile() will
return an integer equal to the number of bytes read from the file.
If unsuccessful, readfile() will return false, and there are quite a few reasons why
it may file. For example, the file might not exist, or it might exist with the wrong
permissions.
Here is an example script:
<?php
readfile("/home/paul/test.txt");
readfile("c:\\boot.ini");
?>
The first example will work on most Unix-like systems, and will attempt to open
the file test.txt in /home/paul/ directory - you will almost certainly want to change
this to point to somewhere that exists and you have access to. The second
example is for Windows systems, and should work in any Windows NT-descended
OS, including Windows XP, Vista, 7 and 8.
The advantages to using readfile() are clear: there is no fuss, and there is little
way for it to go wrong. However, the disadvantage is equally clear: you have got
absolutely no control over the text that comes out.
You should use readfile() when you just want to print a file out without any further
action on your behalf - it is a powerful little one-liner.
3
CLIL-3-PHP-3
Note that from here on in I will be using the variable $filename to signify a
filename you have chosen. This is to avoid having to keep printing separate
examples for Windows and Unix.
4
CLIL-3-PHP-3
file_get_contents()
file()
string file_get_contents (string filename [, bool use_include_path [, resource context]])
array file (string filename [, bool use_include_path [, resource context]])
The next evolutionary step up from readfile() is just called file_get_contents(),
and also takes one parameter for the filename to open. This time, however, it does
not output any data - instead, it will return the contents of the file as string,
replete with new line characters \n where appropriate
Here is file_get_contents() in use:
<?php
$filestring = file_get_contents($filename);
print $filestring;
?>
$filename, as mentioned already, is a variable used to represent a file you have
chosen already, whether that be on Unix or Windows, which means that
file_get_contents() opens that file and places its contents into $filestring.
Effectively that piece of code is the same as our call to readfile(), but only because
we're not doing anything with $filestring once we have it.
Consider this script:
<?php
$filestring = file_get_contents($filename);
$filearray = explode("\n", $filestring);
while (list($var, $val) = each($filearray)) {
++$var;
$val = trim($val);
print "Line $var: $val<br />";
}
?>
5
CLIL-3-PHP-3
This time we use explode() to turn $filestring into an array, which is then iterated
through, outputting one line at a time with line numbers. Remember that array
indices start at 0, so we need ++$var to make sure that it starts at line 1 rather
than line 0. Also, note that we call trim() on $val - this is because each element in
the array still has its new line character \n at the end, and trim() will take that off.
File_get_contents() is an excellent general-purpose file-handling function that you
will likely find yourself using extensively.
As an alternative, if you find yourself always wanting your files inside arrays, you
can use the file() function - it works in the same manner as file_get_contents(),
with the exception that it returns an array, which each line in the file returned as
an element.
6
CLIL-3-PHP-3
fopen()
fread()
resource fopen ( string filename, string mode [, bool use_include_path [, resource context]])
string fread ( resource handle, int length)
fopen() is, for many, a fiendishly complex function to use. The reason for this is
because it is another one of those functions lifted straight from C, and so is not as
user-friendly as most PHP functions. On the flip-side, as per usual, is the fact that
fopen() is an incredibly versatile function that some people come to love for its
ability to manipulate files just as you want it to.
fopen() has two key parameters: the file to open, and how you would like it
opened. Parameter one is easy - it is just $filename, as with the other examples.
Parameter two is what makes fopen() so special: you specify letters in a string
that define whether you want to read from ("r"), write to ("w"), or append to ("a")
the file specified in parameter one. There is also a fourth option, "b", which opens
the file in binary mode. This is not necessary on Unix-based systems, but it is on
Windows, so it is best to use it everywhere - it is not detrimental on Unix-based
systems at all.
Take a look at the following usages:
<?php
fopen($filename, "r");
fopen($filename, "w");
?>
fopen() returns a file handle resource, which is basically the contents of the file.
You cannot output it directly, e.g. "print fopen(....)", but fopen() -related functions
all accept file handles as the file to work with, so you should store the return value
7
CLIL-3-PHP-3
of fopen() in a variable for later use. Therefore, here's a full usage of fopen() :
<?php
$handle = fopen($filename, "a");
?>
If the file cannot be opened, fopen() returns false. If the file is successfully
opened, a file handle is returned and you can proceed.
Once the file handle is ready, we can call other functions on the opened file,
depending on how the file was opened (the second parameter to fopen() ). To read
from a file, the function fread() is used, and to write to a file fwrite() is used. For
now we're interested in reading, so you should use "rb" for the second parameter
to fopen().
fread() takes two parameters: a file handle to read from (this is the return value
from fopen(), remember) and the number of bytes to read. The second parameter
might not make sense at first, after all you generally want to read in all of a file,
right?
Note: Do not worry about specifying a number in parameter two that is larger
than the file - PHP will stop reading when it hits the end of the file or the
number of bytes in parameter two, whichever comes first.
Well, while the chances are that you will indeed want to read in all of a file, doing
so requires you have enough memory to be able to read in the entire file at once if you are working with files of several megabytes or indeed hundreds of
megabytes, PHP's resource consumption would balloon. In circumstances such as
these you have but two choices: buy more RAM, or parse your data sequentially!
8
CLIL-3-PHP-3
Luckily, most people will not have to make that choice - most people work with
text files under a megabyte in size, and PHP can load a megabyte file all at once in
a tiny fraction of a second. To instruct PHP to use fread() to read in the entire
contents of a file, you simply need to specify the exact file size in bytes of that file
as parameter two to fread(). Sound difficult? PHP comes to the rescue again with
the filesize() function, which takes the name of a file to check, and returns its
filesize in bytes - precisely what we're looking for.
When reading in a file, PHP uses a file pointer to determine which byte it is
currently up to - kind of like the array cursor. Each time you read in a byte, PHP
advances the array cursor by one place - reading in the entire file at once
advances the array cursor to the end of the file.
So, to use fread() to read in an entire file, we can use the following line:
<?php
$contents = fread($handle, filesize($filename));
?>
Notice that fread() 's return value is the text it read in, and in above situation that
is the entire file. To finish off using fread() it is simply necessary to close the file
as soon as you are done with it.
Note: Although PHP automatically closes all files you left open in your script, it is
not smart to rely on it to do so - it is a poor use of resources, and it might affect
other processes trying to read the file. Always, always, always close your files
the minute you are finished with them.
To close a file you have opened with fopen(), use fclose() - it takes the file handle
we got from fopen(), and returns true if it was able to close the file successfully.
9
CLIL-3-PHP-3
We have now got enough to use fopen() to fully open and read in a file, then close
it. Here is the script:
<?php
$handle = fopen($filename, "rb");
$contents = fread($handle, filesize($filename));
fclose($handle);
print $contents;
?>
Firstly, remember that you will need to set $filename to be the location of a file on
your system that you have access to. Secondly, notice that fopen() is called with
"fb" as the second parameter - read-only, binary-safe. Always use "b" to ensure
compatibility with Windows servers. Thirdly, notice that filesize() is being used to
fread() in all of $filename's contents. Finally, notice that fclose() is called before
$contents is printed - it is closed as soon as $handle is no longer needed. Get into
- and stay in - this habit.
10
CLIL-3-PHP-3
Creating and changing files
Like reading files, creating and changing files can also be done in more than one
way. Luckily for you, there are just two options this time: file_put_contents() and
fwrite(). Both of these functions complement functions we just looked at, which
are file_get_contents() and fread() respectively, and they work in mostly the same
way.
file_put_contents()
int file_put_contents ( string filename, string data [, int flags [, resource context]])
This function writes to a file with the equivalent of fopen(), fwrite() (the opposite
of fread() ), and fclose() - all in one function, just like file_get_contents. It takes
two parameters, the filename to write to and the content to write respectively,
with a third optional parameter specifying extra flags which we will get to in a
moment. If file_put_contents() is successful it returns the number of bytes written
to the file, otherwise it will return false.
Using the file_put_contents() function is easy - here's an example:
<?php
$myarray[] = "This is line one";
$myarray[] = "This is line two";
$myarray[] = "This is line three";
$mystring = implode("\n", $myarray);
$numbytes = file_put_contents($filename, $mystring);
print "$numbytes bytes written\n";
?>
Remember you will need to set $filename first - other than that the script is
straightforward, and should output "52 bytes written", which is the sum total of
11
CLIL-3-PHP-3
the three lines of text plus the two new line characters used to implode() the
array. Remember that the new line character is in fact just one character inside
files, whereas PHP represents it using two, \ and n.
You can pass in a third parameter to file_put_contents(), which, if set to
FILE_APPEND, will act to append the text in your second parameter to the existing
text in the file. If you do not use FILE_APPEND the existing text will be wiped and
replaced, which is not always the desired behaviour.
12
CLIL-3-PHP-3
fwrite()
int fwrite ( resource handle, string string [, int length])
The opposite to fread() is fwrite() and also works with the file handle returned by
fopen(). Fwrite() takes a string to write as a second parameter, and an optional
third parameter where you can specify how many bytes to write. If you do not
specify the third parameter, all of the second parameter is written out to the file,
which is often what you want.
Note: as with fread(), PHP will stop writing when it reaches the end of the string
or when it has reached the number of bytes specified in this length parameter,
whichever comes first - you don't need to worry about specifying more bytes
than you have in the string.
Here is an example using the variable $mystring from the previous example to
save space:
<?php
$handle = fopen($filename, "wb");
$numbytes = fwrite($handle, $mystring);
fclose($handle);
print "$numbytes bytes written\n";
?>
If I had added 10 as the third parameter to the fwrite() call, only the first ten
bytes of $mystring would have been written out.
Note again that fclose() is called immediately after it was finished with, which is
always best practice.
Fwrite() uses a file pointer in the same way as fread() - as you write bytes out,
PHP advances the file pointer appropriately, meaning that, unless you specifically
13
CLIL-3-PHP-3
move the file pointer yourself, you always write to the end of a file.
14
CLIL-3-PHP-3
Moving, copying, and deleting files
bool rename ( string old_name, string new_name [, resource context])
bool copy ( string source, string dest)
bool unlink ( string filename, resource context)
PHP has simple functions to handle all moving, copying, and deleting files, and
quite rightly - they are all very popular things to do, so there is no point making
them difficult. If you are using Unix you will know that there is no command for
"rename", because renaming a file is essentially the same as moving it, so you
use the move (mv) command - it is the same in PHP.
Files are moved using rename(), copied using copy(), and deleted using unlink().
Unlink() might seem like an odd choice of word at first, but Unix systems consider
filenames to be "hard links" to the actual files themselves, so to unlink a file is to
delete it.
Note: all three functions will operate without further input from you. If you
choose to pass an existing file to the second parameter of rename(), it will
rename the file in parameter one to the file in parameter two, overwriting the
original file. The same applies to copy() - you will overwrite all files without
question as long as you have the correct permissions.
15
CLIL-3-PHP-3
Moving files with rename()
Use for both renaming and moving files, rename() takes two parameters: the
original filename and the new filename you wish to use. Rename() can
rename/move files across directories and drives, and will return true on success or
false otherwise.
Here is an example:
<?php
$filename2 = $filename . '.old';
rename($filename, $filename2);
?>
If you had $filename set to c:\\windows\\myfile.txt, the above script would move
that file to c:\\windows\\myfile.txt.old.
Note: rename() should be used to move ordinary files, and not files uploaded
through a form. The reason for this is because there is a special function, called
move_uploaded_file(), which checks to make sure the file has indeed been
uploaded before moving it - this stops people trying to hack your server into
making private files visible. You can perform this check yourself if you like by
calling the is_uploaded_file() function.
16
CLIL-3-PHP-3
Copying files with copy()
Like rename(), copy() also takes two parameters - the filename you wish to copy
from, and the filename you wish to copy to. The difference between rename() and
copy() is that calling rename() results in the file being in only one place, the
destination, whereas copy() leaves the file in the source location as well as placing
a new copy of the file into the destination.
<?php
$filename2 = $filename . '.old';
copy($filename, $filename2);
?>
The result of that script is that there will be a file $filename and also a
$filename.old, e.g. c:\\windows\\myfile.txt and c:\\windows\\myfile.txt.old.
Note: this function will not copy empty (zero-length) files - to do that, you need
to use the function touch().
17
CLIL-3-PHP-3
Deleting files with unlink()
To delete files, simply pass a filename string as the only parameter to unlink().
Note that unlink deals only with files - to delete directories you need rmdir().
<?php
if (unlink($filename)) {
print "Deleted $filename!\n";
} else {
print "Delete of $filename failed!\n";
}
?>
Note: if you have a file opened with fopen(), you need to fclose() it before you
call unlink()
18
CLIL-3-PHP-3
Temporary files
resource tmpfile ( void )
Very often you will find you want to work with a file as if it were a "scratchpad" - a
holding area where you can write out temporary data for later use. To make this
as easy as possible, PHP has a function called tmpfile() which takes no
parameters, but will create a temporary file on the system, fopen() it for you, and
send back the file handle as its return value.
That file is then yours to read from and write to all you wish, and is deleted as
soon as you fclose() the file handle or the script ends. Here is an example of
tmpfile() in use:
<?php
$handle = tmpfile();
$numbytes = fwrite($handle, $mystring);
fclose($handle);
print "$numbytes bytes written\n";
?>
As you can see, tmpfile() is a drop-in replacement for fopen()ing a known file, so
it is easy to make use of.
If you want to know where these temp files are being saved, use the
sys_get_temp_dir() function - it's new in PHP 5.2.1, but returns a string
containing the directory used for creating temporary files.
19
Download