Writers

Writers – Saving archives

Introduction

A writer is an object that deals with data. Some writers transform data (this is the case of the archive writers), some save them to disk (for files writers), or to memory (for the memory writer)... They all implement the same interface.

You can transfer data from a reader to a writer using the File_Archive::extract function.

All the writers can be created thanks to the File_Archive factory, and more particularly the File_Archive::to* functions.

Write archives

To create a writer that will generate an archive, use the toArchive() function:

toArchive ( string $filename , &$innerWriter , $type = null , $stat = array() , $autoClose = true )

  • $filename is the name of the generated archive

  • $innerWriter is another writer in which the archive file will be written

  • $type is one of Tar, Gzip or Zip and indicates the format of compression. If not specified, the type is determined thanks to the extension of the filename.

  • $stat is an optional array to indicate stats about the archive (see the PHP stat function for the possible indexes).

  • $autoClose indicates whether the inner writer will be closed once the data are sent. It may be useful not to close the writer if you want to append some more data after writing. In general, you won't need to keep the writer open, so you should just keep the default value.

Generation of archive writers

<?php
require_once "File/Archive.php";

/* Writer to a tar file */
File_Archive::toArchive("archive.tar"$innerWriter);

/* Writer to a tar.gz file */
File_Archive::toArchive("archive.tgz"$innerWriter);

/* Writer to a zip file */
File_Archive::toArchive("archive.zip"$innerWriter);
?>

Write to files

A writer can write the files to physical files. To create such a writer, call File_Archive::toFiles();. If a directory does not exist, it will be automatically created.

Use files writer

<?php
require_once "File/Archive.php";

/* Copy a whole directory to another location */
File_Archive::extract(
    
File_Archive::read('Path/to/dir''new/directory');
    
File_Archive::toFiles()
);

/* Convert an archive to another format: tgz to zip */
File_Archive::extract(
    
File_Archive::read('archive.tgz/'),

    
File_Archive::toArchive(
        
'archive.zip',
        
File_Archive::toFiles()
    )
);
?>

Send emails

You can send emails as attachment using a mail writer available thanks to File_Archive::toMail function.

toMail ( array $to , array $headers , string $message , &$mail = null )

This function relies on the PEAR Mail and Mail_Mime libraries, and the parameters are the same as the one of these classes:

  • $to an array or a string with comma separated recipients

  • $headers will be sent to Mail_Mime and to $mail: an associative array of headers. The header name is used as key and the header value as value.

  • $message is the text version of the body of the mail. You can provide an HTML version thanks to the setHTMLBody() and addHTMLImage() of the writer. The signatures of these functions are the same as the ones of Mail_Mime.

  • $mail the way to send mail. This is an object created with the Mail::factory() function. If null, Mail::factory('mail') will be used (and the email will be sent using the PHP mail function).

<?php
require_once "File/Archive.php";

/* Send the files in the current directory (no recursion) as attachment */
File_Archive::extract(
    
File_Archive::read('Path/to/dir'''00),
    
File_Archive::toMail(
        
$to// recipients
        
array(
            
'Subject' => 'Path/to/dir directory',
            
'From'    => 'address@of.expeditor'
        
),
        
'Find all the files attached' // body
    
)
);
?>

Send files to the user

To send files to the remote user (i.e. write data to the standard output), you need a special writer. You can build one calling function File_Archive::toOutput().

This writer will automatically send a header forcing the download of the file.

If you don't want that, call File_Archive::toOutput(false).

Multi writers

Using a multi writer, you can write the data to two or more different locations in parallel.

A typical use is to send the file to the user at the same time as you write it to a file.

It can also be used to generate archives in different formats.

You can create a multi writer using File_Archive::toMulti($dest1, $dest2).

Multi writer

<?php
//Send a directory to the user and to a file

File_Archive::extract(
    
File_Archive::read('directory'),
    
File_Archive::toArchive(
        
'multi.zip',
        
File_Archive::toMulti(
            
File_Archive::toOutput(),
            
File_Archive::toFiles()
        )
    )
);
?>

Writing to a writer

It is also possible to write data directly to a writer, without using a reader. To do so, you can use the following interface implemented by any writer:

  • function newFile($URL, $stat)

    Create a new file in the writer.

    $URL is the name of the file, $stat is an array of statistics about the data (see the PHP stat() function for more information).

    The stat array may not contain all the information. The only index that must be present is index 7 (size of the data).

  • function writeData($data)

    Append the specified data to the writer. A call to newFile() must have been done previously.

  • function close()

    Close the writer, eventually flush the data, write the footer... This function must be called before the end of the file, otherwise some data may not be treated by the writer.

Dynamic creation of a zip file

<?php
require_once "File/Archive.php";

$dest File_Archive::toArchive("foo.zip"File_Archive::toFiles());
$dest->newFile("even.txt");
for(
$i=0$i<100$i++)
    
$dest->writeData((2*$i)."\n");
$dest->newFile("odd.txt");
for(
$i=0$i<100$i++)
    
$dest->writeData((2*$i+1)."\n");
$dest->close();

?>

If you do not specify the stat array in the newFile() function, the majority of the archives will have to buffer the data until the end of the file is reached (this is because the size of the file is usually needed to be able to write the header).

This may be a memory problem if you want to generate really large files.

Functions that use writers

All File_Archive functions that take a writer as an argument also accept strings and arrays. The strings will be automatically interpreted as a writer using File_Archive::appender() function. The arrays will be interpreted as a multi writer.

Since the writers are passed by reference, you will have to pass a variable and not the raw string or array.

It is thus possible to rewrite the previous example like that:

Multi writer

<?php
//Send a directory to the user and to a file

File_Archive::extract(
    
$src 'directory',
    
File_Archive::toArchive(
        
'multi.zip',
        array(
            
File_Archive::toOutput(),
            
File_Archive::toFiles()
        )
    )
);
?>