Location: PHPKode > scripts > pfpFileTree > pfpFileTreeDoc.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
	<TITLE></TITLE>
	<META NAME="GENERATOR" CONTENT="OpenOffice.org 2.4  (Unix)">
	<META NAME="CREATED" CONTENT="20110312;34600">
	<META NAME="CHANGED" CONTENT="20110318;23482500">
	<STYLE TYPE="text/css">
	<!--
		@page { size: 8.27in 11.69in; margin: 0.79in }
		P { margin-bottom: 0.08in }
		TD P { margin-bottom: 0in }
		H1 { margin-bottom: 0.08in }
		H1.western { font-family: "Arial", sans-serif; font-size: 16pt }
		H1.cjk { font-family: "DejaVu Sans"; font-size: 16pt }
		H1.ctl { font-family: "Tahoma"; font-size: 16pt }
		H2 { margin-bottom: 0.08in }
		H2.western { font-family: "Arial", sans-serif; font-size: 14pt; font-style: italic }
		H2.cjk { font-family: "HG Mincho Light J"; font-size: 14pt; font-style: italic }
		H2.ctl { font-family: "Arial Unicode MS"; font-size: 14pt; font-style: italic }
		H3 { margin-bottom: 0.08in }
		H3.western { font-family: "Arial", sans-serif }
		H3.cjk { font-family: "HG Mincho Light J" }
		H3.ctl { font-family: "Arial Unicode MS" }
	-->
	</STYLE>
</HEAD>
<BODY LANG="en-US" DIR="LTR">
<H1 CLASS="western">PfpFileTree</H1>
<P>While there are lots of other tools available for managing file
trees, I have endeavoured to write something which provides optimal
flexibility in some clean and well documented code. 
</P>
<P>The pfpFileTree API is implemented as a single class. Out of the
box it allows you to filter and manipulate a set of files which exist
within a directory tree.</P>
<P>The constructor takes a single parameter – the path to the
directory which is the 'root' of the tree.</P>
<P>Most of the other methods take a parameter (the selector) which
defines search criteria: an array of key value pairs. The key is the
attribute to filter on, and the value is the value to match
(optionally preceded by an operator.</P>
<P>The name attribute is the only one which accepts globbing patterns
(and any parameter passed is treated as a globbing pattern). The
others are a little different. If the value passed begins with any of
the characters &lt;,&gt;,+,-,! then that is treated as a comparison
operator (less than, greater than,les than, greater than and not).
Note that the compairson operators only work on string and, integer
and time values – not booleans. A list of the standard attributes
is provided in Appendix A.</P>
<P>The current fileset is held in the $data member variable. This is
an array with the path relative to the base directory (tree root) as
keys. Each value is an array of attributes.</P>
<P>Because its very easy for a recursive search to run away from you,
there is checking built in that halts the execution of the code, sets
an error and returns false when the memory usage rises above a
predfined threshold (90% by default).</P>
<H3 CLASS="western">readTree($attr)</H3>
<P STYLE="margin-bottom: 0in">Adds files to $data which match the
array specified by $attr (pass an empty array to select all files)
with the standard attributes populated. Returns false on failure, or
a reference to the current $data on success. This method will also
decorate $data with any additional attributes returned by a callback
specified with addCallBack.</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<H3 CLASS="western">filter($attr)</H3>
<P STYLE="margin-bottom: 0in">Removes entries from $data which do not
match the selector specified.</P>
<P STYLE="margin-bottom: 0in">Returns false on failure, or a
reference to the current $data on success.</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<H3 CLASS="western">subset($attr)</H3>
<P STYLE="margin-bottom: 0in">Like filter but operates (and returns)
a new pfpFileTree with the filter applied. The current set in the
current object is not modified.</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<H3 CLASS="western">applyCallback($callback, $arg)</H3>
<P STYLE="margin-bottom: 0in">Runs through the current list of files
in $data and calls the function/method specified in $callback with 2
parameters, the first is the canonical path of the file, the second
is a copy of $arg.</P>
<P STYLE="margin-bottom: 0in">Execution stops when the
function/method specified returns false (and this method returns
false) or when all iterations of the method return true (and this
method returns true).</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<P STYLE="margin-bottom: 0in">Note that this callback is independent
of and behaves differently to the callback used in the addCallback()
decorator mathod. 
</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<H3 CLASS="western">compareTree($otherBaseDir)</H3>
<P STYLE="margin-bottom: 0in">Populates the cmp attribute in the
current tree depending on the existence of a file with the same
relative path:</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in">0 if both files
same</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in">1 if the file in
otherBaseDir is different and older</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in">-1 if the file in
otherBaseDir is different and newer</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in">2 if the file does
not exist in otherBaseDir</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in">3 if the file is a
regular file in one tree and a directory in the other</P>
<P STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P STYLE="margin-bottom: 0in">Returns false on failure, or a
reference to the current $data on success. In the event of finding a
type mismatch (file/directory) processing stops immediately and the
method returns false.</P>
<H3 CLASS="western">canWriteTo($otherBaseDir)</H3>
<P>Tests if all the files in the current data set can be written to
the corresponding path in otherBaseDir. This populates the 'can_w'
attribute for the files and returns true if all are writeable. Note
that this method will iterate through the complete current list of
files – even if some cannot be written.</P>
<P>Returns false if one or more files cannot be written.</P>
<H3 CLASS="western">writeTo($otherBaseDir)</H3>
<P>If, and only if, canWriteTo($otherBaseDir) returns true, this
nethod will attempt the actual file copying. Except for very ubnusual
circumstances, all the files will copied and the method will retrn a
reference to the current $data. If something unexpectedly fails, it
will return false (and in this case, some of the files may have been
copied).</P>
<H3 CLASS="western">canDelFiles()</H3>
<P>Guess what? Yes, its the same as canWriteTo but tests of all the
files in the current set can be deleted (inthe case of directories,
it only tests the permissions related to the drectory – it does not
check that the directory will be empty (and hence rmdir successful).</P>
<H3 CLASS="western">delFiles()</H3>
<P STYLE="margin-bottom: 0in">As with the writeTo operation, the
correspondng 'can' method is checked first to see if the code is
likely to succeed – if its not going to, then this method returns
false without changing any files.</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<P STYLE="margin-bottom: 0in">Where files have been deleted, the
'exists' attribute is updated to false. If there are directories
which would normally be deletable but which still contain files,
these will fail silently.</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
<P STYLE="margin-bottom: 0in">Returns true if the operation was
completely successful – otherwise false.</P>
<H3 CLASS="western">clear()</H3>
<P>Resets the current data set (but not the baseDir). 
</P>
<H3 CLASS="western">addCallback($callback)</H3>
<P>Configures extra decorators which are invoked when readTree is
called. Each callback (you can add multiple ones) is called with a
single parameter – the canonical file path. It should return an
array of key/value pairs. The keys should not be any of the standard
keys listed in Appendix A.</P>
<P>e.g.</P>
<pre><code>
$instance-&gt;addCallback('my_fileperms');
function my_fileperms($file)
{
   return array('fileperms'=&gt;fileperms($file);
}
</code></pre>
<H3 CLASS="western">sortFiles($attr)</H3>
<P>It shouldn't come as much of a shock that this changes the order
of the files in $data. If we're going to be deleting stuff, then it
makes more sense to delete the contents of a directory before we try
to delete the directory. If we're rotating log files, then we want to
start with the oldest file in each set.</P>
<P>Again $attr is a set of key/value pairs – but the order is
important! The values stored in the array can be '+' or '-' depending
on whether that key should be sorted ascending or descending.</P>
<P>e.g.</P>
<pre><code>
    sortFiles(array('size'=&gt;'+','mtime'=&gt;'-'));
</code></pre>
<P>small files appear first (ascending), if 2 files have the same
size, the newest appears first (descending).</P>
<H3 CLASS="western">ls()</H3>
<P>Dumps the current $data to stdout.</P>
<H2 CLASS="western">Portability</H2>
<P>I've written it to be as portable as possible, but I don't have a
MS Windows based system to test on. While PHP provides the
DIRECTORY_SEPERATOR constant, which is populated with '\' on such
platforms and '/' every where else, PHP is quite happy to process
patch on MS Windows systems using '/' as the directory seperator. So
pfpFileTree uses '/' throughout.</P>
<P>The constructor set the 'caseSensitive' property to false for
non-MSWindows systems, and to true for anything else – but this can
be changed later. However the semantics of this can get very
complicated very quickly – here be dragons!</P>
<H2 CLASS="western">Symbolic links</H2>
<P>These are processed as if they were normal files. When wrteTo() is
invoked for a link it creates a copy of the linked file. But when
delFiles() is invoked it deletes the link leaving the original file
in place.</P>
<H1 CLASS="western">Appendix A: Attributes</H1>
<TABLE WIDTH=100% BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0>
	<COL WIDTH=51*>
	<COL WIDTH=119*>
	<COL WIDTH=85*>
	<TR VALIGN=TOP>
		<TD WIDTH=20% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Attribute</FONT></P>
		</TD>
		<TD WIDTH=47% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Meaning</FONT></P>
		</TD>
		<TD WIDTH=33% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Example</FONT></P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>exists</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Boolean</P>
			<P>Whether or not the file exists – a bit metaphysical – but
			there are reasons for having this. Populated by readTree(),
			updated by delFiles()</P>
		</TD>
		<TD WIDTH=33%>
			<P>true</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>size</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Integer</P>
			<P>Size in bytes as returned by filesize(). Populated by
			readTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P><CODE><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif">14219</FONT></FONT></CODE></P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>mtime</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Integer</P>
			<P>Unix timestamp of last modified. Populated by readTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P>1296866170</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>type</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">String</P>
			<P STYLE="margin-bottom: 0.2in">File (f), Directory(d) or
			something else (o)</P>
			<P>- something else includes symbolic links. Populated by
			readTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P>f</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>w</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Boolean</P>
			<P>Whether the file is writeable. Populated by readTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P>true</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>r</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Boolean</P>
			<P>Whether the file is readable. Populated by readTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P>false</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>name</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">String</P>
			<P>This is not stored as an attribute – the path relative to the
			base directory is used as the key for an array holding the other
			attributes, however glob filtering can be performed by passing a
			template with a name member (see later). 
			</P>
		</TD>
		<TD WIDTH=33%>
			<P>*.jpeg</P>
		</TD>
	</TR>
	<TR>
		<TD COLSPAN=3 WIDTH=100% VALIGN=TOP>
			<P ALIGN=CENTER>Conditionally populated</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Attribute</FONT></P>
		</TD>
		<TD WIDTH=47% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Meaning</FONT></P>
		</TD>
		<TD WIDTH=33% BGCOLOR="#0084d1">
			<P><FONT SIZE=4>Example</FONT></P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>cmp</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Integer</P>
			<P>The result of the last (tree) compare operation – 0 if same
			in both trees, 1 if different and the file in this tree is newer,
			-1 if different and te other file is newer, 2 if the file was not
			found in the other tree. Note that this does not detect the case
			where a file exists in the other tree but not in this one.
			Populated by compareTree()</P>
		</TD>
		<TD WIDTH=33%>
			<P>-1</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>can_w</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Boolean</P>
			<P STYLE="margin-bottom: 0.2in">Whether the file or directory can
			be written to another location. 
			</P>
			<P>Populated by canWriteTo</P>
		</TD>
		<TD WIDTH=33%>
			<P>true</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>can_d</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">Boolean</P>
			<P STYLE="margin-bottom: 0.2in">Whether the file or directory can
			be deleted</P>
			<P>Populated by canDelete()</P>
		</TD>
		<TD WIDTH=33%>
			<P>false</P>
		</TD>
	</TR>
	<TR VALIGN=TOP>
		<TD WIDTH=20%>
			<P>md5</P>
		</TD>
		<TD WIDTH=47%>
			<P STYLE="margin-bottom: 0.2in">The value returned by md5_file()
			for a file, or in the case of a directory, the md5 hash of the
			string representing the path of the directory relative to the base
			dir – i.e. md5($effPath);</P>
			<P STYLE="margin-bottom: 0.2in"><BR><BR>
			</P>
			<P>This is only populated by compareTree when files exist in both
			trees and are the same size</P>
		</TD>
		<TD WIDTH=33%>
			<P>a5b4ea05ea755c5af59f6ecdfde9d38</P>
		</TD>
	</TR>
</TABLE>
<P><BR><BR>
</P>
<P STYLE="margin-bottom: 0in"><BR>
</P>
</BODY>
</HTML>
Return current item: pfpFileTree